fazland /
skebby-rest-client
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: