Client   A
last analyzed

Complexity

Total Complexity 27

Size/Duplication

Total Lines 280
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 27
lcom 1
cbo 9
dl 0
loc 280
ccs 103
cts 103
cp 1
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 2
B send() 0 42 8
A configureOptions() 0 46 2
B prepareRequest() 0 37 7
A prepareRecipients() 0 21 3
A normalizePhoneNumber() 0 9 1
A executeRequest() 0 6 1
A getSenderParams() 0 15 3
1
<?php declare(strict_types=1);
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\Event\SmsMessageSent;
14
use Fazland\SkebbyRestClient\Exception\EmptyResponseException;
15
use Fazland\SkebbyRestClient\Exception\NoRecipientsSpecifiedException;
16
use Fazland\SkebbyRestClient\Exception\UnknownErrorResponseException;
17
use Fazland\SkebbyRestClient\Transport\Factory;
18
use Fazland\SkebbyRestClient\Transport\TransportInterface;
19
use libphonenumber\NumberParseException;
20
use libphonenumber\PhoneNumberFormat;
21
use libphonenumber\PhoneNumberUtil;
22
use Psr\EventDispatcher\EventDispatcherInterface;
23
use Symfony\Component\OptionsResolver\OptionsResolver;
24
25
/**
26
 * Skebby REST client.
27
 *
28
 * @author Massimiliano Braglia <[email protected]>
29
 */
30
class Client
31
{
32
    /**
33
     * @var array
34
     */
35
    private $config;
36
37
    /**
38
     * @var TransportInterface
39
     */
40
    private $transport;
41
42
    /**
43
     * @var EventDispatcherInterface|null
44
     */
45
    private $dispatcher;
46
47
    /**
48 9
     * Client constructor.
49
     *
50 9
     * @param array                         $options
51
     * @param TransportInterface|null       $transport
52 9
     * @param EventDispatcherInterface|null $dispatcher
53 9
     *
54
     * @throws \Fazland\SkebbyRestClient\Exception\RuntimeException
55 9
     */
56 9
    public function __construct(array $options, TransportInterface $transport = null, EventDispatcherInterface $dispatcher = null)
57
    {
58
        $resolver = new OptionsResolver();
59
60
        $this->configureOptions($resolver);
61
        $this->config = $resolver->resolve($options);
62
63
        $this->transport = null === $transport ? Factory::createTransport() : $transport;
64
        $this->dispatcher = $dispatcher;
65
    }
66
67
    /**
68
     * Sends an SMS.
69 9
     *
70
     * @param Sms $sms
71 9
     *
72 1
     * @return Response[]
73
     *
74
     * @throws EmptyResponseException
75 8
     * @throws NoRecipientsSpecifiedException
76
     * @throws UnknownErrorResponseException
77 8
     */
78 8
    public function send(Sms $sms): array
79 8
    {
80
        if (! $sms->hasRecipients()) {
81 8
            throw new NoRecipientsSpecifiedException();
82 8
        }
83
84
        $messages = [];
85 8
86 8
        $recipients = $sms->getRecipients();
87 6
        foreach (array_chunk($recipients, Recipients::MAX) as $chunk) {
88
            $message = clone $sms;
89
            $message
90 3
                ->setRecipients($chunk)
91 3
                ->clearRecipientVariables()
92
            ;
93
94
            foreach ($chunk as $recipient) {
95 8
                if (! isset($sms->getRecipientVariables()[$recipient])) {
96
                    continue;
97
                }
98 8
99 8
                foreach ($sms->getRecipientVariables()[$recipient] as $variable => $value) {
100 8
                    $message->addRecipientVariable($recipient, $variable, $value);
101
                }
102 8
            }
103
104
            $messages[] = $message;
105 6
        }
106
107
        $responses = [];
108
        foreach ($messages as $message) {
109
            $request = $this->prepareRequest($message);
110
111
            $responses[] = $this->executeRequest($request);
112
113
            if (null !== $this->dispatcher) {
114
                $this->dispatcher->dispatch(new SmsMessageSent($message));
0 ignored issues
show
Documentation introduced by
new \Fazland\SkebbyRestC...msMessageSent($message) is of type object<Fazland\SkebbyRes...t\Event\SmsMessageSent>, but the function expects a object<Psr\EventDispatcher\object>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
115
            }
116
        }
117 9
118
        return $responses;
119
    }
120 9
121 9
    /**
122
     * Configure default options for client.
123
     *
124
     * It takes required options username, password, sender and method.
125
     * validity_period MUST be a \DateInterval object if set
126 9
     * delivery_start MUST be a \DateTime object if set
127 9
     *
128
     * @param OptionsResolver $resolver
129 9
     */
130 9
    private function configureOptions(OptionsResolver $resolver)
131
    {
132
        $resolver
133 9
            ->setRequired([
134 9
                'username',
135 9
                'password',
136 9
                'sender',
137 9
                'method',
138 9
            ])
139 9
            ->setDefaults([
140 9
                'delivery_start' => null,
141 9
                'charset' => Charsets::UTF8,
142 9
                'validity_period' => \DateInterval::createFromDateString('2800 minutes'),
143 9
                'encoding_schema' => EncodingSchemas::NORMAL,
144
                'endpoint_uri' => Endpoints::REST_HTTPS,
145
            ])
146
            ->setAllowedTypes('username', 'string')
147
            ->setAllowedTypes('password', 'string')
148
            ->setAllowedTypes('sender', 'string')
149
            ->setAllowedTypes('method', 'string')
150 9
            ->setAllowedTypes('delivery_start', ['null', 'DateTime'])
151 9
            ->setAllowedTypes('validity_period', ['null', 'DateInterval'])
152 9
            ->setAllowedTypes('encoding_schema', 'string')
153 9
            ->setAllowedTypes('charset', 'string')
154 9
            ->setAllowedTypes('endpoint_uri', 'string')
155 9
            ->setAllowedValues('method', [
156
                SendMethods::CLASSIC,
157 9
                SendMethods::CLASSIC_PLUS,
158 9
                SendMethods::BASIC,
159
                SendMethods::TEST_CLASSIC,
160
                SendMethods::TEST_CLASSIC_PLUS,
161
                SendMethods::TEST_BASIC,
162 9
            ])
163
            ->setAllowedValues('validity_period', function (\DateInterval $value) {
164
                return $value->i >= ValidityPeriods::MIN && $value->i <= ValidityPeriods::MAX;
165
            })
166
            ->setAllowedValues('encoding_schema', [
167
                EncodingSchemas::NORMAL,
168
                EncodingSchemas::UCS2,
169
            ])
170
            ->setAllowedValues('charset', [
171 8
                Charsets::ISO_8859_1,
172
                Charsets::UTF8,
173 8
            ])
174
        ;
175 8
    }
176 8
177
    /**
178
     * Converts the {@see Sms} to an array request.
179 8
     *
180 8
     * @param Sms $sms
181 8
     *
182 8
     * @return string
183 8
     */
184 8
    private function prepareRequest(Sms $sms): string
185 8
    {
186 8
        list($senderString, $senderNumber) = $this->getSenderParams($sms);
187 8
188 8
        $deliveryStart = $sms->getDeliveryStart() ?: $this->config['delivery_start'];
189 8
        $validityPeriod = $sms->getValidityPeriod() ?: $this->config['validity_period'];
190 8
191
        $request = [
192
            'username' => $this->config['username'],
193
            'password' => $this->config['password'],
194
            'method' => $this->config['method'],
195
            'sender_number' => $senderNumber,
196
            'sender_string' => $senderString,
197 8
            'recipients' => $this->prepareRecipients($sms),
198 7
            'text' => str_replace(' ', '+', $sms->getText()),
199
            'user_reference' => $sms->getUserReference(),
200
            'delivery_start' => $deliveryStart ? urlencode($deliveryStart->format(\DateTime::RFC2822)) : null,
201 8
            'validity_period' => $validityPeriod ? $validityPeriod->i : null,
202 8
            'encoding_scheme' => $this->config['encoding_schema'],
203 8
            'charset' => urlencode($this->config['charset']),
204
        ];
205
206 8
        /*
207
         * if sender_string is passed and is empty, it's impossible to use sender_number as sender,
208
         * Skebby will use the default sender set in Skebby Administration Panel.
209
         */
210
        if ('' === trim($request['sender_string'])) {
211
            unset($request['sender_string']);
212
        }
213
214
        $serializedRequest = [];
215
        foreach ($request as $key => $value) {
216 8
            $serializedRequest[] = "$key=$value";
217
        }
218 8
219
        return implode('&', $serializedRequest);
220 8
    }
221 5
222
    /**
223 5
     * Converts the {@see Sms} recipients into an array.
224
     *
225
     * @param Sms $sms
226 3
     *
227
     * @return string
228 3
     */
229 3
    private function prepareRecipients(Sms $sms): string
230 3
    {
231 3
        $recipients = $sms->getRecipients();
232
233
        if (! $sms->hasRecipientVariables()) {
234 3
            $recipients = array_map([$this, 'normalizePhoneNumber'], $recipients);
235 3
236
            return json_encode($recipients);
237
        }
238
239
        $recipientVariables = $sms->getRecipientVariables();
240
241
        return json_encode(array_map(function ($recipient) use ($recipientVariables) {
242
            $targetVariables = [];
243
            if (isset($recipientVariables[$recipient])) {
244
                $targetVariables = $recipientVariables[$recipient];
245
            }
246
247 8
            return array_merge(['recipient' => $this->normalizePhoneNumber($recipient)], $targetVariables);
248
        }, $recipients));
249 8
    }
250 8
251
    /**
252 8
     * Normalizes the phoneNumber.
253
     *
254 8
     * @param string $phoneNumber
255
     *
256
     * @return string
257
     *
258
     * @throws NumberParseException
259
     */
260
    private function normalizePhoneNumber(string $phoneNumber): string
261
    {
262
        $utils = PhoneNumberUtil::getInstance();
263
        $parsed = $utils->parse(preg_replace('/^00/', '+', $phoneNumber), null);
264
265
        $phoneNumber = $utils->format($parsed, PhoneNumberFormat::E164);
266
267 8
        return substr($phoneNumber, 1);
268
    }
269 8
270
    /**
271 8
     * Executes the request.
272
     *
273
     * @param string $request
274
     *
275
     * @return Response
276
     *
277
     * @throws EmptyResponseException
278
     * @throws UnknownErrorResponseException
279
     */
280
    private function executeRequest(string $request): Response
281 8
    {
282
        $response = $this->transport->executeRequest($this->config['endpoint_uri'], $request);
283 8
284
        return new Response($response);
285 8
    }
286 8
287
    /**
288
     * Gets sender parameters (alphanumeric sender or phone number).
289 8
     *
290 1
     * @param Sms $sms
291 1
     *
292
     * @return string[]
293
     */
294 8
    private function getSenderParams(Sms $sms): array
295
    {
296
        $sender = $sms->getSender() ?: $this->config['sender'];
297
298
        $senderString = '';
299
        $senderNumber = '';
300
301
        try {
302
            $senderNumber = $this->normalizePhoneNumber($sender);
303
        } catch (NumberParseException $e) {
304
            $senderString = substr($sender, 0, 11);
305
        }
306
307
        return [$senderString, $senderNumber];
308
    }
309
}
310