1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Toplan\PhpSms; |
4
|
|
|
|
5
|
|
|
use SuperClosure\Serializer; |
6
|
|
|
use Toplan\TaskBalance\Balancer; |
7
|
|
|
use Toplan\TaskBalance\Task; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* Class Sms |
11
|
|
|
* |
12
|
|
|
* |
13
|
|
|
* @author toplan<[email protected]> |
14
|
|
|
*/ |
15
|
|
|
class Sms |
16
|
|
|
{ |
17
|
|
|
/** |
18
|
|
|
* The default name of balancing task. |
19
|
|
|
* |
20
|
|
|
* @var string |
21
|
|
|
*/ |
22
|
|
|
const TASK = 'PhpSms'; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* The instances of class [Toplan\PhpSms\Agent]. |
26
|
|
|
* |
27
|
|
|
* @var array |
28
|
|
|
*/ |
29
|
|
|
protected static $agents = []; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Agents use scheme, these agents are available. |
33
|
|
|
* example: [ |
34
|
|
|
* 'Agent1' => '10 backup', |
35
|
|
|
* 'Agent2' => '20 backup', |
36
|
|
|
* ] |
37
|
|
|
* |
38
|
|
|
* @var array |
39
|
|
|
*/ |
40
|
|
|
protected static $scheme = []; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* The agents` configuration information. |
44
|
|
|
* |
45
|
|
|
* @var array |
46
|
|
|
*/ |
47
|
|
|
protected static $agentsConfig = []; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* Whether to use the queue. |
51
|
|
|
* |
52
|
|
|
* @var bool |
53
|
|
|
*/ |
54
|
|
|
protected static $enableQueue = false; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* How to use the queue. |
58
|
|
|
* |
59
|
|
|
* @var \Closure |
60
|
|
|
*/ |
61
|
|
|
protected static $howToUseQueue = null; |
62
|
|
|
|
63
|
|
|
/** |
64
|
|
|
* The available hooks for balancing task. |
65
|
|
|
* |
66
|
|
|
* @var array |
67
|
|
|
*/ |
68
|
|
|
protected static $availableHooks = [ |
69
|
|
|
'beforeRun', |
70
|
|
|
'beforeDriverRun', |
71
|
|
|
'afterDriverRun', |
72
|
|
|
'afterRun', |
73
|
|
|
]; |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* An instance of class [SuperClosure\Serializer] use for serialization closures. |
77
|
|
|
* |
78
|
|
|
* @var Serializer |
79
|
|
|
*/ |
80
|
|
|
protected static $serializer = null; |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* SMS/voice verify data container. |
84
|
|
|
* |
85
|
|
|
* @var array |
86
|
|
|
*/ |
87
|
|
|
protected $smsData = [ |
88
|
|
|
'to' => null, |
89
|
|
|
'templates' => [], |
90
|
|
|
'content' => null, |
91
|
|
|
'templateData' => [], |
92
|
|
|
'voiceCode' => null, |
93
|
|
|
]; |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* The name of first agent. |
97
|
|
|
* |
98
|
|
|
* @var string|null |
99
|
|
|
*/ |
100
|
|
|
protected $firstAgent = null; |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* Whether the current instance has already pushed to the queue system. |
104
|
|
|
* |
105
|
|
|
* @var bool |
106
|
|
|
*/ |
107
|
|
|
protected $pushedToQueue = false; |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* Status container, |
111
|
|
|
* store some configuration information before serialize current instance(before enqueue). |
112
|
|
|
* |
113
|
|
|
* @var array |
114
|
|
|
*/ |
115
|
|
|
protected $_status_before_enqueue_ = []; |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* Constructor |
119
|
|
|
* |
120
|
|
|
* @param bool $autoBoot |
121
|
|
|
*/ |
122
|
6 |
|
public function __construct($autoBoot = true) |
123
|
|
|
{ |
124
|
6 |
|
if ($autoBoot) { |
125
|
3 |
|
self::bootstrap(); |
126
|
2 |
|
} |
127
|
6 |
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Boot balancing task for send SMS/voice verify. |
131
|
|
|
* |
132
|
|
|
* Note: 判断drivers是否为空不能用'empty',因为在TaskBalance库的<=v0.4.2版本中Task实例的drivers是受保护的属性(不可访问), |
133
|
|
|
* 虽然通过魔术方法可以获取到其值,但其内部却并没有使用'__isset'魔术方法对'empty'或'isset'函数进行逻辑补救. |
134
|
|
|
*/ |
135
|
6 |
|
public static function bootstrap() |
136
|
|
|
{ |
137
|
6 |
|
$task = self::getTask(); |
138
|
6 |
|
if (!count($task->drivers)) { |
139
|
3 |
|
self::configuration(); |
140
|
3 |
|
self::createDrivers($task); |
141
|
2 |
|
} |
142
|
6 |
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* Get or generate a balancing task instance for send SMS/voice verify. |
146
|
|
|
* |
147
|
|
|
* @return Task |
148
|
|
|
*/ |
149
|
15 |
|
public static function getTask() |
150
|
|
|
{ |
151
|
15 |
|
if (!Balancer::hasTask(self::TASK)) { |
152
|
3 |
|
Balancer::task(self::TASK); |
153
|
2 |
|
} |
154
|
|
|
|
155
|
15 |
|
return Balancer::getTask(self::TASK); |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
/** |
159
|
|
|
* Configuration. |
160
|
|
|
*/ |
161
|
6 |
|
protected static function configuration() |
162
|
|
|
{ |
163
|
6 |
|
$config = []; |
164
|
6 |
|
if (empty(self::$scheme)) { |
165
|
3 |
|
self::initScheme($config); |
166
|
2 |
|
} |
167
|
6 |
|
$diff = array_diff_key(self::$scheme, self::$agentsConfig); |
168
|
6 |
|
self::initAgentsConfig(array_keys($diff), $config); |
169
|
6 |
|
self::validateConfig(); |
170
|
6 |
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* Try to read agent use scheme from config file. |
174
|
|
|
* |
175
|
|
|
* @param array $config |
176
|
|
|
*/ |
177
|
3 |
|
protected static function initScheme(array &$config) |
178
|
|
|
{ |
179
|
3 |
|
$config = empty($config) ? include __DIR__ . '/../config/phpsms.php' : $config; |
180
|
3 |
|
$scheme = isset($config['scheme']) ? $config['scheme'] : []; |
181
|
3 |
|
self::scheme($scheme); |
182
|
3 |
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* Try to initialize the specified agents` configuration information. |
186
|
|
|
* |
187
|
|
|
* @param array $agents |
188
|
|
|
* @param array $config |
189
|
|
|
*/ |
190
|
6 |
|
protected static function initAgentsConfig(array $agents, array &$config) |
191
|
|
|
{ |
192
|
6 |
|
if (empty($agents)) { |
193
|
3 |
|
return; |
194
|
|
|
} |
195
|
3 |
|
$config = empty($config) ? include __DIR__ . '/../config/phpsms.php' : $config; |
196
|
3 |
|
$agentsConfig = isset($config['agents']) ? $config['agents'] : []; |
197
|
3 |
|
foreach ($agents as $name) { |
198
|
3 |
|
$agentConfig = isset($agentsConfig[$name]) ? $agentsConfig[$name] : []; |
199
|
3 |
|
self::config($name, $agentConfig); |
200
|
2 |
|
} |
201
|
3 |
|
} |
202
|
|
|
|
203
|
|
|
/** |
204
|
|
|
* validate configuration. |
205
|
|
|
* |
206
|
|
|
* @throws PhpSmsException |
207
|
|
|
*/ |
208
|
6 |
|
protected static function validateConfig() |
209
|
|
|
{ |
210
|
6 |
|
if (empty(self::$scheme)) { |
211
|
|
|
throw new PhpSmsException('Please configure at least one agent'); |
212
|
|
|
} |
213
|
6 |
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* Create drivers for the balancing task. |
217
|
|
|
* |
218
|
|
|
* @param Task $task |
219
|
|
|
*/ |
220
|
18 |
|
protected static function createDrivers(Task $task) |
221
|
|
|
{ |
222
|
3 |
|
foreach (self::$scheme as $name => $scheme) { |
223
|
|
|
//获取代理器配置 |
224
|
3 |
|
$configData = self::config($name); |
225
|
|
|
|
226
|
|
|
//解析代理器数组模式的调度配置 |
227
|
3 |
|
if (is_array($scheme)) { |
228
|
3 |
|
$data = self::parseScheme($scheme); |
229
|
3 |
|
$configData = array_merge($configData, $data); |
230
|
3 |
|
$scheme = $data['scheme']; |
231
|
2 |
|
} |
232
|
3 |
|
$scheme = is_string($scheme) ? $scheme : ''; |
233
|
|
|
|
234
|
|
|
//创建任务驱动器 |
235
|
3 |
|
$task->driver("$name $scheme")->data($configData) |
236
|
18 |
|
->work(function ($driver) { |
237
|
18 |
|
$configData = $driver->getDriverData(); |
238
|
18 |
|
$agent = self::getAgent($driver->name, $configData); |
239
|
18 |
|
$smsData = $driver->getTaskData(); |
240
|
18 |
|
extract($smsData); |
241
|
18 |
|
if (isset($smsData['voiceCode']) && $smsData['voiceCode']) { |
242
|
|
|
$agent->voiceVerify($to, $voiceCode); |
243
|
|
|
} else { |
244
|
18 |
|
$template = isset($templates[$driver->name]) ? $templates[$driver->name] : 0; |
245
|
18 |
|
$agent->sendSms($template, $to, $templateData, $content); |
246
|
|
|
} |
247
|
18 |
|
$result = $agent->result(); |
248
|
18 |
|
if ($result['success']) { |
249
|
18 |
|
$driver->success(); |
250
|
12 |
|
} |
251
|
18 |
|
unset($result['success']); |
252
|
|
|
|
253
|
18 |
|
return $result; |
254
|
3 |
|
}); |
255
|
2 |
|
} |
256
|
3 |
|
} |
257
|
|
|
|
258
|
|
|
/** |
259
|
|
|
* Parsing scheduling configuration. |
260
|
|
|
* 解析代理器的数组模式的调度配置 |
261
|
|
|
* |
262
|
|
|
* @param array $options |
263
|
|
|
* |
264
|
|
|
* @return array |
265
|
|
|
*/ |
266
|
3 |
|
protected static function parseScheme(array $options) |
267
|
|
|
{ |
268
|
3 |
|
$agentClass = self::pullOptionOutOfArrayByKey($options, 'agentClass'); |
269
|
3 |
|
$sendSms = self::pullOptionOutOfArrayByKey($options, 'sendSms'); |
270
|
3 |
|
$voiceVerify = self::pullOptionOutOfArrayByKey($options, 'voiceVerify'); |
271
|
3 |
|
$backup = self::pullOptionOutOfArrayByKey($options, 'backup') ? 'backup' : ''; |
272
|
3 |
|
$scheme = implode(' ', array_values($options)) . " $backup"; |
273
|
|
|
|
274
|
3 |
|
return compact('agentClass', 'sendSms', 'voiceVerify', 'scheme'); |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
/** |
278
|
|
|
* Pull the value out of the specified array by key. |
279
|
|
|
* |
280
|
|
|
* @param array $options |
281
|
|
|
* @param int|string $key |
282
|
|
|
* |
283
|
|
|
* @return mixed |
284
|
|
|
*/ |
285
|
3 |
|
protected static function pullOptionOutOfArrayByKey(array &$options, $key) |
286
|
|
|
{ |
287
|
3 |
|
if (!isset($options[$key])) { |
288
|
3 |
|
return; |
289
|
|
|
} |
290
|
3 |
|
$value = $options[$key]; |
291
|
3 |
|
unset($options[$key]); |
292
|
|
|
|
293
|
3 |
|
return $value; |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
/** |
297
|
|
|
* Get a sms agent instance by agent name, |
298
|
|
|
* if null, will try to create a new agent instance. |
299
|
|
|
* |
300
|
|
|
* @param string $name |
301
|
|
|
* @param array $configData |
302
|
|
|
* |
303
|
|
|
* @throws PhpSmsException |
304
|
|
|
* |
305
|
|
|
* @return mixed |
306
|
|
|
*/ |
307
|
21 |
|
public static function getAgent($name, array $configData) |
308
|
|
|
{ |
309
|
21 |
|
if (!isset(self::$agents[$name])) { |
310
|
6 |
|
$configData['name'] = $name; |
311
|
6 |
|
$className = isset($configData['agentClass']) ? $configData['agentClass'] : ('Toplan\\PhpSms\\' . $name . 'Agent'); |
312
|
6 |
|
if ((isset($configData['sendSms']) && is_callable($configData['sendSms'])) || |
313
|
6 |
|
(isset($configData['voiceVerify']) && is_callable($configData['voiceVerify']))) { |
314
|
|
|
//创建寄生代理器 |
315
|
3 |
|
$configData['agentClass'] = ''; |
316
|
3 |
|
self::$agents[$name] = new ParasiticAgent($configData); |
317
|
5 |
|
} elseif (class_exists($className)) { |
318
|
|
|
//创建新代理器 |
319
|
3 |
|
self::$agents[$name] = new $className($configData); |
320
|
2 |
|
} else { |
321
|
|
|
//无代理器可用 |
322
|
|
|
throw new PhpSmsException("Dont support [$name] agent."); |
323
|
|
|
} |
324
|
4 |
|
} |
325
|
|
|
|
326
|
21 |
|
return self::$agents[$name]; |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
/** |
330
|
|
|
* Set or get agent use scheme by agent name. |
331
|
|
|
* |
332
|
|
|
* @param mixed $agentName |
333
|
|
|
* @param mixed $scheme |
334
|
|
|
* |
335
|
|
|
* @return mixed |
336
|
|
|
*/ |
337
|
12 |
|
public static function scheme($agentName = null, $scheme = null) |
338
|
|
|
{ |
339
|
12 |
View Code Duplication |
if (($agentName === null || is_string($agentName)) && $scheme === null) { |
|
|
|
|
340
|
12 |
|
return $agentName === null ? self::$scheme : |
341
|
12 |
|
(isset(self::$scheme[$agentName]) ? self::$scheme[$agentName] : null); |
342
|
|
|
} |
343
|
6 |
|
if (is_array($agentName)) { |
344
|
6 |
|
foreach ($agentName as $name => $value) { |
345
|
6 |
|
self::scheme($name, $value); |
346
|
4 |
|
} |
347
|
6 |
|
} elseif ($agentName && is_string($agentName)) { |
348
|
3 |
|
self::$scheme["$agentName"] = is_array($scheme) ? $scheme : "$scheme"; |
349
|
6 |
|
} elseif (is_int($agentName) && $scheme && is_string($scheme)) { |
350
|
6 |
|
self::$scheme["$scheme"] = ''; |
351
|
4 |
|
} |
352
|
|
|
|
353
|
6 |
|
return self::$scheme; |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
/** |
357
|
|
|
* Set or get configuration information by agent name. |
358
|
|
|
* |
359
|
|
|
* @param mixed $agentName |
360
|
|
|
* @param mixed $config |
361
|
|
|
* |
362
|
|
|
* @throws PhpSmsException |
363
|
|
|
* |
364
|
|
|
* @return array |
365
|
|
|
*/ |
366
|
12 |
|
public static function config($agentName = null, $config = null) |
367
|
|
|
{ |
368
|
12 |
View Code Duplication |
if (($agentName === null || is_string($agentName)) && $config === null) { |
|
|
|
|
369
|
12 |
|
return $agentName === null ? self::$agentsConfig : |
370
|
12 |
|
(isset(self::$agentsConfig[$agentName]) ? self::$agentsConfig[$agentName] : []); |
371
|
|
|
} |
372
|
6 |
|
if (is_array($agentName)) { |
373
|
3 |
|
foreach ($agentName as $name => $value) { |
374
|
3 |
|
self::config($name, $value); |
375
|
2 |
|
} |
376
|
6 |
|
} elseif ($agentName && is_array($config)) { |
377
|
6 |
|
if (preg_match('/^[0-9]+$/', $agentName)) { |
378
|
|
|
throw new PhpSmsException("Agent name [$agentName] must be string, can not be a pure digital"); |
379
|
|
|
} |
380
|
6 |
|
self::$agentsConfig["$agentName"] = $config; |
381
|
4 |
|
} |
382
|
|
|
|
383
|
6 |
|
return self::$agentsConfig; |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
/** |
387
|
|
|
* Tear down agent use scheme and prepare to create and start a new balancing task, |
388
|
|
|
* so before do it must destroy old task instance. |
389
|
|
|
*/ |
390
|
6 |
|
public static function cleanScheme() |
391
|
|
|
{ |
392
|
6 |
|
Balancer::destroy(self::TASK); |
393
|
6 |
|
self::$scheme = []; |
394
|
6 |
|
} |
395
|
|
|
|
396
|
|
|
/** |
397
|
|
|
* Tear down agent config and prepare to create and start a new balancing task, |
398
|
|
|
* so before do it must destroy old task instance. |
399
|
|
|
*/ |
400
|
3 |
|
public static function cleanConfig() |
401
|
|
|
{ |
402
|
3 |
|
Balancer::destroy(self::TASK); |
403
|
3 |
|
self::$agentsConfig = []; |
404
|
3 |
|
} |
405
|
|
|
|
406
|
|
|
/** |
407
|
|
|
* Create a sms instance send SMS, |
408
|
|
|
* your can also set SMS templates or content at the same time. |
409
|
|
|
* |
410
|
|
|
* @param mixed $agentName |
411
|
|
|
* @param mixed $tempId |
412
|
|
|
* |
413
|
|
|
* @return Sms |
414
|
|
|
*/ |
415
|
|
|
public static function make($agentName = null, $tempId = null) |
416
|
|
|
{ |
417
|
|
|
$sms = new self(); |
418
|
|
|
if (is_array($agentName)) { |
419
|
|
|
$sms->template($agentName); |
420
|
|
|
} elseif ($agentName && is_string($agentName)) { |
421
|
|
|
if ($tempId === null) { |
422
|
|
|
$sms->content($agentName); |
423
|
|
|
} elseif (is_string("$tempId")) { |
424
|
|
|
$sms->template($agentName, $tempId); |
425
|
|
|
} |
426
|
|
|
} |
427
|
|
|
|
428
|
|
|
return $sms; |
429
|
|
|
} |
430
|
|
|
|
431
|
|
|
/** |
432
|
|
|
* Create a sms instance send voice verify, |
433
|
|
|
* your can also set verify code at the same time. |
434
|
|
|
* |
435
|
|
|
* @param string|int $code |
436
|
|
|
* |
437
|
|
|
* @return Sms |
438
|
|
|
*/ |
439
|
3 |
|
public static function voice($code) |
440
|
|
|
{ |
441
|
3 |
|
$sms = new self(); |
442
|
3 |
|
$sms->smsData['voiceCode'] = $code; |
443
|
|
|
|
444
|
3 |
|
return $sms; |
445
|
|
|
} |
446
|
|
|
|
447
|
|
|
/** |
448
|
|
|
* Set whether to use the queue system, and define how to use it. |
449
|
|
|
* |
450
|
|
|
* @param mixed $enable |
451
|
|
|
* @param mixed $handler |
452
|
|
|
* |
453
|
|
|
* @return bool |
454
|
|
|
*/ |
455
|
3 |
|
public static function queue($enable = null, $handler = null) |
456
|
|
|
{ |
457
|
3 |
|
if ($enable === null && $handler === null) { |
458
|
3 |
|
return self::$enableQueue; |
459
|
|
|
} |
460
|
3 |
|
if (is_callable($enable)) { |
461
|
3 |
|
$handler = $enable; |
462
|
3 |
|
$enable = true; |
463
|
2 |
|
} |
464
|
3 |
|
self::$enableQueue = (bool) $enable; |
465
|
3 |
|
if (is_callable($handler)) { |
466
|
3 |
|
self::$howToUseQueue = $handler; |
|
|
|
|
467
|
2 |
|
} |
468
|
|
|
|
469
|
3 |
|
return self::$enableQueue; |
470
|
|
|
} |
471
|
|
|
|
472
|
|
|
/** |
473
|
|
|
* Set the recipient`s mobile number. |
474
|
|
|
* |
475
|
|
|
* @param string $mobile |
476
|
|
|
* |
477
|
|
|
* @return $this |
478
|
|
|
*/ |
479
|
6 |
|
public function to($mobile) |
480
|
|
|
{ |
481
|
6 |
|
$this->smsData['to'] = $mobile; |
482
|
|
|
|
483
|
6 |
|
return $this; |
484
|
|
|
} |
485
|
|
|
|
486
|
|
|
/** |
487
|
|
|
* Set the content for content SMS. |
488
|
|
|
* |
489
|
|
|
* @param string $content |
490
|
|
|
* |
491
|
|
|
* @return $this |
492
|
|
|
*/ |
493
|
3 |
|
public function content($content) |
494
|
|
|
{ |
495
|
3 |
|
$this->smsData['content'] = trim((string) $content); |
496
|
|
|
|
497
|
3 |
|
return $this; |
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
/** |
501
|
|
|
* Set the template id for template SMS. |
502
|
|
|
* |
503
|
|
|
* @param mixed $agentName |
504
|
|
|
* @param mixed $tempId |
505
|
|
|
* |
506
|
|
|
* @return $this |
507
|
|
|
*/ |
508
|
3 |
|
public function template($agentName, $tempId = null) |
509
|
|
|
{ |
510
|
3 |
|
if (is_array($agentName)) { |
511
|
3 |
|
foreach ($agentName as $k => $v) { |
512
|
3 |
|
$this->template($k, $v); |
513
|
2 |
|
} |
514
|
3 |
|
} elseif ($agentName && $tempId) { |
515
|
3 |
|
if (!isset($this->smsData['templates']) || !is_array($this->smsData['templates'])) { |
516
|
|
|
$this->smsData['templates'] = []; |
517
|
|
|
} |
518
|
3 |
|
$this->smsData['templates']["$agentName"] = $tempId; |
519
|
2 |
|
} |
520
|
|
|
|
521
|
3 |
|
return $this; |
522
|
|
|
} |
523
|
|
|
|
524
|
|
|
/** |
525
|
|
|
* Set the template data for template SMS. |
526
|
|
|
* |
527
|
|
|
* @param array $data |
528
|
|
|
* |
529
|
|
|
* @return $this |
530
|
|
|
*/ |
531
|
3 |
|
public function data(array $data) |
532
|
|
|
{ |
533
|
3 |
|
$this->smsData['templateData'] = $data; |
534
|
|
|
|
535
|
3 |
|
return $this; |
536
|
|
|
} |
537
|
|
|
|
538
|
|
|
/** |
539
|
|
|
* Set the first agent by name. |
540
|
|
|
* |
541
|
|
|
* @param string $name |
542
|
|
|
* |
543
|
|
|
* @return $this |
544
|
|
|
*/ |
545
|
3 |
|
public function agent($name) |
546
|
|
|
{ |
547
|
3 |
|
$this->firstAgent = (string) $name; |
548
|
|
|
|
549
|
3 |
|
return $this; |
550
|
|
|
} |
551
|
|
|
|
552
|
|
|
/** |
553
|
|
|
* Start send SMS/voice verify. |
554
|
|
|
* |
555
|
|
|
* If give a true parameter, this system will immediately start request to send SMS/voice verify whatever whether to use the queue. |
556
|
|
|
* if you are already pushed sms instance to the queue, you can recall the method `send()` in queue system without `true` parameter, |
557
|
|
|
* so this mechanism in order to make you convenient use the method `send()` in queue system. |
558
|
|
|
* |
559
|
|
|
* @param bool $immediately |
560
|
|
|
* |
561
|
|
|
* @return mixed |
562
|
|
|
*/ |
563
|
18 |
|
public function send($immediately = false) |
564
|
|
|
{ |
565
|
18 |
|
if (!self::$enableQueue || $this->pushedToQueue) { |
566
|
18 |
|
$immediately = true; |
567
|
12 |
|
} |
568
|
18 |
|
if ($immediately) { |
569
|
18 |
|
$result = Balancer::run(self::TASK, [ |
570
|
18 |
|
'data' => $this->getData(), |
571
|
18 |
|
'driver' => $this->firstAgent, |
572
|
12 |
|
]); |
573
|
12 |
|
} else { |
574
|
3 |
|
$result = $this->push(); |
575
|
|
|
} |
576
|
|
|
|
577
|
18 |
|
return $result; |
578
|
|
|
} |
579
|
|
|
|
580
|
|
|
/** |
581
|
|
|
* Push to the queue by a custom method. |
582
|
|
|
* |
583
|
|
|
* @throws \Exception | PhpSmsException |
584
|
|
|
* |
585
|
|
|
* @return mixed |
586
|
|
|
*/ |
587
|
3 |
|
public function push() |
588
|
|
|
{ |
589
|
3 |
|
if (is_callable(self::$howToUseQueue)) { |
590
|
|
|
try { |
591
|
3 |
|
$this->pushedToQueue = true; |
592
|
|
|
|
593
|
3 |
|
return call_user_func_array(self::$howToUseQueue, [$this, $this->smsData]); |
594
|
|
|
} catch (\Exception $e) { |
595
|
|
|
$this->pushedToQueue = false; |
596
|
|
|
throw $e; |
597
|
|
|
} |
598
|
|
|
} else { |
599
|
|
|
throw new PhpSmsException('Please define how to use queue by method `queue($available, $handler)`'); |
600
|
|
|
} |
601
|
|
|
} |
602
|
|
|
|
603
|
|
|
/** |
604
|
|
|
* Get all the data of SMS/voice verify. |
605
|
|
|
* |
606
|
|
|
* @param null|string $name |
607
|
|
|
* |
608
|
|
|
* @return mixed |
609
|
|
|
*/ |
610
|
36 |
|
public function getData($name = null) |
611
|
|
|
{ |
612
|
36 |
|
if (is_string($name) && isset($this->smsData["$name"])) { |
613
|
3 |
|
return $this->smsData[$name]; |
614
|
|
|
} |
615
|
|
|
|
616
|
36 |
|
return $this->smsData; |
617
|
|
|
} |
618
|
|
|
|
619
|
|
|
/** |
620
|
|
|
* Overload static method. |
621
|
|
|
* |
622
|
|
|
* @param string $name |
623
|
|
|
* @param array $args |
624
|
|
|
* |
625
|
|
|
* @throws PhpSmsException |
626
|
|
|
*/ |
627
|
9 |
|
public static function __callStatic($name, $args) |
628
|
|
|
{ |
629
|
9 |
|
$name = $name === 'beforeSend' ? 'beforeRun' : $name; |
630
|
9 |
|
$name = $name === 'afterSend' ? 'afterRun' : $name; |
631
|
9 |
|
$name = $name === 'beforeAgentSend' ? 'beforeDriverRun' : $name; |
632
|
9 |
|
$name = $name === 'afterAgentSend' ? 'afterDriverRun' : $name; |
633
|
9 |
|
if (in_array($name, self::$availableHooks)) { |
634
|
9 |
|
$handler = $args[0]; |
635
|
9 |
|
$override = isset($args[1]) ? (bool) $args[1] : false; |
636
|
9 |
|
if (is_callable($handler)) { |
637
|
9 |
|
$task = self::getTask(); |
638
|
9 |
|
$task->hook($name, $handler, $override); |
639
|
6 |
|
} else { |
640
|
3 |
|
throw new PhpSmsException("Please give method $name() a callable parameter"); |
641
|
|
|
} |
642
|
6 |
|
} else { |
643
|
|
|
throw new PhpSmsException("Dont find method $name()"); |
644
|
|
|
} |
645
|
9 |
|
} |
646
|
|
|
|
647
|
|
|
/** |
648
|
|
|
* Overload method. |
649
|
|
|
* |
650
|
|
|
* @param string $name |
651
|
|
|
* @param array $args |
652
|
|
|
* |
653
|
|
|
* @throws PhpSmsException |
654
|
|
|
* @throws \Exception |
655
|
|
|
*/ |
656
|
3 |
|
public function __call($name, $args) |
657
|
|
|
{ |
658
|
|
|
try { |
659
|
3 |
|
$this->__callStatic($name, $args); |
660
|
2 |
|
} catch (\Exception $e) { |
661
|
|
|
throw $e; |
662
|
|
|
} |
663
|
3 |
|
} |
664
|
|
|
|
665
|
|
|
/** |
666
|
|
|
* Serialize magic method. |
667
|
|
|
* |
668
|
|
|
* @return array |
669
|
|
|
*/ |
670
|
3 |
|
public function __sleep() |
671
|
|
|
{ |
672
|
|
|
try { |
673
|
3 |
|
$this->_status_before_enqueue_['scheme'] = self::serializeOrDeserializeScheme(self::scheme()); |
674
|
3 |
|
$this->_status_before_enqueue_['agentsConfig'] = self::config(); |
675
|
3 |
|
$this->_status_before_enqueue_['handlers'] = self::serializeHandlers(); |
676
|
2 |
|
} catch (\Exception $e) { |
677
|
|
|
//swallow exception |
678
|
|
|
} |
679
|
|
|
|
680
|
3 |
|
return ['pushedToQueue', 'smsData', 'firstAgent', '_status_before_enqueue_']; |
681
|
|
|
} |
682
|
|
|
|
683
|
|
|
/** |
684
|
|
|
* Deserialize magic method. |
685
|
|
|
*/ |
686
|
3 |
|
public function __wakeup() |
687
|
|
|
{ |
688
|
3 |
|
if (empty($this->_status_before_enqueue_)) { |
689
|
|
|
return; |
690
|
|
|
} |
691
|
3 |
|
$status = $this->_status_before_enqueue_; |
692
|
3 |
|
self::$scheme = self::serializeOrDeserializeScheme($status['scheme']); |
693
|
3 |
|
self::$agentsConfig = $status['agentsConfig']; |
694
|
3 |
|
Balancer::destroy(self::TASK); |
695
|
3 |
|
self::bootstrap(); |
696
|
3 |
|
self::reinstallHandlers($status['handlers']); |
697
|
3 |
|
} |
698
|
|
|
|
699
|
|
|
/** |
700
|
|
|
* Get a closure serializer. |
701
|
|
|
* |
702
|
|
|
* @return Serializer |
703
|
|
|
*/ |
704
|
3 |
|
protected static function getSerializer() |
705
|
|
|
{ |
706
|
3 |
|
if (!self::$serializer) { |
707
|
3 |
|
self::$serializer = new Serializer(); |
708
|
2 |
|
} |
709
|
|
|
|
710
|
3 |
|
return self::$serializer; |
711
|
|
|
} |
712
|
|
|
|
713
|
|
|
/** |
714
|
|
|
* Serialize or deserialize the agent use scheme. |
715
|
|
|
* |
716
|
|
|
* @param array $scheme |
717
|
|
|
* |
718
|
|
|
* @return array |
719
|
|
|
*/ |
720
|
3 |
|
protected static function serializeOrDeserializeScheme(array $scheme) |
721
|
|
|
{ |
722
|
3 |
|
foreach ($scheme as $name => &$options) { |
723
|
3 |
|
if (is_array($options)) { |
724
|
3 |
|
self::serializeOrDeserializeClosureAndReplace($options, 'sendSms'); |
725
|
3 |
|
self::serializeOrDeserializeClosureAndReplace($options, 'voiceVerify'); |
726
|
2 |
|
} |
727
|
2 |
|
} |
728
|
|
|
|
729
|
3 |
|
return $scheme; |
730
|
|
|
} |
731
|
|
|
|
732
|
|
|
/** |
733
|
|
|
* Serialize the hooks` handlers of balancing task |
734
|
|
|
* |
735
|
|
|
* @return array |
736
|
|
|
*/ |
737
|
3 |
|
protected static function serializeHandlers() |
738
|
|
|
{ |
739
|
3 |
|
$task = self::getTask(); |
740
|
3 |
|
$hooks = $task->handlers; |
741
|
3 |
|
foreach ($hooks as &$handlers) { |
742
|
3 |
|
foreach (array_keys($handlers) as $key) { |
743
|
3 |
|
self::serializeOrDeserializeClosureAndReplace($handlers, $key); |
744
|
2 |
|
} |
745
|
2 |
|
} |
746
|
|
|
|
747
|
3 |
|
return $hooks; |
748
|
|
|
} |
749
|
|
|
|
750
|
|
|
/** |
751
|
|
|
* Reinstall hooks` handlers for balancing task. |
752
|
|
|
* |
753
|
|
|
* @param array $handlers |
754
|
|
|
*/ |
755
|
3 |
|
protected static function reinstallHandlers(array $handlers) |
756
|
|
|
{ |
757
|
3 |
|
$serializer = self::getSerializer(); |
758
|
3 |
|
foreach ($handlers as $hookName => $serializedHandlers) { |
759
|
3 |
|
foreach ($serializedHandlers as $index => $handler) { |
760
|
3 |
|
if (is_string($handler)) { |
761
|
3 |
|
$handler = $serializer->unserialize($handler); |
762
|
2 |
|
} |
763
|
3 |
|
self::$hookName($handler, $index === 0); |
764
|
2 |
|
} |
765
|
2 |
|
} |
766
|
3 |
|
} |
767
|
|
|
|
768
|
|
|
/** |
769
|
|
|
* Serialize/deserialize the specified closure and replace the origin value. |
770
|
|
|
* |
771
|
|
|
* @param array $options |
772
|
|
|
* @param int|string $key |
773
|
|
|
*/ |
774
|
3 |
|
protected static function serializeOrDeserializeClosureAndReplace(array &$options, $key) |
775
|
|
|
{ |
776
|
3 |
|
if (!isset($options[$key])) { |
777
|
3 |
|
return; |
778
|
|
|
} |
779
|
3 |
|
$serializer = self::getSerializer(); |
780
|
3 |
|
if (is_callable($options[$key])) { |
781
|
3 |
|
$options[$key] = (string) $serializer->serialize($options[$key]); |
782
|
3 |
|
} elseif (is_string($options[$key])) { |
783
|
3 |
|
$options[$key] = $serializer->unserialize($options[$key]); |
784
|
2 |
|
} |
785
|
3 |
|
} |
786
|
|
|
} |
787
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.