1 | <?php |
||
30 | */ |
||
31 | class Client |
||
32 | { |
||
33 | private array $config; |
||
|
|||
34 | |||
35 | private TransportInterface $transport; |
||
36 | |||
37 | private ?EventDispatcherInterface $dispatcher; |
||
38 | |||
39 | /** |
||
40 | * Client constructor. |
||
41 | * |
||
42 | * @throws \Fazland\SkebbyRestClient\Exception\RuntimeException |
||
43 | */ |
||
44 | public function __construct(array $options, ?TransportInterface $transport = null, ?EventDispatcherInterface $dispatcher = null) |
||
45 | { |
||
46 | $resolver = new OptionsResolver(); |
||
47 | |||
48 | 9 | $this->configureOptions($resolver); |
|
49 | $this->config = $resolver->resolve($options); |
||
50 | 9 | ||
51 | $this->transport = $transport ?? Factory::createTransport(); |
||
52 | 9 | $this->dispatcher = $dispatcher; |
|
53 | 9 | } |
|
54 | |||
55 | 9 | /** |
|
56 | 9 | * Sends an SMS. |
|
57 | * |
||
58 | * @return Response[] |
||
59 | * |
||
60 | * @throws EmptyResponseException |
||
61 | * @throws NoRecipientsSpecifiedException |
||
62 | * @throws UnknownErrorResponseException |
||
63 | */ |
||
64 | public function send(Sms $sms): array |
||
65 | { |
||
66 | if (! $sms->hasRecipients()) { |
||
67 | throw new NoRecipientsSpecifiedException(); |
||
68 | } |
||
69 | 9 | ||
70 | $messages = []; |
||
71 | 9 | ||
72 | 1 | $recipients = $sms->getRecipients(); |
|
73 | foreach (array_chunk($recipients, Recipients::MAX) as $chunk) { |
||
74 | $message = clone $sms; |
||
75 | 8 | $message |
|
76 | ->setRecipients($chunk) |
||
77 | 8 | ->clearRecipientVariables() |
|
78 | 8 | ; |
|
79 | 8 | ||
80 | foreach ($chunk as $recipient) { |
||
81 | 8 | if (! isset($sms->getRecipientVariables()[$recipient])) { |
|
82 | 8 | continue; |
|
83 | } |
||
84 | |||
85 | 8 | foreach ($sms->getRecipientVariables()[$recipient] as $variable => $value) { |
|
86 | 8 | $message->addRecipientVariable($recipient, $variable, $value); |
|
87 | 6 | } |
|
88 | } |
||
89 | |||
90 | 3 | $messages[] = $message; |
|
91 | 3 | } |
|
92 | |||
93 | $responses = []; |
||
94 | foreach ($messages as $message) { |
||
95 | 8 | $request = $this->prepareRequest($message); |
|
96 | |||
97 | $responses[] = $this->executeRequest($request); |
||
98 | 8 | ||
99 | 8 | if (null !== $this->dispatcher) { |
|
100 | 8 | $this->dispatcher->dispatch(new SmsMessageSent($message)); |
|
101 | } |
||
102 | 8 | } |
|
103 | |||
104 | return $responses; |
||
105 | 6 | } |
|
106 | |||
107 | /** |
||
108 | * Configure default options for client. |
||
109 | * |
||
110 | * It takes required options username, password, sender and method. |
||
111 | * validity_period MUST be a \DateInterval object if set |
||
112 | * delivery_start MUST be a \DateTime object if set |
||
113 | */ |
||
114 | private function configureOptions(OptionsResolver $resolver): void |
||
115 | { |
||
116 | $resolver |
||
117 | 9 | ->setRequired([ |
|
118 | 'username', |
||
119 | 'password', |
||
120 | 9 | 'sender', |
|
121 | 9 | 'method', |
|
122 | ]) |
||
123 | ->setDefaults([ |
||
124 | 'delivery_start' => null, |
||
125 | 'charset' => Charsets::UTF8, |
||
126 | 9 | 'validity_period' => \DateInterval::createFromDateString('2800 minutes'), |
|
127 | 9 | 'encoding_schema' => EncodingSchemas::NORMAL, |
|
128 | 'endpoint_uri' => Endpoints::REST_HTTPS, |
||
129 | 9 | ]) |
|
130 | 9 | ->setAllowedTypes('username', 'string') |
|
131 | ->setAllowedTypes('password', 'string') |
||
132 | ->setAllowedTypes('sender', 'string') |
||
133 | 9 | ->setAllowedTypes('method', 'string') |
|
134 | 9 | ->setAllowedTypes('delivery_start', ['null', 'DateTime']) |
|
135 | 9 | ->setAllowedTypes('validity_period', ['null', 'DateInterval']) |
|
136 | 9 | ->setAllowedTypes('encoding_schema', 'string') |
|
137 | 9 | ->setAllowedTypes('charset', 'string') |
|
138 | 9 | ->setAllowedTypes('endpoint_uri', 'string') |
|
139 | 9 | ->setAllowedValues('method', [ |
|
140 | 9 | SendMethods::CLASSIC, |
|
141 | 9 | SendMethods::CLASSIC_PLUS, |
|
142 | 9 | SendMethods::BASIC, |
|
143 | 9 | SendMethods::TEST_CLASSIC, |
|
144 | SendMethods::TEST_CLASSIC_PLUS, |
||
145 | SendMethods::TEST_BASIC, |
||
146 | ]) |
||
147 | ->setAllowedValues('validity_period', function (\DateInterval $value) { |
||
148 | return $value->i >= ValidityPeriods::MIN && $value->i <= ValidityPeriods::MAX; |
||
149 | }) |
||
150 | 9 | ->setAllowedValues('encoding_schema', [ |
|
151 | 9 | EncodingSchemas::NORMAL, |
|
152 | 9 | EncodingSchemas::UCS2, |
|
153 | 9 | ]) |
|
154 | 9 | ->setAllowedValues('charset', [ |
|
155 | 9 | Charsets::ISO_8859_1, |
|
156 | Charsets::UTF8, |
||
157 | 9 | ]) |
|
158 | 9 | ; |
|
159 | } |
||
160 | |||
161 | /** |
||
162 | 9 | * Converts the {@see Sms} to an array request. |
|
163 | */ |
||
164 | private function prepareRequest(Sms $sms): string |
||
165 | { |
||
166 | [$senderString, $senderNumber] = $this->getSenderParams($sms); |
||
167 | |||
168 | $deliveryStart = $sms->getDeliveryStart() ?: $this->config['delivery_start']; |
||
169 | $validityPeriod = $sms->getValidityPeriod() ?: $this->config['validity_period']; |
||
170 | |||
171 | 8 | $request = [ |
|
172 | 'username' => $this->config['username'], |
||
173 | 8 | 'password' => $this->config['password'], |
|
174 | 'method' => $this->config['method'], |
||
175 | 8 | 'sender_number' => $senderNumber, |
|
176 | 8 | 'sender_string' => $senderString, |
|
177 | 'recipients' => $this->prepareRecipients($sms), |
||
178 | 'text' => str_replace(' ', '+', $sms->getText()), |
||
179 | 8 | 'user_reference' => $sms->getUserReference(), |
|
180 | 8 | 'delivery_start' => $deliveryStart ? urlencode($deliveryStart->format(\DateTime::RFC2822)) : null, |
|
181 | 8 | 'validity_period' => $validityPeriod ? $validityPeriod->i : null, |
|
182 | 8 | 'encoding_scheme' => $this->config['encoding_schema'], |
|
183 | 8 | 'charset' => urlencode($this->config['charset']), |
|
184 | 8 | ]; |
|
185 | 8 | ||
186 | 8 | /* |
|
187 | 8 | * if sender_string is passed and is empty, it's impossible to use sender_number as sender, |
|
188 | 8 | * Skebby will use the default sender set in Skebby Administration Panel. |
|
189 | 8 | */ |
|
190 | 8 | if ('' === trim($request['sender_string'])) { |
|
191 | unset($request['sender_string']); |
||
192 | } |
||
193 | |||
194 | $serializedRequest = []; |
||
195 | foreach ($request as $key => $value) { |
||
196 | $serializedRequest[] = "$key=$value"; |
||
197 | 8 | } |
|
198 | 7 | ||
199 | return implode('&', $serializedRequest); |
||
200 | } |
||
201 | 8 | ||
202 | 8 | /** |
|
203 | 8 | * Converts the {@see Sms} recipients into an array. |
|
204 | */ |
||
205 | private function prepareRecipients(Sms $sms): string |
||
206 | 8 | { |
|
207 | $recipients = $sms->getRecipients(); |
||
208 | |||
209 | if (! $sms->hasRecipientVariables()) { |
||
210 | $recipients = array_map([$this, 'normalizePhoneNumber'], $recipients); |
||
211 | |||
212 | return json_encode($recipients); |
||
213 | } |
||
214 | |||
215 | $recipientVariables = $sms->getRecipientVariables(); |
||
216 | 8 | ||
217 | return json_encode(array_map(function ($recipient) use ($recipientVariables) { |
||
218 | 8 | $targetVariables = []; |
|
219 | if (isset($recipientVariables[$recipient])) { |
||
220 | 8 | $targetVariables = $recipientVariables[$recipient]; |
|
221 | 5 | } |
|
222 | |||
223 | 5 | return array_merge(['recipient' => $this->normalizePhoneNumber($recipient)], $targetVariables); |
|
224 | }, $recipients), JSON_THROW_ON_ERROR); |
||
225 | } |
||
226 | 3 | ||
227 | /** |
||
228 | 3 | * Normalizes the phoneNumber. |
|
229 | 3 | * |
|
230 | 3 | * @throws NumberParseException |
|
231 | 3 | */ |
|
232 | private function normalizePhoneNumber(string $phoneNumber): string |
||
233 | { |
||
234 | 3 | $utils = PhoneNumberUtil::getInstance(); |
|
235 | 3 | $parsed = $utils->parse(preg_replace('/^00/', '+', $phoneNumber)); |
|
236 | |||
237 | $phoneNumber = $utils->format($parsed, PhoneNumberFormat::E164); |
||
238 | |||
239 | return substr($phoneNumber, 1); |
||
240 | } |
||
241 | |||
242 | /** |
||
243 | * Executes the request. |
||
244 | * |
||
245 | * @throws EmptyResponseException |
||
246 | * @throws UnknownErrorResponseException |
||
247 | 8 | */ |
|
248 | private function executeRequest(string $request): Response |
||
249 | 8 | { |
|
250 | 8 | $response = $this->transport->executeRequest($this->config['endpoint_uri'], $request); |
|
251 | |||
252 | 8 | return new Response($response); |
|
253 | } |
||
254 | 8 | ||
255 | /** |
||
256 | * Gets sender parameters (alphanumeric sender or phone number). |
||
257 | * |
||
258 | * @return string[] |
||
259 | */ |
||
260 | private function getSenderParams(Sms $sms): array |
||
261 | { |
||
262 | $sender = $sms->getSender() ?: $this->config['sender']; |
||
263 | |||
264 | $senderString = ''; |
||
265 | $senderNumber = ''; |
||
266 | |||
267 | 8 | try { |
|
268 | $senderNumber = $this->normalizePhoneNumber($sender); |
||
269 | 8 | } catch (NumberParseException $e) { |
|
270 | $senderString = substr($sender, 0, 11); |
||
271 | 8 | } |
|
272 | |||
273 | return [$senderString, $senderNumber]; |
||
274 | } |
||
275 | } |
||
276 |