Completed
Push — master ( 72dacd...198bb5 )
by Alessandro
03:45
created

Client::prepareRequest()   C

Complexity

Conditions 7
Paths 64

Size

Total Lines 37
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 37
ccs 25
cts 25
cp 1
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 23
nc 64
nop 1
crap 7
1
<?php
2
3
namespace Fazland\SkebbyRestClient\Client;
4
5
use Fazland\SkebbyRestClient\Constant\Charsets;
6
use Fazland\SkebbyRestClient\Constant\EncodingSchemas;
7
use Fazland\SkebbyRestClient\Constant\Endpoints;
8
use Fazland\SkebbyRestClient\Constant\Recipients;
9
use Fazland\SkebbyRestClient\Constant\SendMethods;
10
use Fazland\SkebbyRestClient\Constant\ValidityPeriods;
11
use Fazland\SkebbyRestClient\DataStructure\Response;
12
use Fazland\SkebbyRestClient\DataStructure\Sms;
13
use Fazland\SkebbyRestClient\Exception\NoRecipientsSpecifiedException;
14
use Fazland\SkebbyRestClient\Transport\Factory;
15
use Fazland\SkebbyRestClient\Transport\TransportInterface;
16
use libphonenumber\NumberParseException;
17
use libphonenumber\PhoneNumberFormat;
18
use libphonenumber\PhoneNumberUtil;
19
use Symfony\Component\OptionsResolver\OptionsResolver;
20
21
/**
22
 * @author Massimiliano Braglia <[email protected]>
23
 */
24
class Client
25
{
26
    /**
27
     * @var array
28
     */
29
    private $config;
30
    /**
31
     * @var TransportInterface
32
     */
33
    private $transport;
34
35
    /**
36
     * @param array $options
37
     * @param TransportInterface $transport
38
     */
39 9
    public function __construct(array $options, TransportInterface $transport = null)
40
    {
41 9
        $resolver = new OptionsResolver();
42
43 9
        $this->configureOptions($resolver);
44 9
        $this->config = $resolver->resolve($options);
45
46 9
        $this->transport = null === $transport ? Factory::createTransport() : $transport;
47 9
    }
48
49
    /**
50
     * Send an SMS
51
     *
52
     * @param Sms $sms
53
     *
54
     * @return Response[]
55
     *
56
     * @throws NoRecipientsSpecifiedException
57
     */
58 9
    public function send(Sms $sms)
59 5
    {
60 9
        if (! $sms->hasRecipients()) {
61 1
            throw new NoRecipientsSpecifiedException();
62
        }
63
64 8
        $messages = [];
65
66 8
        $recipients = $sms->getRecipients();
67 8
        foreach (array_chunk($recipients, Recipients::MAX) as $chunk) {
68 8
            $message = clone $sms;
69
            $message
70 8
                ->setRecipients($chunk)
71 8
                ->clearRecipientVariables()
72
            ;
73
74 8
            foreach ($chunk as $recipient) {
75 8
                if (! isset($sms->getRecipientVariables()[$recipient])) {
76 6
                    continue;
77
                }
78
79 3
                foreach ($sms->getRecipientVariables()[$recipient] as $variable => $value) {
80 3
                    $message->addRecipientVariable($recipient, $variable, $value);
81 3
                }
82 8
            }
83
84 8
            $messages[] = $message;
85 8
        }
86
87 8
        $responses = [];
88 8
        foreach ($messages as $message) {
89 8
            $request = $this->prepareRequest($message);
90
91 8
            $responses[] = $this->executeRequest($request);
92 6
        }
93
94 6
        return $responses;
95
    }
96
97
    /**
98
     * Configure default options for client.
99
     *
100
     * It takes required options username, password, sender and method.
101
     * validity_period MUST be a \DateInterval object if set
102
     * delivery_start MUST be a \DateTime object if set
103
     *
104
     * @param OptionsResolver $resolver
105
     */
106 9
    private function configureOptions(OptionsResolver $resolver)
107
    {
108
        $resolver
109 9
            ->setRequired([
110 9
                'username',
111 9
                'password',
112 9
                'sender',
113 9
                'method',
114 9
            ])
115 9
            ->setDefaults([
116 9
                'delivery_start' => null,
117 9
                'charset' => Charsets::UTF8,
118 9
                'validity_period' => \DateInterval::createFromDateString('2800 minutes'),
119 9
                'encoding_schema' => EncodingSchemas::NORMAL,
120 9
                'endpoint_uri' => Endpoints::REST_HTTPS,
121 9
            ])
122 9
            ->setAllowedTypes('username', 'string')
123 9
            ->setAllowedTypes('password', 'string')
124 9
            ->setAllowedTypes('sender', 'string')
125 9
            ->setAllowedTypes('method', 'string')
126 9
            ->setAllowedTypes('delivery_start', ['null', 'DateTime'])
127 9
            ->setAllowedTypes('validity_period', ['null', 'DateInterval'])
128 9
            ->setAllowedTypes('encoding_schema', 'string')
129 9
            ->setAllowedTypes('charset', 'string')
130 9
            ->setAllowedTypes('endpoint_uri', 'string')
131 9
            ->setAllowedValues('method', [
132 9
                SendMethods::CLASSIC,
133 9
                SendMethods::CLASSIC_PLUS,
134 9
                SendMethods::BASIC,
135 9
                SendMethods::TEST_CLASSIC,
136 9
                SendMethods::TEST_CLASSIC_PLUS,
137 9
                SendMethods::TEST_BASIC,
138 9
            ])
139
            ->setAllowedValues('validity_period', function (\DateInterval $value) {
140 9
                return $value->i >= ValidityPeriods::MIN && $value->i <= ValidityPeriods::MAX;
141 9
            })
142 9
            ->setAllowedValues('encoding_schema', [
143 9
                EncodingSchemas::NORMAL,
144 9
                EncodingSchemas::UCS2,
145 9
            ])
146 9
            ->setAllowedValues('charset', [
147 9
                Charsets::ISO_8859_1,
148 9
                Charsets::UTF8,
149 9
            ])
150
        ;
151 9
    }
152
153
    /**
154
     * @param Sms $sms
155
     *
156
     * @return array
157
     */
158 8
    private function prepareRequest(Sms $sms)
159
    {
160 8
        list($sender_string, $sender_number) = $this->getSenderParams($sms);
161
162 8
        $deliveryStart = $sms->getDeliveryStart() ?: $this->config['delivery_start'];
163 8
        $validityPeriod = $sms->getValidityPeriod() ?: $this->config['validity_period'];
164
165
        $request = [
166 8
            'username' => $this->config['username'],
167 8
            'password' => $this->config['password'],
168 8
            'method' => $this->config['method'],
169 8
            'sender_number' => $sender_number,
170 8
            'sender_string' => $sender_string,
171 8
            'recipients' => $this->prepareRecipients($sms),
172 8
            'text' => str_replace(' ', '+', $sms->getText()),
173 8
            'user_reference' => $sms->getUserReference(),
174 8
            'delivery_start' => $deliveryStart ? urlencode($deliveryStart->format(\DateTime::RFC2822)) : null,
175 8
            'validity_period' => $validityPeriod ? $validityPeriod->i : null,
176 8
            'encoding_scheme' => $this->config['encoding_schema'],
177 8
            'charset' => urlencode($this->config['charset']),
178 8
        ];
179
180
        /*
181
        * if sender_string is passed and is empty, it's impossible to use sender_number as sender,
182
        * Skebby will use the default sender set in Skebby Administration Panel
183
        */
184 8
        if (trim($request['sender_string']) === '') {
185 7
            unset($request['sender_string']);
186 7
        }
187
188 8
        $serializedRequest = [];
189 8
        foreach ($request as $key => $value) {
190 8
            $serializedRequest[] = "$key=$value";
191 8
        }
192
193 8
        return implode('&', $serializedRequest);
194
    }
195
196
    /**
197
     * @param Sms $sms
198
     *
199
     * @return string
200
     */
201 8
    private function prepareRecipients(Sms $sms)
202
    {
203 8
        $recipients = $sms->getRecipients();
204
205 8
        if (! $sms->hasRecipientVariables()) {
206 5
            $recipients = array_map([$this, 'normalizePhoneNumber'], $recipients);
207
208 5
            return json_encode($recipients);
209
        }
210
211 3
        $recipientVariables = $sms->getRecipientVariables();
212
213 3
        return json_encode(array_map(function ($recipient) use ($recipientVariables) {
214 3
            $targetVariables = [];
215 3
            if (isset($recipientVariables[$recipient])) {
216 3
                $targetVariables = $recipientVariables[$recipient];
217 3
            }
218
219 3
            return array_merge(['recipient' => $this->normalizePhoneNumber($recipient)], $targetVariables);
220 3
        }, $recipients));
221
    }
222
223
    /**
224
     * @param string $phoneNumber
225
     *
226
     * @return string
227
     *
228
     * @throws NumberParseException
229
     */
230 8
    private function normalizePhoneNumber($phoneNumber)
231
    {
232 8
        $utils = PhoneNumberUtil::getInstance();
233 8
        $parsed = $utils->parse(preg_replace('/^00/', '+', $phoneNumber), null);
234
235 8
        $phoneNumber = $utils->format($parsed, PhoneNumberFormat::E164);
236
237 8
        return substr($phoneNumber, 1);
238
    }
239
240
    /**
241
     * @param string $request
242
     *
243
     * @return Response
244
     */
245 8
    private function executeRequest($request)
246
    {
247 8
        $response = $this->transport->executeRequest($this->config['endpoint_uri'], $request);
248
249 8
        return new Response($response);
250
    }
251
252
    /**
253
     * Get sender parameters (alphanumeric sender or phone number)
254
     *
255
     * @param Sms $sms
256
     *
257
     * @return array
258
     */
259 8
    private function getSenderParams(Sms $sms)
260
    {
261 8
        $sender = $sms->getSender() ?: $this->config['sender'];
262
263 8
        $sender_string = null;
264 8
        $sender_number = null;
265
266
        try {
267 8
            $sender_number = $this->normalizePhoneNumber($sender);
268 8
        } catch (NumberParseException $e) {
269 1
            $sender_string = substr($sender, 0, 11);
270
        }
271
272 8
        return [$sender_string, $sender_number];
273
    }
274
}
275