This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
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
|
|||
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 |
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: