Completed
Pull Request — master (#15)
by lan tian
05:15
created

Sms::pullAgentOptionByName()   B

Complexity

Conditions 5
Paths 10

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 5

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 10
ccs 5
cts 5
cp 1
rs 8.8571
cc 5
eloc 6
nc 10
nop 2
crap 5
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
    public function __construct()
93
    {
94
        self::init();
95 9
    }
96
97 9
    /**
98 9
     * create sms instance and set templates
99
     *
100
     * @param null $agentName
101
     * @param null $tempId
102
     *
103
     * @return Sms
104
     */
105
    public static function make($agentName = null, $tempId = null)
106
    {
107
        $sms = new self();
108 3
        if (is_array($agentName)) {
109
            $sms->template($agentName);
110 3
        } elseif ($agentName && is_string($agentName)) {
111 3
            if ($tempId === null) {
112
                $sms->content($agentName);
113 3
            } elseif (is_string("$tempId")) {
114
                $sms->template($agentName, $tempId);
115
            }
116
        }
117
118
        return $sms;
119
    }
120
121 3
    /**
122
     * send voice verify
123
     *
124
     * @param $code
125
     *
126
     * @return Sms
127
     */
128
    public static function voice($code)
129
    {
130
        $sms = new self();
131 3
        $sms->smsData['voiceCode'] = $code;
132
133 3
        return $sms;
134 3
    }
135
136 3
    /**
137
     * set how to use queue.
138
     *
139
     * @param $enable
140
     * @param $handler
141
     *
142
     * @return bool
143
     */
144
    public static function queue($enable = null, $handler = null)
145
    {
146
        if ($enable === null && $handler === null) {
147 3
            return self::$enableQueue;
148
        }
149 3
        if (is_callable($enable)) {
150 3
            $handler = $enable;
151
            $enable = true;
152 3
        }
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 3
        }
157 3
158 3
        return self::$enableQueue;
159 3
    }
160
161 3
    /**
162
     * set the mobile number
163
     *
164
     * @param $mobile
165
     *
166
     * @return $this
167
     */
168
    public function to($mobile)
169
    {
170
        $this->smsData['to'] = $mobile;
171 6
172
        return $this;
173 6
    }
174
175 6
    /**
176
     * set content for content sms
177
     *
178
     * @param $content
179
     *
180
     * @return $this
181
     */
182
    public function content($content)
183
    {
184
        $this->smsData['content'] = trim((String) $content);
185 3
186
        return $this;
187 3
    }
188
189 3
    /**
190
     * set template id for template sms
191
     *
192
     * @param $agentName
193
     * @param $tempId
194
     *
195
     * @return $this
196
     */
197
    public function template($agentName, $tempId = null)
198
    {
199
        if (is_array($agentName)) {
200 3
            foreach ($agentName as $k => $v) {
201
                $this->template($k, $v);
202 3
            }
203 3
        } elseif ($agentName && $tempId) {
204 3
            if (!isset($this->smsData['templates']) || !is_array($this->smsData['templates'])) {
205 3
                $this->smsData['templates'] = [];
206 3
            }
207 3
            $this->smsData['templates']["$agentName"] = $tempId;
208
        }
209
210 3
        return $this;
211 3
    }
212
213 3
    /**
214
     * set data for template sms
215
     *
216
     * @param array $data
217
     *
218
     * @return $this
219
     */
220
    public function data(array $data)
221
    {
222
        $this->smsData['templateData'] = $data;
223 18
224
        return $this;
225 18
    }
226
227 3
    /**
228
     * set the first agent
229
     *
230
     * @param $name
231
     *
232
     * @return $this
233
     */
234
    public function agent($name)
235
    {
236
        $this->firstAgent = (String) $name;
237 3
238
        return $this;
239 3
    }
240
241 3
    /**
242
     * start send
243
     *
244
     * @param bool $immediately
245
     *
246
     * @return mixed
247
     */
248
    public function send($immediately = false)
249
    {
250
        $this->validator();
251 15
252
        // if disable push to queue,
253 15
        // send the sms immediately.
254
        if (!self::$enableQueue) {
255
            $immediately = true;
256
        }
257 15
258 12
        // whatever 'PhpSms' whether to enable or disable push to queue,
259 12
        // 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
        if ($this->pushedToQueue) {
264
            $immediately = true;
265
        }
266 15
267 3
        // whether to send sms immediately,
268 3
        // or push it to queue.
269
        if ($immediately) {
270
            $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
        } else {
272 15
            $result = $this->push();
273 15
        }
274 15
275 3
        return $result;
276
    }
277
278 15
    /**
279
     * push sms send task to queue
280
     *
281
     * @throws \Exception | PhpSmsException
282
     *
283
     * @return mixed
284
     */
285
    protected function push()
286
    {
287
        if (is_callable(self::$howToUseQueue)) {
288 3
            try {
289
                $this->pushedToQueue = true;
290 3
291
                return call_user_func_array(self::$howToUseQueue, [$this, $this->smsData]);
292 3
            } catch (\Exception $e) {
293
                $this->pushedToQueue = false;
294 3
                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
    public function getData()
307
    {
308
        return $this->smsData;
309 33
    }
310
311 33
    /**
312
     * init
313
     */
314
    protected static function init()
315
    {
316
        self::configuration();
317
        $task = self::generatorTask();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $task is correct as self::generatorTask() (which targets Toplan\PhpSms\Sms::generatorTask()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
318
        self::createDrivers($task);
319 15
    }
320
321 15
    /**
322
     * generator a sms send task
323 15
     *
324
     * @return null
325
     */
326
    public static function generatorTask()
327
    {
328
        if (!Balancer::hasTask(self::TASK)) {
329
            Balancer::task(self::TASK, null);
330
        }
331 18
332
        return Balancer::getTask(self::TASK);
333 18
    }
334
335
    /**
336 3
     * configuration
337 3
     */
338 3
    protected static function configuration()
339
    {
340 18
        $config = [];
341
        self::generatorAgentsName($config);
342
        self::generatorAgentsConfig($config);
343
        self::configValidator();
344
    }
345
346 15
    /**
347
     * generate enabled agents name
348 15
     *
349 15
     * @param array $config
350 3
     */
351 3
    protected static function generatorAgentsName(&$config)
352 3
    {
353 15
        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 15
        }
358 15
    }
359
360
    /**
361
     * generator agents config
362
     *
363
     * @param array $config
364
     */
365 3
    protected static function generatorAgentsConfig(&$config)
366
    {
367 3
        $diff = array_diff_key(self::$agentsName, self::$agentsConfig);
368 3
        $diff = array_keys($diff);
369 3
        if (count($diff)) {
370 3
            $config = $config ?: include __DIR__ . '/../config/phpsms.php';
371 3
            $agentsConfig = isset($config['agents']) ? $config['agents'] : [];
372
            foreach ($diff as $name) {
373
                $agentConfig = isset($agentsConfig[$name]) ? $agentsConfig[$name] : [];
374
                self::agents($name, $agentConfig);
375
            }
376
        }
377
    }
378 3
379
    /**
380 3
     * config value validator
381 3
     *
382 3
     * @throws PhpSmsException
383
     */
384
    protected static function configValidator()
385
    {
386
        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 15
    }
390
391 15
    /**
392
     * create drivers for sms send task
393
     *
394 15
     * @param $task
395 15
     */
396 15
    protected static function createDrivers($task)
397
    {
398 9
        foreach (self::$agentsName as $name => $options) {
399
            //获取代理器配置
400
            $configData = self::getAgentConfigData($name);
401 15
            //解析代理器数组模式的调度配置
402 15
            if (is_array($options)) {
403
                $data = self::parseAgentArrayOptions($options);
404
                $configData = array_merge($configData, $data);
405
                $options = $data['driverOpts'];
406
            }
407
            //创建任务驱动器
408
            $task->driver("$name $options")->data($configData)
409 15
                 ->work(function ($driver) {
410
                     $configData = $driver->getDriverData();
411 3
                     $agent = self::getSmsAgent($driver->name, $configData);
412 3
                     $smsData = $driver->getTaskData();
413 3
                     extract($smsData);
414 3
                     if (isset($smsData['voiceCode']) && $smsData['voiceCode']) {
415 15
                         $agent->voiceVerify($to, $voiceCode);
416 15
                     } else {
417 15
                         $template = isset($templates[$driver->name]) ? $templates[$driver->name] : 0;
418 15
                         $agent->sendSms($template, $to, $templateData, $content);
419 15
                     }
420 15
                     $result = $agent->getResult();
421
                     if ($result['success']) {
422
                         $driver->success();
423 15
                     }
424 15
                     unset($result['success']);
425
426 15
                     return $result;
427 15
                 });
428 15
        }
429 15
    }
430 15
431
    /**
432 15
     * 解析可用代理器的数组模式的调度配置
433 3
     *
434 3
     * @param array $options
435 3
     *
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 3
        $driverOpts = implode(' ', array_values($options)) . " $backup";
445
446 3
        return compact('agentClass', 'sendSms', 'voiceVerify', 'driverOpts');
447 3
    }
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 18
            $value = isset($options[$name]) ? ($options[$name] ? 'backup' : '') : '';
462
        }
463 18
        unset($options[$name]);
464 3
465 3
        return $value;
466 3
    }
467 3
468
    /**
469
     * get agent config data by name
470 3
     *
471
     * @param $name
472 18
     *
473
     * @return array
474
     */
475
    protected static function getAgentConfigData($name)
476
    {
477
        return isset(self::$agentsConfig[$name]) ?
478
               (Array) self::$agentsConfig[$name] : [];
479
    }
480 18
481
    /**
482 18
     * get a sms agent instance,
483
     * if null, will create a new agent instance
484
     *
485
     * @param       $name
486 18
     * @param array $configData
487
     *
488
     * @throws PhpSmsException
489
     *
490
     * @return mixed
491
     */
492
    public static function getSmsAgent($name, array $configData)
493
    {
494
        if (!isset(self::$agents[$name])) {
495 6
            $configData['name'] = $name;
496
            $className = isset($configData['agentClass']) ? $configData['agentClass'] : ('Toplan\\PhpSms\\' . $name . 'Agent');
497 6
            if ((isset($configData['sendSms']) && is_callable($configData['sendSms'])) ||
498 6
                (isset($configData['voiceVerify']) && is_callable($configData['voiceVerify']))) {
499 6
                //将临时代理器寄生到LogAgent
500 6
                self::$agents[$name] = new LogAgent($configData);
501 6
            } elseif (class_exists($className)) {
502 3
                //创建新代理器
503 6
                self::$agents[$name] = new $className($configData);
504 3
            } else {
505 3
                //无代理器可用
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 6
        }
509
510
        return self::$agents[$name];
511
    }
512
513
    /**
514
     * validate
515
     *
516
     * @throws PhpSmsException
517
     */
518 6
    protected function validator()
519
    {
520 6
        if (!$this->smsData['to']) {
521 6
            throw new PhpSmsException('Please set send sms(or voice verify) to who use `to()` method.');
522 6
        }
523 6
524 6
        return true;
525 6
    }
526
527
    /**
528 6
     * set enable agents
529 6
     *
530 6
     * @param      $agentName
531
     * @param null $options
532
     */
533
    public static function enable($agentName, $options = null)
534
    {
535
        if (is_array($agentName)) {
536
            foreach ($agentName as $name => $opt) {
537 9
                self::enable($name, $opt);
538
            }
539 9
        } elseif ($agentName && is_string($agentName) && $options !== null) {
540
            self::$agentsName["$agentName"] = is_array($options) ? $options : "$options";
541
        } elseif (is_int($agentName) && !is_array($options) && "$options") {
542
            self::$agentsName["$options"] = '1';
543
        } elseif ($agentName && $options === null) {
544
            self::$agentsName["$agentName"] = '1';
545
        }
546
    }
547 9
548
    /**
549 9
     * set config for available agents
550
     *
551
     * @param       $agentName
552
     * @param array $config
553
     *
554
     * @throws PhpSmsException
555 3
     */
556
    public static function agents($agentName, array $config = [])
557 3
    {
558 3
        if (is_array($agentName)) {
559
            foreach ($agentName as $name => $conf) {
560
                self::agents($name, $conf);
561
            }
562
        } elseif ($agentName && is_array($config)) {
563 3
            if (preg_match('/^[0-9]+$/', $agentName)) {
564
                throw new PhpSmsException("Agent name [$agentName] must be string, could not be a pure digital");
565 3
            }
566 3
            self::$agentsConfig["$agentName"] = $config;
567
        }
568
    }
569
570
    /**
571
     * get enable agents
572
     *
573
     * @return array
574
     */
575
    public static function getEnableAgents()
576 6
    {
577
        return self::$agentsName;
578 6
    }
579 6
580 6
    /**
581 6
     * get agents config info
582 6
     *
583 6
     * @return array
584 6
     */
585 6
    public static function getAgentsConfig()
586 6
    {
587
        return self::$agentsConfig;
588
    }
589 6
590
    /**
591
     * tear down enable agents
592 6
     */
593
    public static function cleanEnableAgents()
594
    {
595
        self::$agentsName = [];
596
    }
597
598
    /**
599
     * tear down agents config
600
     */
601
    public static function cleanAgentsConfig()
602
    {
603 3
        self::$agentsConfig = [];
604
    }
605
606 3
    /**
607 3
     * overload static method
608
     *
609
     * @param $name
610 3
     * @param $args
611
     *
612
     * @throws PhpSmsException
613
     */
614
    public static function __callStatic($name, $args)
615
    {
616
        $name = $name === 'beforeSend' ? 'beforeRun' : $name;
617
        $name = $name === 'afterSend' ? 'afterRun' : $name;
618
        $name = $name === 'beforeAgentSend' ? 'beforeDriverRun' : $name;
619
        $name = $name === 'afterAgentSend' ? 'afterDriverRun' : $name;
620
        if (in_array($name, self::$enableHooks)) {
621
            $handler = $args[0];
622
            $override = isset($args[1]) ? (bool) $args[1] : false;
623
            if (is_callable($handler)) {
624
                $task = self::generatorTask();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $task is correct as self::generatorTask() (which targets Toplan\PhpSms\Sms::generatorTask()) seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
625
                $task->hook($name, $handler, $override);
0 ignored issues
show
Bug introduced by
The method hook cannot be called on $task (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
626
            } else {
627
                throw new PhpSmsException("Please give method static $name() a callable parameter");
628
            }
629
        } else {
630
            throw new PhpSmsException("Do not find static method $name()");
631
        }
632
    }
633
634
    /**
635
     * overload method
636
     *
637
     * @param $name
638
     * @param $args
639
     *
640
     * @throws PhpSmsException
641
     * @throws \Exception
642
     */
643
    public function __call($name, $args)
644
    {
645
        try {
646
            $this->__callStatic($name, $args);
647
        } catch (\Exception $e) {
648
            throw $e;
649
        }
650
    }
651
}
652