PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / 1.2.20
VikAppointments Services Booking Calendar v1.2.20
1.2.21 1.2.20 trunk 1.2.17 1.2.18 1.2.19
vikappointments / libraries / adapter / sms / driver.php
vikappointments / libraries / adapter / sms Last commit date
dispatcher.php 1 month ago driver.php 1 month ago status.php 1 month ago
driver.php
542 lines
1 <?php
2 /**
3 * @package VikWP - Libraries
4 * @subpackage adapter.sms
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2023 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 JLoader::import('adapter.sms.status');
15
16 /**
17 * This class describes the events that an abstract sms driver should handle.
18 *
19 * @since 10.1.30
20 */
21 abstract class JSmsDriver
22 {
23 /**
24 * The name of the plugin that has requested the sms.
25 *
26 * @var string
27 */
28 protected $caller;
29
30 /**
31 * The driver identifier.
32 *
33 * @var string
34 */
35 protected $driver;
36
37 /**
38 * The order details.
39 *
40 * @var JObject
41 */
42 protected $order;
43
44 /**
45 * The driver configuration parameters.
46 *
47 * @var JObject
48 */
49 protected $params;
50
51 /**
52 * Class constructor.
53 *
54 * @param string $caller The name of the plugin that requested the sms.
55 * @param mixed $order The order details to start the transaction.
56 * @param mixed $params The configuration of the driver.
57 */
58 public function __construct($caller, $order, $params = array())
59 {
60 if (is_string($params))
61 {
62 $params = (array) json_decode($params);
63 }
64
65 $this->caller = $caller;
66 $this->order = new JObject($order);
67 $this->params = new JObject($params);
68 }
69
70 /**
71 * Returns the name of the plugin that dispatched the sms.
72 *
73 * @return string
74 */
75 public function getCaller()
76 {
77 return $this->caller;
78 }
79
80 /**
81 * Checks if the given caller matches the current one.
82 *
83 * @param string $caller The caller to check.
84 *
85 * @return boolean True if the callers are equal, otherwise false.
86 *
87 * @uses getCaller()
88 */
89 public function isCaller($caller)
90 {
91 return !strcasecmp($this->getCaller(), $caller);
92 }
93
94 /**
95 * Returns the name of the driver.
96 *
97 * @return string
98 */
99 public function getDriver()
100 {
101 if (!$this->driver)
102 {
103 $class = get_class($this);
104
105 // extract driver name from classname
106 $this->driver = preg_replace_callback("/{$this->caller}Sms(.*?)/i", function($match)
107 {
108 return $match[1];
109 }, $class);
110
111 $this->driver = strtolower($this->driver);
112 }
113
114 return $this->driver;
115 }
116
117 /**
118 * Checks if the given driver matches the current one.
119 *
120 * @param string $driver The driver to check.
121 *
122 * @return boolean True if the drivers are equal, otherwise false.
123 *
124 * @uses getDriver()
125 */
126 public function isDriver($driver)
127 {
128 return !strcasecmp($this->getDriver(), $driver);
129 }
130
131 /**
132 * Returns the order detail related to the specified key.
133 *
134 * @param string $key The registry key.
135 * @param mixed $def The default value if the key is not set.
136 *
137 * @return mixed The registry value, or the default one.
138 */
139 public function get($key, $def = null)
140 {
141 return $this->order->get($key, $def);
142 }
143
144 /**
145 * Updates the specified key into the order details object.
146 *
147 * @param string $key The registry key.
148 * @param mixed $val The value to update.
149 *
150 * @param mixed The old value.
151 */
152 public function set($key, $val)
153 {
154 return $this->order->set($key, $val);
155 }
156
157 /**
158 * Returns an associative array containing the order details.
159 *
160 * @return array
161 */
162 public function getOrder()
163 {
164 return $this->order->getProperties();
165 }
166
167 /**
168 * Replaces the internal order details with the specified ones.
169 *
170 * @param array|object $order
171 *
172 * @return void
173 *
174 * @since 10.1.69
175 */
176 public function setOrder($order)
177 {
178 $this->order = new JObject($order);
179 }
180
181 /**
182 * Returns the configuration parameter related to the specified key.
183 *
184 * @param string $key The registry key.
185 * @param mixed $def The default value if the key is not set.
186 *
187 * @return mixed The registry value, or the default one.
188 */
189 public function getParam($key, $def = null)
190 {
191 return $this->params->get($key, $def);
192 }
193
194 /**
195 * Updates the specified key into the configuration object.
196 *
197 * @param string $key The registry key.
198 * @param mixed $val The value to update.
199 *
200 * @param mixed The old value.
201 */
202 public function setParam($key, $val)
203 {
204 return $this->params->set($key, $val);
205 }
206
207 /**
208 * Returns an associative array containing the driver parameters.
209 *
210 * @return array
211 */
212 public function getParams()
213 {
214 return $this->params->getProperties();
215 }
216
217 /**
218 * Returns an associative array containing the form
219 * fields used to construct the configuration of the driver.
220 *
221 * This method triggers two ACTIONS to manipulate the
222 * configuration array before it is returned:
223 * - sms_driver_before_admin_params
224 * - sms_driver_after_admin_params
225 *
226 * @return array The sms driver configuration.
227 *
228 * @uses buildAdminParameters()
229 */
230 public function getAdminParameters()
231 {
232 /**
233 * Plugins can manipulate the properties of this object.
234 * Fires before the configuration array is generated.
235 *
236 * @param self A reference to this object.
237 *
238 * @since 10.1.30
239 */
240 do_action('sms_driver_before_admin_params_' . $this->caller, array(&$this));
241
242 // children drivers will create the configuration form (as array)
243 $config = $this->buildAdminParameters();
244
245 /**
246 * Plugins can manipulate the configuration form of the driver.
247 * Fires after generating the config form.
248 *
249 * @param self A reference to this object.
250 * @param array A reference to the configuration array.
251 *
252 * @since 10.1.30
253 */
254 do_action_ref_array('sms_driver_after_admin_params_' . $this->caller, array(&$this, &$config));
255
256 return $config;
257 }
258
259 /**
260 * Abstract method used to build the associative array
261 * to allow the plugins to construct a configuration form.
262 *
263 * In case the driver needs an API Key, the array should
264 * be built as follows:
265 *
266 * {"apikey": {"type": "text", "label": "API Key"}}
267 *
268 * @return array The associative array.
269 */
270 protected function buildAdminParameters()
271 {
272 // return an empty array because a sms driver
273 // may not need a configuration
274 return array();
275 }
276
277 /**
278 * Dispatches the SMS to the specified phone number.
279 *
280 * This method triggers the ACTIONS below:
281 * - sms_driver_before_send
282 * - sms_driver_after_send
283 *
284 * @param string $phone The sms receiver.
285 * @param string $text The message to send.
286 *
287 * @return mixed An object describing the status of the sms.
288 *
289 * @uses dispatch()
290 */
291 public function sendMessage($phone, $text)
292 {
293 /**
294 * Plugins can manipulate the properties of this object.
295 * Fires before sending the SMS.
296 *
297 * @param self A reference to this object.
298 * @param string The sms receiver.
299 * @param string The message to send.
300 *
301 * @since 10.1.30
302 */
303 do_action('sms_driver_before_send_' . $this->caller, array(&$this, &$phone, &$text));
304
305 $status = new JSmsStatus();
306
307 // sanitize phone number
308 $phone = $this->sanitizePhone($phone);
309
310 // children drivers will dispatch the message
311 $this->dispatch($phone, $text, $status);
312
313 // inject receiver and SMS within the status object
314 $status->setData('phone', $phone);
315 $status->setData('sms', $text);
316
317 $response = null;
318
319 /**
320 * Plugins can manipulate the response object to return.
321 * By filling the &$response variable, this method will return
322 * it instead of the default &$status one.
323 * Fires after dispatching the sms.
324 *
325 * @param self A reference to this object.
326 * @param JSmsStatus A reference to the status object.
327 * @param mixed A reference to the final response (null by default).
328 *
329 * @since 10.1.30
330 */
331 do_action_ref_array('sms_driver_after_send_' . $this->caller, array(&$this, &$status, &$response));
332
333 if (is_null($response))
334 {
335 // no hook fired, the response will be equals to the status object
336 $response = $status;
337 }
338
339 return $response;
340 }
341
342 /**
343 * Abstract method used to dispatch a SMS to the specified phone.
344 *
345 * @param string $phone The sms receiver.
346 * @param string $text The message to send.
347 * @param JSmsStatus &$status The status of the notification.
348 *
349 * @return void
350 */
351 abstract protected function dispatch($phone, $text, JSmsStatus &$status);
352
353 /**
354 * Checks whether the current driver is able to estimate the credit.
355 *
356 * @return boolean
357 */
358 public function canEstimate()
359 {
360 // inherit in chidlren classes
361 return false;
362 }
363
364 /**
365 * Tries to recover the remaining balance.
366 *
367 * This method triggers the ACTIONS below:
368 * - sms_driver_before_estimate
369 * - sms_driver_after_estimate
370 *
371 * @param string $phone An optional receiver.
372 * @param string $text An optional message.
373 *
374 * @return mixed The remaining balance on success, false otherwise.
375 *
376 * @uses estimate()
377 */
378 public function getCredit($phone = null, $text = null)
379 {
380 /**
381 * Plugins can manipulate the properties of this object.
382 * Fires before estimating the remaining credit.
383 *
384 * @param self A reference to this object.
385 * @param string The sms receiver.
386 * @param string The message to send.
387 *
388 * @since 10.1.30
389 */
390 do_action('sms_driver_before_estimate_' . $this->caller, array(&$this, &$phone, &$text));
391
392 // sanitize phone number
393 $phone = $this->sanitizePhone($phone);
394
395 try
396 {
397 // children drivers will estimate the credit
398 $credit = $this->estimateCredit($phone, $text);
399 }
400 catch (Exception $e)
401 {
402 $credit = false;
403 }
404
405 /**
406 * Plugins can manipulate the resulting credit.
407 * Fires after estimating the user credit.
408 *
409 * @param self A reference to this object.
410 * @param mixed A reference to the user credit (null by default).
411 *
412 * @since 10.1.30
413 */
414 do_action_ref_array('sms_driver_after_estimate_' . $this->caller, array(&$this, &$credit));
415
416 return $credit;
417 }
418
419 /**
420 * Tries to estimate the remaining user credit.
421 *
422 * @param string $phone An optional receiver.
423 * @param string $text An optional message.
424 *
425 * @return mixed The remaining balance on success, false otherwise.
426 *
427 * @throws Exception
428 */
429 protected function estimateCredit($phone, $text)
430 {
431 // inherit in children classes to estimate credit
432 throw new Exception('Not implemented', 501);
433 }
434
435 /**
436 * Validates the response.
437 * Implement for platform BC.
438 *
439 * @param mixed $response
440 *
441 * @return boolean
442 */
443 public function validateResponse($response)
444 {
445 // reset log
446 $this->log = '';
447
448 if (!$response instanceof JSmsStatus)
449 {
450 // invalid argument
451 return false;
452 }
453
454 // register log
455 $this->log = $response->log;
456
457 // check sms status
458 return $response->isVerified();
459 }
460
461 /**
462 * Returns the latest logs.
463 *
464 * @return string
465 */
466 public function getLog()
467 {
468 return isset($this->log) ? $this->log : '';
469 }
470
471 /**
472 * Helper method used to sanitize phone numbers.
473 *
474 * @param string $phone The phone number to sanitize.
475 *
476 * @return string The cleansed number.
477 */
478 public function sanitizePhone($phone)
479 {
480 // trim unexpected characters
481 $phone = preg_replace("/[^0-9+]/", '', $phone);
482
483 // replace 00 dial code with +
484 if (substr($phone, 0, 2) == '00')
485 {
486 $phone = '+' . substr($phone, 2);
487 }
488
489 return $phone;
490 }
491
492 /**
493 * Adds the dial code to the phone number.
494 *
495 * @param string $prefix The dial code to prepend.
496 * @param string $phone The phone number.
497 *
498 * @return string The resulting phone number.
499 */
500 public function addDialCode($prefix, $phone)
501 {
502 // check if the phone number already owns a dial code
503 if ($prefix && !preg_match("/^\+/", $phone))
504 {
505 // sanitize dial code
506 $prefix = $this->sanitizePhone($prefix);
507
508 // normalize dial code
509 $prefix = '+' . ltrim($prefix, '+');
510
511 // prepend dial code
512 $phone = $prefix . $phone;
513 }
514
515 return $phone;
516 }
517
518 /**
519 * Check if the specified message contains UTF-8 characters.
520 *
521 * @param string $message The string to check.
522 *
523 * @return boolean True if unicode, otherwise false.
524 */
525 public function isUnicode($message)
526 {
527 if (function_exists('iconv'))
528 {
529 $latin = @iconv('UTF-8', 'ISO-8859-1', $message);
530
531 if (strcmp($latin, $message))
532 {
533 $arr = unpack('H*hex', @iconv('UTF-8', 'UCS-2BE', $message));
534 //return strtoupper($arr['hex']);
535 return true;
536 }
537 }
538
539 return false;
540 }
541 }
542