Completed
Pull Request — master (#44)
by lan tian
04:25
created

Sms::send()   B

Complexity

Conditions 4
Paths 8

Size

Total Lines 32
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4

Importance

Changes 10
Bugs 2 Features 2
Metric Value
c 10
b 2
f 2
dl 0
loc 32
rs 8.5806
ccs 14
cts 14
cp 1
cc 4
eloc 13
nc 8
nop 1
crap 4
1
<?php
2
3
namespace Toplan\PhpSms;
4
5
use SuperClosure\Serializer;
6
use Toplan\TaskBalance\Balancer;
7
8
/**
9
 * Class Sms
10
 */
11
class Sms
12
{
13
    /**
14
     * sms send task name
15
     */
16
    const TASK = 'PhpSms';
17
18
    /**
19
     * agents instance
20
     */
21
    protected static $agents = [];
22
23
    /**
24
     * agents`s name
25
     *
26
     * @var
27
     */
28
    protected static $agentsName = [];
29
30
    /**
31
     * agents`s config
32
     *
33
     * @var
34
     */
35
    protected static $agentsConfig = [];
36
37
    /**
38
     * whether to enable queue
39
     *
40
     * @var bool
41
     */
42
    protected static $enableQueue = false;
43
44
    /**
45
     * queue work
46
     *
47
     * @var \Closure
48
     */
49
    protected static $howToUseQueue = null;
50
51
    /**
52
     * sms already pushed to queue
53
     *
54
     * @var bool
55
     */
56
    protected $pushedToQueue = false;
57
58
    /**
59
     * hook handlers
60
     *
61
     * @var array
62
     */
63
    protected static $enableHooks = [
64
        'beforeRun',
65
        'beforeDriverRun',
66
        'afterDriverRun',
67
        'afterRun',
68
    ];
69
70
    /**
71
     * sms data
72
     *
73
     * @var array
74
     */
75
    protected $smsData = [
76
        'to'           => null,
77
        'templates'    => [],
78
        'content'      => '',
79
        'templateData' => [],
80
        'voiceCode'    => null,
81
    ];
82
83
    /**
84
     * first agent for send sms/voice verify
85
     *
86
     * @var string
87
     */
88
    protected $firstAgent = null;
89
90
    /**
91
     * a instance of class 'SuperClosure\Serializer'
92
     * @var null
93
     */
94 9
    protected static $serializer = null;
95
96 9
    /**
97 9
     * store the static properties of Sms class when serialize a instance
98 9
     * @var array
99 9
     */
100
    protected $_status_before_enqueue_ = [];
101
102
    /**
103
     * construct
104
     *
105
     * @param bool $autoBoot
106
     */
107
    public function __construct($autoBoot = true)
108
    {
109 3
        if ($autoBoot) {
110
            self::bootstrap();
111 3
        }
112 3
    }
113
114 3
    /**
115
     * create sms instance and set templates
116
     *
117
     * @param mixed $agentName
118
     * @param mixed $tempId
119
     *
120
     * @return Sms
121
     */
122 3
    public static function make($agentName = null, $tempId = null)
123
    {
124
        $sms = new self();
125
        if (is_array($agentName)) {
126
            $sms->template($agentName);
127
        } elseif ($agentName && is_string($agentName)) {
128
            if ($tempId === null) {
129
                $sms->content($agentName);
130
            } elseif (is_string("$tempId")) {
131
                $sms->template($agentName, $tempId);
132 3
            }
133
        }
134 3
135 3
        return $sms;
136
    }
137 3
138
    /**
139
     * send voice verify
140
     *
141
     * @param $code
142
     *
143
     * @return Sms
144
     */
145
    public static function voice($code)
146
    {
147
        $sms = new self();
148 3
        $sms->smsData['voiceCode'] = $code;
149
150 3
        return $sms;
151 3
    }
152
153 3
    /**
154 3
     * set how to use queue.
155 3
     *
156 3
     * @param $enable
157 3
     * @param $handler
158 3
     *
159 3
     * @return bool
160 3
     */
161
    public static function queue($enable = null, $handler = null)
162 3
    {
163
        if ($enable === null && $handler === null) {
164
            return self::$enableQueue;
165
        }
166
        if (is_callable($enable)) {
167
            $handler = $enable;
168
            $enable = true;
169
        }
170
        self::$enableQueue = (bool) $enable;
171
        if (is_callable($handler)) {
172 6
            self::$howToUseQueue = $handler;
0 ignored issues
show
Documentation Bug introduced by
It seems like $handler of type callable is incompatible with the declared type object<Closure> 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...
173
        }
174 6
175
        return self::$enableQueue;
176 6
    }
177
178
    /**
179
     * set the mobile number
180
     *
181
     * @param $mobile
182
     *
183
     * @return $this
184
     */
185
    public function to($mobile)
186 3
    {
187
        $this->smsData['to'] = $mobile;
188 3
189
        return $this;
190 3
    }
191
192
    /**
193
     * set content for content sms
194
     *
195
     * @param $content
196
     *
197
     * @return $this
198
     */
199
    public function content($content)
200
    {
201 3
        $this->smsData['content'] = trim((string) $content);
202
203 3
        return $this;
204 3
    }
205 3
206 3
    /**
207 3
     * set template id for template sms
208 3
     *
209
     * @param $agentName
210
     * @param $tempId
211 3
     *
212 3
     * @return $this
213
     */
214 3
    public function template($agentName, $tempId = null)
215
    {
216
        if (is_array($agentName)) {
217
            foreach ($agentName as $k => $v) {
218
                $this->template($k, $v);
219
            }
220
        } elseif ($agentName && $tempId) {
221
            if (!isset($this->smsData['templates']) || !is_array($this->smsData['templates'])) {
222
                $this->smsData['templates'] = [];
223
            }
224 3
            $this->smsData['templates']["$agentName"] = $tempId;
225
        }
226 3
227
        return $this;
228 3
    }
229
230
    /**
231
     * set data for template sms
232
     *
233
     * @param array $data
234
     *
235
     * @return $this
236
     */
237
    public function data(array $data)
238 3
    {
239
        $this->smsData['templateData'] = $data;
240 3
241
        return $this;
242 3
    }
243
244
    /**
245
     * set the first agent
246
     *
247
     * @param $name
248
     *
249
     * @return $this
250
     */
251
    public function agent($name)
252 15
    {
253
        $this->firstAgent = (string) $name;
254 15
255
        return $this;
256
    }
257
258 15
    /**
259 12
     * start send
260 12
     *
261
     * @param bool $immediately
262
     *
263
     * @return mixed
264
     */
265
    public function send($immediately = false)
266
    {
267 15
        $this->validator();
268 3
269 3
        // if disable push to queue,
270
        // send the sms immediately.
271
        if (!self::$enableQueue) {
272
            $immediately = true;
273 15
        }
274 15
275 15
        // whatever 'PhpSms' whether to enable or disable push to queue,
276 15
        // if you are already pushed sms instance to queue,
277 15
        // you can recall the method `send()` in queue job without `true` parameter.
278 15
        //
279 3
        // So this mechanism in order to make you convenient use the method `send()` in queue system.
280
        if ($this->pushedToQueue) {
281
            $immediately = true;
282 15
        }
283
284
        // whether to send sms immediately,
285
        // or push it to queue.
286
        if ($immediately) {
287
            $result = Balancer::run(self::TASK, [
288
                'data'   => $this->getData(),
289
                'driver' => $this->firstAgent,
290
            ]);
291
        } else {
292 3
            $result = $this->push();
293
        }
294 3
295
        return $result;
296 3
    }
297
298 3
    /**
299
     * push sms send task to queue
300
     *
301
     * @throws \Exception | PhpSmsException
302
     *
303
     * @return mixed
304
     */
305
    protected function push()
306
    {
307
        if (is_callable(self::$howToUseQueue)) {
308
            try {
309
                $this->pushedToQueue = true;
310
311
                return call_user_func_array(self::$howToUseQueue, [$this, $this->smsData]);
312
            } catch (\Exception $e) {
313 33
                $this->pushedToQueue = false;
314
                throw $e;
315 33
            }
316
        } else {
317
            throw new PhpSmsException('Please define how to use queue by method `queue($enable, $handler)`');
318
        }
319
    }
320
321 9
    /**
322
     * get sms data
323 9
     *
324 9
     * @return array
325 3
     */
326 3
    public function getData()
327 3
    {
328 9
        return $this->smsData;
329
    }
330
331
    /**
332
     * bootstrap
333
     *
334
     * @param bool $force
335 18
     */
336
    public static function bootstrap($force = false)
337 18
    {
338 3
        if (!!$force) {
339 3
            Balancer::destroy(self::TASK);
340
        }
341 18
        $task = self::generatorTask();
342
        if (!count($task->drivers)) {
343
            self::configuration();
344
            self::createDrivers($task);
345
        }
346
    }
347 3
348
    /**
349 3
     * generator a sms send task
350 3
     *
351 3
     * @return object
352 3
     */
353 3
    public static function generatorTask()
354
    {
355
        if (!Balancer::hasTask(self::TASK)) {
356
            Balancer::task(self::TASK);
357
        }
358
359
        return Balancer::getTask(self::TASK);
360 3
    }
361
362 3
    /**
363 3
     * configuration
364 3
     */
365 3
    protected static function configuration()
366 3
    {
367 3
        $config = [];
368
        self::generatorAgentsName($config);
369
        self::generatorAgentsConfig($config);
370
        self::configValidator();
371
    }
372
373
    /**
374 3
     * generate enabled agents name
375
     *
376 3
     * @param array $config
377 3
     */
378 3
    protected static function generatorAgentsName(&$config)
379 3
    {
380 3
        if (empty(self::$agentsName)) {
381 3
            $config = $config ?: include __DIR__ . '/../config/phpsms.php';
382 3
            $enableAgents = isset($config['enable']) ? $config['enable'] : null;
383 3
            self::enable($enableAgents);
384 3
        }
385 3
    }
386 3
387
    /**
388
     * generator agents config
389
     *
390
     * @param array $config
391
     */
392
    protected static function generatorAgentsConfig(&$config)
393 3
    {
394
        $diff = array_diff_key(self::$agentsName, self::$agentsConfig);
395 3
        $diff = array_keys($diff);
396
        if (count($diff)) {
397
            $config = $config ?: include __DIR__ . '/../config/phpsms.php';
398 3
            $agentsConfig = isset($config['agents']) ? $config['agents'] : [];
399
            foreach ($diff as $name) {
400
                $agentConfig = isset($agentsConfig[$name]) ? $agentsConfig[$name] : [];
401
                self::agents($name, $agentConfig);
402
            }
403
        }
404
    }
405 15
406
    /**
407 3
     * config value validator
408
     *
409 3
     * @throws PhpSmsException
410
     */
411 3
    protected static function configValidator()
412
    {
413
        if (!count(self::$agentsName)) {
414
            throw new PhpSmsException('Please set at least one enable agent in config file(config/phpsms.php) or use method enable()');
415
        }
416
    }
417 3
418 15
    /**
419 15
     * create drivers for sms send task
420 15
     *
421 15
     * @param $task
422 15
     */
423 15
    protected static function createDrivers($task)
424
    {
425
        foreach (self::$agentsName as $name => $options) {
426 15
            //获取代理器配置
427 15
            $configData = self::getAgentConfigData($name);
428
            //解析代理器数组模式的调度配置
429 15
            if (is_array($options)) {
430 15
                $data = self::parseAgentArrayOptions($options);
431 15
                $configData = array_merge($configData, $data);
432 15
                $options = $data['driverOpts'];
433 15
            }
434
            //创建任务驱动器
435 15
            $task->driver("$name $options")->data($configData)
436 3
                 ->work(function ($driver) {
437 3
                     $configData = $driver->getDriverData();
438 3
                     $agent = self::getSmsAgent($driver->name, $configData);
439
                     $smsData = $driver->getTaskData();
440
                     extract($smsData);
441
                     if (isset($smsData['voiceCode']) && $smsData['voiceCode']) {
442
                         $agent->voiceVerify($to, $voiceCode);
443
                     } else {
444
                         $template = isset($templates[$driver->name]) ? $templates[$driver->name] : 0;
445
                         $agent->sendSms($template, $to, $templateData, $content);
446
                     }
447
                     $result = $agent->getResult();
448
                     if ($result['success']) {
449
                         $driver->success();
450
                     }
451
                     unset($result['success']);
452
453
                     return $result;
454
                 });
455
        }
456
    }
457
458
    /**
459
     * 解析可用代理器的数组模式的调度配置
460
     *
461
     * @param array $options
462
     *
463
     * @return array
464
     */
465
    protected static function parseAgentArrayOptions(array $options)
466
    {
467
        $agentClass = self::pullAgentOptionByName($options, 'agentClass');
468
        $sendSms = self::pullAgentOptionByName($options, 'sendSms');
469
        $voiceVerify = self::pullAgentOptionByName($options, 'voiceVerify');
470
        $backup = self::pullAgentOptionByName($options, 'backup');
471
        $driverOpts = implode(' ', array_values($options)) . " $backup";
472
473
        return compact('agentClass', 'sendSms', 'voiceVerify', 'driverOpts');
474
    }
475
476
    /**
477
     * 从调度配置中拉取指定数据
478
     *
479
     * @param array  $options
480
     * @param string $name
481
     *
482
     * @return null|string
483
     */
484 3
    protected static function pullAgentOptionByName(array &$options, $name)
485
    {
486 3
        $value = isset($options[$name]) ? $options[$name] : null;
487 3
        if ($name === 'backup') {
488
            $value = isset($options[$name]) ? ($options[$name] ? 'backup' : '') : '';
489
        }
490
        unset($options[$name]);
491
492
        return $value;
493
    }
494
495
    /**
496
     * get agent config data by name
497
     *
498
     * @param $name
499
     *
500
     * @return array
501 18
     */
502
    protected static function getAgentConfigData($name)
503 18
    {
504 3
        return isset(self::$agentsConfig[$name]) ?
505 3
               (array) self::$agentsConfig[$name] : [];
506 3
    }
507 3
508
    /**
509
     * get a sms agent instance,
510
     * if null, will create a new agent instance
511 3
     *
512
     * @param       $name
513 3
     * @param array $configData
514 3
     *
515
     * @throws PhpSmsException
516
     *
517
     * @return mixed
518 3
     */
519
    public static function getSmsAgent($name, array $configData)
520 18
    {
521
        if (!isset(self::$agents[$name])) {
522
            $configData['name'] = $name;
523
            $className = isset($configData['agentClass']) ? $configData['agentClass'] : ('Toplan\\PhpSms\\' . $name . 'Agent');
524
            if ((isset($configData['sendSms']) && is_callable($configData['sendSms'])) ||
525
                (isset($configData['voiceVerify']) && is_callable($configData['voiceVerify']))) {
526
                //创建寄生代理器
527
                $configData['agentClass'] = '';
528 18
                self::$agents[$name] = new ParasiticAgent($configData);
529
            } elseif (class_exists($className)) {
530 18
                //创建新代理器
531
                self::$agents[$name] = new $className($configData);
532
            } else {
533
                //无代理器可用
534 18
                throw new PhpSmsException("Do not support [$name] agent.");
535
            }
536
        }
537
538
        return self::$agents[$name];
539
    }
540
541
    /**
542
     * validate
543 6
     *
544
     * @throws PhpSmsException
545 6
     */
546 6
    protected function validator()
547 6
    {
548 6
        if (!$this->smsData['to']) {
549 6
            throw new PhpSmsException('Please set send sms(or voice verify) to who use `to()` method.');
550 3
        }
551 6
552 3
        return true;
553 6
    }
554 3
555 3
    /**
556 6
     * set enable agents
557
     *
558
     * @param      $agentName
559
     * @param null $options
560
     */
561
    public static function enable($agentName, $options = null)
562
    {
563
        if (is_array($agentName)) {
564
            foreach ($agentName as $name => $opt) {
565
                self::enable($name, $opt);
566 6
            }
567
        } elseif ($agentName && is_string($agentName) && $options !== null) {
568 6
            self::$agentsName["$agentName"] = is_array($options) ? $options : "$options";
569 3
        } elseif (is_int($agentName) && !is_array($options) && "$options") {
570 3
            self::$agentsName["$options"] = '1';
571 3
        } elseif ($agentName && $options === null) {
572 6
            self::$agentsName["$agentName"] = '1';
573 6
        }
574
    }
575
576 6
    /**
577 6
     * set config for available agents
578 6
     *
579
     * @param       $agentName
580
     * @param array $config
581
     *
582
     * @throws PhpSmsException
583
     */
584
    public static function agents($agentName, array $config = [])
585 9
    {
586
        if (is_array($agentName)) {
587 9
            foreach ($agentName as $name => $conf) {
588
                self::agents($name, $conf);
589
            }
590
        } elseif ($agentName && is_array($config)) {
591
            if (preg_match('/^[0-9]+$/', $agentName)) {
592
                throw new PhpSmsException("Agent name [$agentName] must be string, could not be a pure digital");
593
            }
594
            self::$agentsConfig["$agentName"] = $config;
595 9
        }
596
    }
597 9
598
    /**
599
     * get enable agents
600
     *
601
     * @return array
602
     */
603 3
    public static function getEnableAgents()
604
    {
605 3
        return self::$agentsName;
606 3
    }
607
608
    /**
609
     * get agents config info
610
     *
611 3
     * @return array
612
     */
613 3
    public static function getAgentsConfig()
614 3
    {
615
        return self::$agentsConfig;
616
    }
617
618
    /**
619
     * tear down enable agents
620
     */
621
    public static function cleanEnableAgents()
622
    {
623
        self::$agentsName = [];
624 6
    }
625
626 6
    /**
627 6
     * tear down agents config
628 6
     */
629 6
    public static function cleanAgentsConfig()
630 6
    {
631 6
        self::$agentsConfig = [];
632 6
    }
633 6
634 6
    /**
635 6
     * overload static method
636 6
     *
637
     * @param $name
638
     * @param $args
639 6
     *
640
     * @throws PhpSmsException
641
     */
642 6
    public static function __callStatic($name, $args)
643
    {
644
        $name = $name === 'beforeSend' ? 'beforeRun' : $name;
645
        $name = $name === 'afterSend' ? 'afterRun' : $name;
646
        $name = $name === 'beforeAgentSend' ? 'beforeDriverRun' : $name;
647
        $name = $name === 'afterAgentSend' ? 'afterDriverRun' : $name;
648
        if (in_array($name, self::$enableHooks)) {
649
            $handler = $args[0];
650
            $override = isset($args[1]) ? (bool) $args[1] : false;
651
            if (is_callable($handler)) {
652
                $task = self::generatorTask();
653 3
                $task->hook($name, $handler, $override);
654
            } else {
655
                throw new PhpSmsException("Please give method static $name() a callable parameter");
656 3
            }
657 3
        } else {
658
            throw new PhpSmsException("Do not find static method $name()");
659
        }
660 3
    }
661
662
    /**
663
     * overload method
664
     *
665
     * @param $name
666
     * @param $args
667
     *
668
     * @throws PhpSmsException
669
     * @throws \Exception
670
     */
671
    public function __call($name, $args)
672
    {
673
        try {
674
            $this->__callStatic($name, $args);
675
        } catch (\Exception $e) {
676
            throw $e;
677
        }
678
    }
679
680
    /**
681
     * serialize magic method
682
     * store current sms instance status
683
     *
684
     * @return array
685
     */
686
    public function __sleep()
687
    {
688
        $this->_status_before_enqueue_['enableAgents'] = self::serializeEnableAgents();
689
        $this->_status_before_enqueue_['agentsConfig'] = self::getAgentsConfig();
690
        $this->_status_before_enqueue_['handlers'] = self::serializeHandlers();
691
        
692
        return ['pushedToQueue', 'smsData', 'firstAgent', '_status_before_enqueue_'];
693
    }
694
695
    /**
696
     * unserialize magic method
697
     * note: the force bootstrap must before reinstall handlers!
698
     */
699
    public function __wakeup()
700
    {
701
        $status = $this->_status_before_enqueue_;
702
        self::$agentsName = self::unserializeEnableAgents($status['enableAgents']);
703
        self::$agentsConfig = $status['agentsConfig'];
704
        self::bootstrap(true);
705
        self::reinstallHandlers($status['handlers']);
706
    }
707
708
    /**
709
     * get a serializer
710
     *
711
     * @return Serializer
712
     */
713
    public static function getSerializer()
714
    {
715
        if (!self::$serializer) {
716
            self::$serializer = new Serializer();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \SuperClosure\Serializer() of type object<SuperClosure\Serializer> is incompatible with the declared type null of property $serializer.

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...
717
        }
718
719
        return self::$serializer;
720
    }
721
722
    /**
723
     * serialize enable agents
724
     *
725
     * @return array
726
     */
727
    protected static function serializeEnableAgents()
728
    {
729
        $serializer = self::getSerializer();
730
        $enableAgents = self::getEnableAgents();
731
        foreach ($enableAgents as $name => &$options) {
732
            if (is_array($options)) {
733 View Code Duplication
                if (isset($options['sendSms']) && is_callable($options['sendSms'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
734
                    $options['sendSms'] = $serializer->serialize($options['sendSms']);
0 ignored issues
show
Documentation introduced by
$options['sendSms'] is of type callable, but the function expects a object<Closure>.

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...
735
                }
736 View Code Duplication
                if (isset($options['voiceVerify']) && is_callable($options['voiceVerify'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
737
                    $options['voiceVerify'] = $serializer->serialize($options['voiceVerify']);
0 ignored issues
show
Documentation introduced by
$options['voiceVerify'] is of type callable, but the function expects a object<Closure>.

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...
738
                }
739
            }
740
        }
741
742
        return $enableAgents;
743
    }
744
745
    /**
746
     * unserialize enable agents
747
     *
748
     * @param array $serialized
749
     *
750
     * @return mixed
751
     */
752
    protected static function unserializeEnableAgents(array $serialized)
753
    {
754
        $serializer = self::getSerializer();
755
        foreach ($serialized as $name => &$options) {
756
            if (is_array($options)) {
757
                if (isset($options['sendSms'])) {
758
                    $options['sendSms'] = $serializer->unserialize($options['sendSms']);
759
                }
760
                if (isset($options['voiceVerify'])) {
761
                    $options['voiceVerify'] = $serializer->unserialize($options['voiceVerify']);
0 ignored issues
show
Documentation introduced by
$options['voiceVerify'] is of type object<Closure>, but the function expects a string.

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...
762
                }
763
            }
764
        }
765
766
        return $serialized;
767
    }
768
769
    /**
770
     * serialize these hooks` handlers:
771
     * 'beforeRun','beforeDriverRun','afterDriverRun','afterRun'
772
     *
773
     * @return array
774
     */
775
    protected static function serializeHandlers()
776
    {
777
        $hooks = [];
778
        $serializer = self::getSerializer();
779
        $task = self::generatorTask();
780
        foreach ($task->handlers as $hookName => $handlers) {
781
            foreach ($handlers as $handler) {
782
                $serialized = $serializer->serialize($handler);
783
                if (!isset($hooks[$hookName])) {
784
                    $hooks[$hookName] = [];
785
                }
786
                array_push($hooks[$hookName], $serialized);
787
            }
788
        }
789
790
        return $hooks;
791
    }
792
793
    /**
794
     * reinstall hooks` handlers by serialized handlers
795
     *
796
     * @param array $handlers
797
     */
798
    protected static function reinstallHandlers(array $handlers)
799
    {
800
        $serializer = self::getSerializer();
801
        foreach ($handlers as $hookName => $serializedHandlers) {
802
            foreach ($serializedHandlers as $index => $handler) {
803
                if (is_string($handler)) {
804
                    $handler = $serializer->unserialize($handler);
805
                }
806
                $override = $index === 0;
807
                self::$hookName($handler, $override);
808
            }
809
        }
810
    }
811
}
812