Completed
Pull Request — master (#88)
by lan tian
03:55
created

Sms   D

Complexity

Total Complexity 115

Size/Duplication

Total Lines 795
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 5

Test Coverage

Coverage 91.35%

Importance

Changes 0
Metric Value
wmc 115
lcom 2
cbo 5
dl 0
loc 795
ccs 264
cts 289
cp 0.9135
rs 4.4444
c 0
b 0
f 0

39 Methods

Rating   Name   Duplication   Size   Complexity  
A __call() 0 8 2
A __construct() 0 6 2
A bootstrap() 0 7 2
A taskInitialized() 0 6 1
A getTask() 0 8 2
A configuration() 0 10 2
A initScheme() 0 6 3
B initAgentsConfig() 0 12 6
A validateConfig() 0 6 2
C initTask() 0 29 7
B queue() 0 16 5
A to() 0 6 1
A content() 0 6 1
A template() 0 6 1
A data() 0 6 1
A agent() 0 6 1
A send() 0 16 4
A push() 0 15 3
A getData() 0 8 3
B __callStatic() 0 19 8
A __sleep() 0 12 2
A __wakeup() 0 12 2
A getSerializer() 0 8 2
A serializeOrDeserializeScheme() 0 11 3
A serializeHandlers() 0 12 3
A reinstallHandlers() 0 12 4
A serializeOrDeserializeClosureAndReplace() 0 12 4
A parseScheme() 0 10 2
B getAgent() 0 18 7
A hasAgent() 0 4 1
A scheme() 0 10 4
A modifyScheme() 0 8 2
B config() 0 19 6
A modifyConfig() 0 8 2
A validateAgentName() 0 6 4
A cleanScheme() 0 5 1
A cleanConfig() 0 4 1
B make() 0 16 7
A voice() 0 8 1

How to fix   Complexity   

Complex Class

Complex classes like Sms often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Sms, and based on these observations, apply Extract Interface, too.

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