Completed
Pull Request — master (#3)
by Massimiliano
04:24
created

Client::getSenderParams()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

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