Completed
Pull Request — master (#54)
by lan tian
02:09
created

Sms::generatorAgentsName()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

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