Completed
Push — master ( 4cdf42...f68f13 )
by lan tian
9s
created

Sms::validateConfig()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

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