Completed
Push — master ( c6402c...17293b )
by Alessandro
07:29
created

Client::prepareRequest()   B

Complexity

Conditions 6
Paths 32

Size

Total Lines 29
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 6.0033

Importance

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