Completed
Push — master ( 4039b5...9dc6e4 )
by lan tian
03:47
created

Sms::queue()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 5

Importance

Changes 7
Bugs 2 Features 2
Metric Value
c 7
b 2
f 2
dl 0
loc 15
ccs 12
cts 12
cp 1
rs 8.8571
cc 5
eloc 10
nc 5
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
     * log agent`s name
19
     */
20
    const LOG_AGENT = 'Log';
21
22
    /**
23
     * agents instance
24
     */
25
    protected static $agents;
26
27
    /**
28
     * agents`s name
29
     *
30
     * @var
31
     */
32
    protected static $agentsName = [];
33
34
    /**
35
     * agents`s config
36
     *
37
     * @var
38
     */
39
    protected static $agentsConfig = [];
40
41
    /**
42
     * whether to enable queue
43
     *
44
     * @var bool
45
     */
46
    protected static $enableQueue = false;
47
48
    /**
49
     * queue work
50
     *
51
     * @var null
52
     */
53
    protected static $howToUseQueue = null;
54
55
    /**
56
     * sms already pushed to queue
57
     *
58
     * @var bool
59
     */
60
    protected $pushedToQueue = false;
61
62
    /**
63
     * hook handlers
64
     *
65
     * @var array
66
     */
67
    protected static $enableHooks = [
68
        'beforeRun',
69
        'afterRun',
70
    ];
71
72
    /**
73
     * sms data
74
     *
75
     * @var array
76
     */
77
    protected $smsData = [
78
        'to'           => null,
79
        'templates'    => [],
80
        'content'      => '',
81
        'templateData' => [],
82
        'voiceCode'    => null,
83
    ];
84
85
    /**
86
     * first agent for send sms/voice verify
87
     *
88
     * @var string
89
     */
90
    protected $firstAgent = null;
91
92
    /**
93
     * construct
94
     */
95 3
    public function __construct()
96
    {
97 3
        self::init();
98 3
    }
99
100
    /**
101
     * create sms instance and set templates
102
     *
103
     * @param null $agentName
104
     * @param null $tempId
105
     *
106
     * @return Sms
107
     */
108
    public static function make($agentName = null, $tempId = null)
109
    {
110
        $sms = new self();
111
        if (is_array($agentName)) {
112
            $sms->template($agentName);
113
        } elseif ($agentName && is_string($agentName)) {
114
            if ($tempId === null) {
115
                $sms->content($agentName);
116
            } elseif (is_string("$tempId")) {
117
                $sms->template($agentName, $tempId);
118
            }
119
        }
120
121
        return $sms;
122
    }
123
124
    /**
125
     * send voice verify
126
     *
127
     * @param $code
128
     *
129
     * @return Sms
130
     */
131 3
    public static function voice($code)
132
    {
133 3
        $sms = new self();
134 3
        $sms->smsData['voiceCode'] = $code;
135
136 3
        return $sms;
137
    }
138
139
    /**
140
     * set how to use queue.
141
     *
142
     * @param $enable
143
     * @param $handler
144
     *
145
     * @return boolean
146
     */
147 3
    public static function queue($enable = null, $handler = null)
148
    {
149 3
        if ($enable === null && $handler === null) {
150 3
            return self::$enableQueue;
151
        }
152 3
        if (is_callable($enable)) {
153 3
            $handler = $enable;
154 3
            $enable = true;
155 3
        }
156 3
        self::$enableQueue = (bool) $enable;
157 3
        if (is_callable($handler)) {
158 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...
159 3
        }
160 3
        return self::$enableQueue;
161
    }
162
163
    /**
164
     * set the mobile number
165
     *
166
     * @param $mobile
167
     *
168
     * @return $this
169
     */
170 3
    public function to($mobile)
171
    {
172 3
        $this->smsData['to'] = $mobile;
173
174 3
        return $this;
175
    }
176
177
    /**
178
     * set content for content sms
179
     *
180
     * @param $content
181
     *
182
     * @return $this
183
     */
184 3
    public function content($content)
185
    {
186 3
        $this->smsData['content'] = trim((String) $content);
187
188 3
        return $this;
189
    }
190
191
    /**
192
     * set template id for template sms
193
     *
194
     * @param $agentName
195
     * @param $tempId
196
     *
197
     * @return $this
198
     */
199 3
    public function template($agentName, $tempId = null)
200
    {
201 3
        if (is_array($agentName)) {
202 3
            foreach ($agentName as $k => $v) {
203 3
                $this->template($k, $v);
204 3
            }
205 3
        } elseif ($agentName && $tempId) {
206 3
            if (!isset($this->smsData['templates']) || !is_array($this->smsData['templates'])) {
207
                $this->smsData['templates'] = [];
208
            }
209 3
            $this->smsData['templates']["$agentName"] = $tempId;
210 3
        }
211
212 3
        return $this;
213
    }
214
215
    /**
216
     * set data for template sms
217
     *
218
     * @param array $data
219
     *
220
     * @return $this
221
     */
222 3
    public function data(array $data)
223
    {
224 3
        $this->smsData['templateData'] = $data;
225
226 3
        return $this;
227
    }
228
229
    /**
230
     * set the first agent
231
     *
232
     * @param $name
233
     *
234
     * @return $this
235
     */
236 3
    public function agent($name)
237
    {
238 3
        $this->firstAgent = (String) $name;
239
240 3
        return $this;
241
    }
242
243
    /**
244
     * start send
245
     *
246
     * @param bool $immediately
247
     *
248
     * @return mixed
249
     */
250 9
    public function send($immediately = false)
251
    {
252 9
        $this->validator();
253 9
        $results = null;
0 ignored issues
show
Unused Code introduced by
$results is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
254
255
        // if disable push to queue,
256
        // send the sms immediately.
257 9
        if (!self::$enableQueue) {
258 6
            $immediately = true;
259 6
        }
260
261
        // whatever 'PhpSms' whether to enable or disable push to queue,
262
        // if you are already pushed sms instance to queue,
263
        // you can recall the method `send()` in queue job without `true` parameter.
264
        //
265
        // So this mechanism in order to make you convenient use the method `send()` in queue system.
266 9
        if ($this->pushedToQueue) {
267 3
            $immediately = true;
268 3
        }
269
270
        // whether to send sms immediately,
271
        // or push it to queue.
272 9
        if ($immediately) {
273 9
            $results = 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...
274 9
        } else {
275 3
            $results = $this->push();
276
        }
277
278 9
        return $results;
279
    }
280
281
    /**
282
     * push sms send task to queue
283
     *
284
     * @throws \Exception | PhpSmsException
285
     *
286
     * @return mixed
287
     */
288 3
    protected function push()
289
    {
290 3
        if (is_callable(self::$howToUseQueue)) {
291
            try {
292 3
                $this->pushedToQueue = true;
293
294 3
                return call_user_func_array(self::$howToUseQueue, [$this, $this->smsData]);
295
            } catch (\Exception $e) {
296
                $this->pushedToQueue = false;
297
                throw $e;
298
            }
299
        } else {
300
            throw new PhpSmsException('Please define how to use queue by method `queue($enable, $handler)`');
301
        }
302
    }
303
304
    /**
305
     * get sms data
306
     *
307
     * @return array
308
     */
309 27
    public function getData()
310
    {
311 27
        return $this->smsData;
312
    }
313
314
    /**
315
     * init
316
     *
317
     * @return $task
0 ignored issues
show
Documentation introduced by
The doc-type $task could not be parsed: Unknown type name "$task" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
318
     */
319 3
    protected static function init()
320
    {
321 3
        self::configuration();
322
323 3
        return self::generatorTask();
324
    }
325
326
    /**
327
     * generator a sms send task
328
     *
329
     * @return null
330
     */
331 6
    public static function generatorTask()
332
    {
333 6
        if (!Balancer::getTask(self::TASK)) {
334
            Balancer::task(self::TASK, function ($task) {
335
                // create drivers
336
                self::createAgents($task);
337
            });
338
        }
339
340 6
        return Balancer::getTask(self::TASK);
341
    }
342
343
    /**
344
     * configuration
345
     */
346 3
    protected static function configuration()
347
    {
348 3
        $config = [];
349 3
        if (!self::$agentsName) {
0 ignored issues
show
Bug Best Practice introduced by
The expression self::$agentsName of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
350
            $config = include __DIR__ . '/../config/phpsms.php';
351
            self::generatorAgentsName($config);
352
        }
353 3
        if (!self::$agentsConfig) {
0 ignored issues
show
Bug Best Practice introduced by
The expression self::$agentsConfig of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
354
            $config = $config ?: include __DIR__ . '/../config/phpsms.php';
355
            self::generatorAgentsConfig($config);
356
        }
357 3
        self::configValidator();
358 3
    }
359
360
    /**
361
     * generate enabled agents name
362
     *
363
     * @param array $config
364
     */
365
    protected static function generatorAgentsName($config)
366
    {
367
        $config = isset($config['enable']) ? $config['enable'] : null;
368
        if ($config) {
369
            self::enable($config);
370
        }
371
    }
372
373
    /**
374
     * generator agents config
375
     *
376
     * @param array $config
377
     */
378
    protected static function generatorAgentsConfig($config)
379
    {
380
        $config = isset($config['agents']) ? $config['agents'] : [];
381
        self::agents($config);
382
    }
383
384
    /**
385
     * config value validator
386
     *
387
     * @throws PhpSmsException
388
     */
389 3
    protected static function configValidator()
390
    {
391 3
        if (!count(self::$agentsName)) {
392
            throw new PhpSmsException('Please set at least one enable agent in config file(config/phpsms.php) or use method enable()');
393
        }
394 3
        foreach (self::$agentsName as $agentName => $options) {
395 3
            if ($agentName === self::LOG_AGENT) {
396 3
                continue;
397
            }
398 3
            if (!isset(self::$agentsConfig[$agentName])) {
399
                throw new PhpSmsException("Please configuration [$agentName] agent in config file(config/phpsms.php) or use method agents()");
400
            }
401 3
        }
402 3
    }
403
404
    /**
405
     * create drivers for sms send task
406
     *
407
     * @param $task
408
     */
409 9
    protected static function createAgents($task)
410
    {
411
        foreach (self::$agentsName as $name => $options) {
412
            $configData = self::getAgentConfigData($name);
413
            $task->driver("$name $options")
414
                 ->data($configData)
415 9
                 ->work(function ($driver, $data) {
0 ignored issues
show
Unused Code introduced by
The parameter $data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
416 9
                     $configData = $driver->getDriverData();
417 9
                     $agent = self::getSmsAgent($driver->name, $configData);
418 9
                     $smsData = $driver->getTaskData();
419 9
                     extract($smsData);
420 9
                     if (isset($smsData['voiceCode']) && $smsData['voiceCode']) {
421
                         $agent->voiceVerify($to, $voiceCode);
422
                     } else {
423 9
                         $template = isset($templates[$driver->name]) ? $templates[$driver->name] : 0;
424 9
                         $agent->sendSms($template, $to, $templateData, $content);
425
                     }
426 9
                     $result = $agent->getResult();
427 9
                     if ($result['success']) {
428 9
                         $driver->success();
429 9
                     }
430 9
                     unset($result['success']);
431
432 9
                     return $result;
433
                 });
434
        }
435
    }
436
437
    /**
438
     * get agent config data by name
439
     *
440
     * @param $name
441
     *
442
     * @return array
443
     */
444
    protected static function getAgentConfigData($name)
445
    {
446
        return isset(self::$agentsConfig[$name]) ?
447
               (Array) self::$agentsConfig[$name] : [];
448
    }
449
450
    /**
451
     * get a sms agent instance,
452
     * if null, will create a new agent instance
453
     *
454
     * @param       $name
455
     * @param array $configData
456
     *
457
     * @throws PhpSmsException
458
     *
459
     * @return mixed
460
     */
461 9
    protected static function getSmsAgent($name, array $configData)
462
    {
463 9
        if (!isset(self::$agents[$name])) {
464 3
            $className = 'Toplan\\PhpSms\\' . $name . 'Agent';
465 3
            if (class_exists($className)) {
466 3
                self::$agents[$name] = new $className($configData);
467 3
            } else {
468
                throw new PhpSmsException("Agent [$name] not support.");
469
            }
470 3
        }
471
472 9
        return self::$agents[$name];
473
    }
474
475
    /**
476
     * validate
477
     *
478
     * @throws PhpSmsException
479
     */
480 9
    protected function validator()
481
    {
482 9
        if (!$this->smsData['to']) {
483
            throw new PhpSmsException('Please set send sms(or voice verify) to who use `to()` method.');
484
        }
485
486 9
        return true;
487
    }
488
489
    /**
490
     * set enable agents
491
     *
492
     * @param      $agentName
493
     * @param null $options
494
     */
495 3
    public static function enable($agentName, $options = null)
496
    {
497 3
        if (is_array($agentName)) {
498
            //([
499
            //  'name1' => 'opt',
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
500
            //  'name2',
501
            //  ......
502
            //])
503 3
            foreach ($agentName as $name => $opt) {
504 3
                self::enable($name, $opt);
505 3
            }
506 3
        } elseif ($agentName && is_string($agentName) && !is_array($options) && is_string("$options")) {
507
            //(name, opts)
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
508 3
            self::$agentsName["$agentName"] = "$options";
509 3
        } elseif (is_int($agentName) && !is_array($options) && "$options") {
510
            //(0, name)
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
511
            //(1, name)
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
512
            self::$agentsName["$options"] = '1';
513
        } elseif ($agentName && $options === null) {
514
            //(name)
515
            self::$agentsName["$agentName"] = '1';
516
        }
517 3
    }
518
519
    /**
520
     * set config for available agents
521
     *
522
     * @param       $agentName
523
     * @param array $config
524
     *
525
     * @throws PhpSmsException
526
     */
527 3
    public static function agents($agentName, array $config = [])
528
    {
529 3
        if (is_array($agentName)) {
530 3
            foreach ($agentName as $name => $conf) {
531 3
                self::agents($name, $conf);
532 3
            }
533 3
        } elseif ($agentName && is_array($config)) {
534 3
            if (preg_match('/^[0-9]+$/', $agentName)) {
535
                throw new PhpSmsException("Agent name [$agentName] must be string, could not be a pure digital");
536
            }
537 3
            self::$agentsConfig["$agentName"] = $config;
538 3
        }
539 3
    }
540
541
    /**
542
     * get enable agents
543
     *
544
     * @return array
545
     */
546 3
    public static function getEnableAgents()
547
    {
548 3
        return self::$agentsName;
549
    }
550
551
    /**
552
     * get agents config info
553
     *
554
     * @return array
555
     */
556 3
    public static function getAgentsConfig()
557
    {
558 3
        return self::$agentsConfig;
559
    }
560
561
    /**
562
     * overload static method
563
     *
564
     * @param $name
565
     * @param $args
566
     *
567
     * @throws PhpSmsException
568
     */
569
    public static function __callStatic($name, $args)
570
    {
571
        $name = $name === 'beforeSend' ? 'beforeRun' : $name;
572
        $name = $name === 'afterSend' ? 'afterRun' : $name;
573
        if (in_array($name, self::$enableHooks)) {
574
            $handler = $args[0];
575
            $override = isset($args[1]) ? (bool) $args[1] : false;
576
            if ($handler && is_callable($handler)) {
577
                $task = self::init();
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $task is correct as self::init() (which targets Toplan\PhpSms\Sms::init()) 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...
578
                $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...
579
            } else {
580
                throw new PhpSmsException("Please give method static $name() a callable parameter");
581
            }
582
        } else {
583
            throw new PhpSmsException("Do not find static method $name()");
584
        }
585
    }
586
587
    /**
588
     * overload method
589
     *
590
     * @param $name
591
     * @param $args
592
     *
593
     * @throws PhpSmsException
594
     * @throws \Exception
595
     */
596
    public function __call($name, $args)
597
    {
598
        try {
599
            $this->__callStatic($name, $args);
600
        } catch (\Exception $e) {
601
            throw $e;
602
        }
603
    }
604
}
605