Completed
Push — master ( a1ebc5...13ca76 )
by lan tian
02:20
created

Sms::parseAgentArrayOptions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 10
ccs 0
cts 7
cp 0
rs 9.4286
cc 1
eloc 7
nc 1
nop 1
crap 2
1
<?php
2
3
namespace Toplan\PhpSms;
4
5
use Toplan\TaskBalance\Balancer;
6
7
/**
8
 * Class Sms
9
 */
10
class Sms
11
{
12
    /**
13
     * sms send task name
14
     */
15
    const TASK = 'PhpSms';
16
17
    /**
18
     * agents instance
19
     */
20
    protected static $agents;
21
22
    /**
23
     * agents`s name
24
     *
25
     * @var
26
     */
27
    protected static $agentsName = [];
28
29
    /**
30
     * agents`s config
31
     *
32
     * @var
33
     */
34
    protected static $agentsConfig = [];
35
36
    /**
37
     * whether to enable queue
38
     *
39
     * @var bool
40
     */
41
    protected static $enableQueue = false;
42
43
    /**
44
     * queue work
45
     *
46
     * @var null
47
     */
48
    protected static $howToUseQueue = null;
49
50
    /**
51
     * sms already pushed to queue
52
     *
53
     * @var bool
54
     */
55
    protected $pushedToQueue = false;
56
57
    /**
58
     * hook handlers
59
     *
60
     * @var array
61
     */
62
    protected static $enableHooks = [
63
        'beforeRun',
64
        'beforeDriverRun',
65
        'afterDriverRun',
66
        'afterRun',
67
    ];
68
69
    /**
70
     * sms data
71
     *
72
     * @var array
73
     */
74
    protected $smsData = [
75
        'to'           => null,
76
        'templates'    => [],
77
        'content'      => '',
78
        'templateData' => [],
79
        'voiceCode'    => null,
80
    ];
81
82
    /**
83
     * first agent for send sms/voice verify
84
     *
85
     * @var string
86
     */
87
    protected $firstAgent = null;
88
89
    /**
90
     * construct
91
     */
92 9
    public function __construct()
93
    {
94 9
        self::init();
95 9
    }
96
97
    /**
98
     * create sms instance and set templates
99
     *
100
     * @param null $agentName
101
     * @param null $tempId
102
     *
103
     * @return Sms
104
     */
105 3
    public static function make($agentName = null, $tempId = null)
106
    {
107 3
        $sms = new self();
108 3
        if (is_array($agentName)) {
109
            $sms->template($agentName);
110 3
        } elseif ($agentName && is_string($agentName)) {
111
            if ($tempId === null) {
112
                $sms->content($agentName);
113
            } elseif (is_string("$tempId")) {
114
                $sms->template($agentName, $tempId);
115
            }
116
        }
117
118 3
        return $sms;
119
    }
120
121
    /**
122
     * send voice verify
123
     *
124
     * @param $code
125
     *
126
     * @return Sms
127
     */
128 3
    public static function voice($code)
129
    {
130 3
        $sms = new self();
131 3
        $sms->smsData['voiceCode'] = $code;
132
133 3
        return $sms;
134
    }
135
136
    /**
137
     * set how to use queue.
138
     *
139
     * @param $enable
140
     * @param $handler
141
     *
142
     * @return bool
143
     */
144 3
    public static function queue($enable = null, $handler = null)
145
    {
146 3
        if ($enable === null && $handler === null) {
147 3
            return self::$enableQueue;
148
        }
149 3
        if (is_callable($enable)) {
150 3
            $handler = $enable;
151 3
            $enable = true;
152 2
        }
153 3
        self::$enableQueue = (bool) $enable;
154 3
        if (is_callable($handler)) {
155 3
            self::$howToUseQueue = $handler;
0 ignored issues
show
Documentation Bug introduced by
It seems like $handler of type callable is incompatible with the declared type null of property $howToUseQueue.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
156 2
        }
157
158 3
        return self::$enableQueue;
159
    }
160
161
    /**
162
     * set the mobile number
163
     *
164
     * @param $mobile
165
     *
166
     * @return $this
167
     */
168 6
    public function to($mobile)
169
    {
170 6
        $this->smsData['to'] = $mobile;
171
172 6
        return $this;
173
    }
174
175
    /**
176
     * set content for content sms
177
     *
178
     * @param $content
179
     *
180
     * @return $this
181
     */
182 3
    public function content($content)
183
    {
184 3
        $this->smsData['content'] = trim((String) $content);
185
186 3
        return $this;
187
    }
188
189
    /**
190
     * set template id for template sms
191
     *
192
     * @param $agentName
193
     * @param $tempId
194
     *
195
     * @return $this
196
     */
197 3
    public function template($agentName, $tempId = null)
198
    {
199 3
        if (is_array($agentName)) {
200 3
            foreach ($agentName as $k => $v) {
201 3
                $this->template($k, $v);
202 2
            }
203 3
        } elseif ($agentName && $tempId) {
204 3
            if (!isset($this->smsData['templates']) || !is_array($this->smsData['templates'])) {
205
                $this->smsData['templates'] = [];
206
            }
207 3
            $this->smsData['templates']["$agentName"] = $tempId;
208 2
        }
209
210 3
        return $this;
211
    }
212
213
    /**
214
     * set data for template sms
215
     *
216
     * @param array $data
217
     *
218
     * @return $this
219
     */
220 11
    public function data(array $data)
221
    {
222 3
        $this->smsData['templateData'] = $data;
223
224 3
        return $this;
225 10
    }
226
227
    /**
228
     * set the first agent
229
     *
230
     * @param $name
231
     *
232
     * @return $this
233
     */
234 3
    public function agent($name)
235
    {
236 3
        $this->firstAgent = (String) $name;
237
238 3
        return $this;
239
    }
240
241
    /**
242
     * start send
243
     *
244
     * @param bool $immediately
245
     *
246
     * @return mixed
247
     */
248 15
    public function send($immediately = false)
249
    {
250 15
        $this->validator();
251
252
        // if disable push to queue,
253
        // send the sms immediately.
254 15
        if (!self::$enableQueue) {
255 12
            $immediately = true;
256 8
        }
257
258
        // whatever 'PhpSms' whether to enable or disable push to queue,
259
        // if you are already pushed sms instance to queue,
260
        // you can recall the method `send()` in queue job without `true` parameter.
261
        //
262
        // So this mechanism in order to make you convenient use the method `send()` in queue system.
263 15
        if ($this->pushedToQueue) {
264 3
            $immediately = true;
265 2
        }
266
267
        // whether to send sms immediately,
268
        // or push it to queue.
269 15
        if ($immediately) {
270 15
            $result = Balancer::run(self::TASK, $this->getData(), $this->firstAgent);
0 ignored issues
show
Documentation introduced by
$this->getData() is of type array, but the function expects a string|null.

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:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
271 10
        } else {
272 3
            $result = $this->push();
273
        }
274
275 15
        return $result;
276
    }
277
278
    /**
279
     * push sms send task to queue
280
     *
281
     * @throws \Exception | PhpSmsException
282
     *
283
     * @return mixed
284
     */
285 3
    protected function push()
286
    {
287 3
        if (is_callable(self::$howToUseQueue)) {
288
            try {
289 3
                $this->pushedToQueue = true;
290
291 3
                return call_user_func_array(self::$howToUseQueue, [$this, $this->smsData]);
292
            } catch (\Exception $e) {
293
                $this->pushedToQueue = false;
294
                throw $e;
295
            }
296
        } else {
297
            throw new PhpSmsException('Please define how to use queue by method `queue($enable, $handler)`');
298
        }
299
    }
300
301
    /**
302
     * get sms data
303
     *
304
     * @return array
305
     */
306 33
    public function getData()
307
    {
308 33
        return $this->smsData;
309
    }
310
311
    /**
312
     * init
313
     */
314 9
    protected static function init()
315
    {
316 9
        self::configuration();
317 9
        $task = self::generatorTask();
318 9
        self::createDrivers($task);
319 9
    }
320
321
    /**
322
     * generator a sms send task
323
     *
324
     * @return object
325
     */
326 18
    public static function generatorTask()
327
    {
328 18
        if (!Balancer::hasTask(self::TASK)) {
329 3
            Balancer::task(self::TASK, null);
330 2
        }
331
332 18
        return Balancer::getTask(self::TASK);
333
    }
334
335
    /**
336
     * configuration
337
     */
338 9
    protected static function configuration()
339
    {
340 9
        $config = [];
341 9
        self::generatorAgentsName($config);
342 9
        self::generatorAgentsConfig($config);
343 9
        self::configValidator();
344 9
    }
345
346
    /**
347
     * generate enabled agents name
348
     *
349
     * @param array $config
350
     */
351 9
    protected static function generatorAgentsName(&$config)
352
    {
353 9
        if (empty(self::$agentsName)) {
354 3
            $config = $config ?: include __DIR__ . '/../config/phpsms.php';
355 3
            $enableAgents = isset($config['enable']) ? $config['enable'] : null;
356 3
            self::enable($enableAgents);
357 2
        }
358 9
    }
359
360
    /**
361
     * generator agents config
362
     *
363
     * @param array $config
364
     */
365 9
    protected static function generatorAgentsConfig(&$config)
366
    {
367 9
        $diff = array_diff_key(self::$agentsName, self::$agentsConfig);
368 9
        $diff = array_keys($diff);
369 9
        if (count($diff)) {
370 3
            $config = $config ?: include __DIR__ . '/../config/phpsms.php';
371 3
            $agentsConfig = isset($config['agents']) ? $config['agents'] : [];
372 3
            foreach ($diff as $name) {
373 3
                $agentConfig = isset($agentsConfig[$name]) ? $agentsConfig[$name] : [];
374 3
                self::agents($name, $agentConfig);
375 2
            }
376 2
        }
377 9
    }
378
379
    /**
380
     * config value validator
381
     *
382
     * @throws PhpSmsException
383
     */
384 9
    protected static function configValidator()
385
    {
386 9
        if (!count(self::$agentsName)) {
387
            throw new PhpSmsException('Please set at least one enable agent in config file(config/phpsms.php) or use method enable()');
388
        }
389 9
    }
390
391
    /**
392
     * create drivers for sms send task
393
     *
394
     * @param $task
395
     */
396 15
    protected static function createDrivers($task)
397
    {
398 9
        foreach (self::$agentsName as $name => $options) {
399
            //获取代理器配置
400 9
            $configData = self::getAgentConfigData($name);
401
            //解析代理器数组模式的调度配置
402 9
            if (is_array($options)) {
403
                $data = self::parseAgentArrayOptions($options);
404
                $configData = array_merge($configData, $data);
405
                $options = $data['driverOpts'];
406
            }
407
            //创建任务驱动器
408 9
            $task->driver("$name $options")->data($configData)
409 15
                 ->work(function ($driver) {
410 15
                     $configData = $driver->getDriverData();
411 15
                     $agent = self::getSmsAgent($driver->name, $configData);
412 15
                     $smsData = $driver->getTaskData();
413 15
                     extract($smsData);
414 15
                     if (isset($smsData['voiceCode']) && $smsData['voiceCode']) {
415
                         $agent->voiceVerify($to, $voiceCode);
416
                     } else {
417 15
                         $template = isset($templates[$driver->name]) ? $templates[$driver->name] : 0;
418 15
                         $agent->sendSms($template, $to, $templateData, $content);
419
                     }
420 15
                     $result = $agent->getResult();
421 15
                     if ($result['success']) {
422 15
                         $driver->success();
423 10
                     }
424 15
                     unset($result['success']);
425
426 15
                     return $result;
427 9
                 });
428 6
        }
429 9
    }
430
431
    /**
432
     * 解析可用代理器的数组模式的调度配置
433
     *
434
     * @param array $options
435
     *
436
     * @return array
437
     */
438
    protected static function parseAgentArrayOptions(array $options)
439
    {
440
        $agentClass = self::pullAgentOptionByName($options, 'agentClass');
441
        $sendSms = self::pullAgentOptionByName($options, 'sendSms');
442
        $voiceVerify = self::pullAgentOptionByName($options, 'voiceVerify');
443
        $backup = self::pullAgentOptionByName($options, 'backup');
444
        $driverOpts = implode(' ', array_values($options)) . " $backup";
445
446
        return compact('agentClass', 'sendSms', 'voiceVerify', 'driverOpts');
447
    }
448
449
    /**
450
     * 从调度配置中拉取指定数据
451
     *
452
     * @param $options
453
     * @param $name
454
     *
455
     * @return null|string
456
     */
457
    protected static function pullAgentOptionByName(&$options, $name)
458
    {
459
        $value = isset($options[$name]) ? $options[$name] : null;
460
        if ($name === 'backup') {
461
            $value = isset($options[$name]) ? ($options[$name] ? 'backup' : '') : '';
462
        }
463
        unset($options[$name]);
464
465
        return $value;
466
    }
467
468
    /**
469
     * get agent config data by name
470
     *
471
     * @param $name
472
     *
473
     * @return array
474
     */
475 9
    protected static function getAgentConfigData($name)
476
    {
477 9
        return isset(self::$agentsConfig[$name]) ?
478 9
               (Array) self::$agentsConfig[$name] : [];
479
    }
480
481
    /**
482
     * get a sms agent instance,
483
     * if null, will create a new agent instance
484
     *
485
     * @param       $name
486
     * @param array $configData
487
     *
488
     * @throws PhpSmsException
489
     *
490
     * @return mixed
491
     */
492 18
    public static function getSmsAgent($name, array $configData)
493
    {
494 18
        if (!isset(self::$agents[$name])) {
495 3
            $configData['name'] = $name;
496 3
            $className = isset($configData['agentClass']) ? $configData['agentClass'] : ('Toplan\\PhpSms\\' . $name . 'Agent');
497 3
            if ((isset($configData['sendSms']) && is_callable($configData['sendSms'])) ||
498 3
                (isset($configData['voiceVerify']) && is_callable($configData['voiceVerify']))) {
499
                //将临时代理器寄生到LogAgent
500
                self::$agents[$name] = new LogAgent($configData);
501 3
            } elseif (class_exists($className)) {
502
                //创建新代理器
503 3
                self::$agents[$name] = new $className($configData);
504 2
            } else {
505
                //无代理器可用
506
                throw new PhpSmsException("Agent [$name] not support. If you are want to use parasitic agent, please set callable arguments: [sendSms] and [voiceVerify]");
507
            }
508 2
        }
509
510 18
        return self::$agents[$name];
511
    }
512
513
    /**
514
     * validate
515
     *
516
     * @throws PhpSmsException
517
     */
518 18
    protected function validator()
519
    {
520 18
        if (!$this->smsData['to']) {
521
            throw new PhpSmsException('Please set send sms(or voice verify) to who use `to()` method.');
522
        }
523
524 18
        return true;
525
    }
526
527
    /**
528
     * set enable agents
529
     *
530
     * @param      $agentName
531
     * @param null $options
532
     */
533 6
    public static function enable($agentName, $options = null)
534
    {
535 6
        if (is_array($agentName)) {
536 6
            foreach ($agentName as $name => $opt) {
537 6
                self::enable($name, $opt);
538 4
            }
539 6
        } elseif ($agentName && is_string($agentName) && $options !== null) {
540 3
            self::$agentsName["$agentName"] = is_array($options) ? $options : "$options";
541 6
        } elseif (is_int($agentName) && !is_array($options) && "$options") {
542 3
            self::$agentsName["$options"] = '1';
543 5
        } elseif ($agentName && $options === null) {
544 3
            self::$agentsName["$agentName"] = '1';
545 2
        }
546 6
    }
547
548
    /**
549
     * set config for available agents
550
     *
551
     * @param       $agentName
552
     * @param array $config
553
     *
554
     * @throws PhpSmsException
555
     */
556 6
    public static function agents($agentName, array $config = [])
557
    {
558 6
        if (is_array($agentName)) {
559 3
            foreach ($agentName as $name => $conf) {
560 3
                self::agents($name, $conf);
561 2
            }
562 6
        } elseif ($agentName && is_array($config)) {
563 6
            if (preg_match('/^[0-9]+$/', $agentName)) {
564
                throw new PhpSmsException("Agent name [$agentName] must be string, could not be a pure digital");
565
            }
566 6
            self::$agentsConfig["$agentName"] = $config;
567 4
        }
568 6
    }
569
570
    /**
571
     * get enable agents
572
     *
573
     * @return array
574
     */
575 9
    public static function getEnableAgents()
576
    {
577 9
        return self::$agentsName;
578
    }
579
580
    /**
581
     * get agents config info
582
     *
583
     * @return array
584
     */
585 9
    public static function getAgentsConfig()
586
    {
587 9
        return self::$agentsConfig;
588
    }
589
590
    /**
591
     * tear down enable agents
592
     */
593 3
    public static function cleanEnableAgents()
594
    {
595 3
        self::$agentsName = [];
596 3
    }
597
598
    /**
599
     * tear down agents config
600
     */
601 3
    public static function cleanAgentsConfig()
602
    {
603 3
        self::$agentsConfig = [];
604 3
    }
605
606
    /**
607
     * overload static method
608
     *
609
     * @param $name
610
     * @param $args
611
     *
612
     * @throws PhpSmsException
613
     */
614 6
    public static function __callStatic($name, $args)
615
    {
616 6
        $name = $name === 'beforeSend' ? 'beforeRun' : $name;
617 6
        $name = $name === 'afterSend' ? 'afterRun' : $name;
618 6
        $name = $name === 'beforeAgentSend' ? 'beforeDriverRun' : $name;
619 6
        $name = $name === 'afterAgentSend' ? 'afterDriverRun' : $name;
620 6
        if (in_array($name, self::$enableHooks)) {
621 6
            $handler = $args[0];
622 6
            $override = isset($args[1]) ? (bool) $args[1] : false;
623 6
            if (is_callable($handler)) {
624 6
                $task = self::generatorTask();
625 6
                $task->hook($name, $handler, $override);
626 4
            } else {
627 2
                throw new PhpSmsException("Please give method static $name() a callable parameter");
628
            }
629 4
        } else {
630
            throw new PhpSmsException("Do not find static method $name()");
631
        }
632 6
    }
633
634
    /**
635
     * overload method
636
     *
637
     * @param $name
638
     * @param $args
639
     *
640
     * @throws PhpSmsException
641
     * @throws \Exception
642
     */
643 3
    public function __call($name, $args)
644
    {
645
        try {
646 3
            $this->__callStatic($name, $args);
647 2
        } catch (\Exception $e) {
648
            throw $e;
649
        }
650 3
    }
651
}
652