Completed
Pull Request — master (#56)
by lan tian
04:48 queued 02:33
created

Sms::cleanScheme()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
ccs 1
cts 1
cp 1
rs 9.4285
cc 1
eloc 3
nc 1
nop 0
crap 1
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
     * The default name of balancing task.
19
     *
20
     * @var string
21
     */
22
    const TASK = 'PhpSms';
23
24
    /**
25
     * The instances of class [Toplan\PhpSms\Agent].
26
     *
27
     * @var array
28
     */
29
    protected static $agents = [];
30
31
    /**
32
     * Agents use scheme, these agents are available.
33
     * example: [
34
     *   'Agent1' => '10 backup',
35
     *   'Agent2' => '20 backup',
36
     * ]
37
     *
38
     * @var array
39
     */
40
    protected static $scheme = [];
41
42
    /**
43
     * The agents` configuration information.
44
     *
45
     * @var array
46
     */
47
    protected static $agentsConfig = [];
48
49
    /**
50
     * Whether to use the queue.
51
     *
52
     * @var bool
53
     */
54
    protected static $enableQueue = false;
55
56
    /**
57
     * How to use the queue.
58
     *
59
     * @var \Closure
60
     */
61
    protected static $howToUseQueue = null;
62
63
    /**
64
     * The available hooks for balancing task.
65
     *
66
     * @var array
67
     */
68
    protected static $availableHooks = [
69
        'beforeRun',
70
        'beforeDriverRun',
71
        'afterDriverRun',
72
        'afterRun',
73
    ];
74
75
    /**
76
     * An instance of class [SuperClosure\Serializer] use for serialization closures.
77
     *
78
     * @var Serializer
79
     */
80
    protected static $serializer = null;
81
82
    /**
83
     * SMS/voice verify data container.
84
     *
85
     * @var array
86
     */
87
    protected $smsData = [
88
        'to'           => null,
89
        'templates'    => [],
90
        'content'      => null,
91
        'templateData' => [],
92
        'voiceCode'    => null,
93
    ];
94
95
    /**
96
     * The name of first agent.
97
     *
98
     * @var string|null
99
     */
100
    protected $firstAgent = null;
101
102
    /**
103
     * Whether the current instance has already pushed to the queue system.
104
     *
105
     * @var bool
106
     */
107
    protected $pushedToQueue = false;
108
109
    /**
110
     * Status container,
111
     * store some configuration information before serialize current instance(before enqueue).
112
     *
113
     * @var array
114
     */
115
    protected $_status_before_enqueue_ = [];
116
117
    /**
118
     * Constructor
119 6
     *
120
     * @param bool $autoBoot
121 6
     */
122 3
    public function __construct($autoBoot = true)
123 2
    {
124 6
        if ($autoBoot) {
125
            self::bootstrap();
126
        }
127
    }
128
129 6
    /**
130
     * Boot balancing task for send SMS/voice verify.
131 6
     */
132
    public static function bootstrap()
133
    {
134
        $task = self::getTask();
135 6
136 3
        //注意这里不能用'empty',因为在TaskBalance库的<=v0.4.2版本中Task实例的drivers是受保护的属性(不可访问),
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
137 3
        //虽然通过魔术方法可以获取到其值,但其内部却并没有使用'__isset'魔术方法对'empty'或'isset'函数进行逻辑补救.
0 ignored issues
show
Unused Code Comprehensibility introduced by
40% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
138 2
        if (!count($task->drivers)) {
139 6
            self::configuration();
140
            self::createDrivers($task);
141
        }
142
    }
143
144
    /**
145
     * Get or generate a balancing task instance for send SMS/voice verify.
146 15
     *
147
     * @return Task
148 15
     */
149 3
    public static function getTask()
150 2
    {
151
        if (!Balancer::hasTask(self::TASK)) {
152 15
            Balancer::task(self::TASK);
153
        }
154
155
        return Balancer::getTask(self::TASK);
156
    }
157
158 6
    /**
159
     * Configuration.
160 6
     */
161 6
    protected static function configuration()
162 3
    {
163 2
        $config = [];
164 6
        if (empty(self::$scheme)) {
165 6
            self::initScheme($config);
166 6
        }
167 6
        $diff = array_diff_key(self::$scheme, self::$agentsConfig);
168
        self::initAgentsConfig(array_keys($diff), $config);
169
        self::validateConfig();
170
    }
171
172
    /**
173
     * Try to read agent use scheme from config file.
174 3
     *
175
     * @param array $config
176 3
     */
177 3
    protected static function initScheme(array &$config)
178 3
    {
179 3
        $config = empty($config) ? include __DIR__ . '/../config/phpsms.php' : $config;
180
        $scheme = isset($config['scheme']) ? $config['scheme'] : [];
181
        self::scheme($scheme);
182
    }
183
184
    /**
185
     * Try to initialize the specified agents` configuration information.
186
     *
187 6
     * @param array $agents
188
     * @param array $config
189 6
     */
190 3
    protected static function initAgentsConfig(array $agents, array &$config)
191
    {
192 3
        if (empty($agents)) {
193 3
            return;
194 3
        }
195 3
        $config = empty($config) ? include __DIR__ . '/../config/phpsms.php' : $config;
196 3
        $agentsConfig = isset($config['agents']) ? $config['agents'] : [];
197 2
        foreach ($agents as $name) {
198 3
            $agentConfig = isset($agentsConfig[$name]) ? $agentsConfig[$name] : [];
199
            self::config($name, $agentConfig);
200
        }
201
    }
202
203
    /**
204
     * validate configuration.
205 6
     *
206
     * @throws PhpSmsException
207 6
     */
208
    protected static function validateConfig()
209
    {
210 6
        if (empty(self::$scheme)) {
211
            throw new PhpSmsException('Please configure at least one agent');
212
        }
213
    }
214
215
    /**
216
     * Create drivers for the balancing task.
217 18
     *
218
     * @param Task $task
219 3
     */
220
    protected static function createDrivers(Task $task)
221 3
    {
222
        foreach (self::$scheme as $name => $scheme) {
223 3
            //获取代理器配置
224 3
            $configData = self::config($name);
225 3
226 3
            //解析代理器数组模式的调度配置
227 2
            if (is_array($scheme)) {
228
                $data = self::parseScheme($scheme);
229 3
                $configData = array_merge($configData, $data);
230 18
                $scheme = $data['scheme'];
231 18
            }
232 18
            $scheme = is_string($scheme) ? $scheme : '';
233 18
234 18
            //创建任务驱动器
235 18
            $task->driver("$name $scheme")->data($configData)
236
                 ->work(function ($driver) {
237
                     $configData = $driver->getDriverData();
238 18
                     $agent = self::getSmsAgent($driver->name, $configData);
239 18
                     $smsData = $driver->getTaskData();
240
                     extract($smsData);
241 18
                     if (isset($smsData['voiceCode']) && $smsData['voiceCode']) {
242 18
                         $agent->voiceVerify($to, $voiceCode);
243 18
                     } else {
244 12
                         $template = isset($templates[$driver->name]) ? $templates[$driver->name] : 0;
245 18
                         $agent->sendSms($template, $to, $templateData, $content);
246
                     }
247 18
                     $result = $agent->result();
248 3
                     if ($result['success']) {
249 2
                         $driver->success();
250 3
                     }
251
                     unset($result['success']);
252
253
                     return $result;
254
                 });
255
        }
256
    }
257
258
    /**
259
     * Parsing scheduling configuration.
260 3
     * 解析代理器的数组模式的调度配置
261
     *
262 3
     * @param array $options
263 3
     *
264 3
     * @return array
265 3
     */
266 3
    protected static function parseScheme(array $options)
267
    {
268 3
        $agentClass = self::pullOptionOutOfArrayByName($options, 'agentClass');
269
        $sendSms = self::pullOptionOutOfArrayByName($options, 'sendSms');
270
        $voiceVerify = self::pullOptionOutOfArrayByName($options, 'voiceVerify');
271
        $backup = self::pullOptionOutOfArrayByName($options, 'backup') ? 'backup' : '';
272
        $scheme = implode(' ', array_values($options)) . " $backup";
273
274
        return compact('agentClass', 'sendSms', 'voiceVerify', 'scheme');
275
    }
276
277
    /**
278
     * Pull the value of the specified option out of the array.
279 3
     *
280
     * @param array      $options
281 3
     * @param int|string $name
282 3
     *
283
     * @return mixed
284 3
     */
285 3
    protected static function pullOptionOutOfArrayByName(array &$options, $name)
286
    {
287 3
        if (!isset($options[$name])) {
288
            return;
289
        }
290
        $value = $options[$name];
291
        unset($options[$name]);
292
293
        return $value;
294
    }
295
296
    /**
297 3
     * Get a sms agent instance by agent name,
298
     * if null, will try to create a new agent instance.
299 3
     *
300
     * @param string $name
301
     * @param array  $configData
302
     *
303
     * @throws PhpSmsException
304
     *
305
     * @return mixed
306
     */
307
    public static function getSmsAgent($name, array $configData)
308
    {
309
        if (!isset(self::$agents[$name])) {
310
            $configData['name'] = $name;
311
            $className = isset($configData['agentClass']) ? $configData['agentClass'] : ('Toplan\\PhpSms\\' . $name . 'Agent');
312
            if ((isset($configData['sendSms']) && is_callable($configData['sendSms'])) ||
313 21
                (isset($configData['voiceVerify']) && is_callable($configData['voiceVerify']))) {
314
                //创建寄生代理器
315 21
                $configData['agentClass'] = '';
316 6
                self::$agents[$name] = new ParasiticAgent($configData);
317 6
            } elseif (class_exists($className)) {
318 6
                //创建新代理器
319 6
                self::$agents[$name] = new $className($configData);
320
            } else {
321 3
                //无代理器可用
322 3
                throw new PhpSmsException("Dont support [$name] agent.");
323 5
            }
324
        }
325 3
326 2
        return self::$agents[$name];
327
    }
328
329
    /**
330 4
     * Set or get agent use scheme by agent name.
331
     *
332 21
     * @param mixed $agentName
333
     * @param mixed $scheme
334
     *
335
     * @return mixed
336
     */
337
    public static function scheme($agentName = null, $scheme = null)
338
    {
339 View Code Duplication
        if (($agentName === null || is_string($agentName)) && $scheme === null) {
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...
340
            return $agentName === null ? self::$scheme :
341 6
                (isset(self::$scheme[$agentName]) ? self::$scheme[$agentName] : null);
342
        }
343 6
        if (is_array($agentName)) {
344 6
            foreach ($agentName as $name => $value) {
345 6
                self::scheme($name, $value);
346 4
            }
347 6
        } elseif ($agentName && is_string($agentName)) {
348 3
            self::$scheme["$agentName"] = is_array($scheme) ? $scheme : "$scheme";
349 6
        } elseif (is_int($agentName) && $scheme && is_string($scheme)) {
350 3
            self::$scheme["$scheme"] = '';
351 5
        }
352 3
353 2
        return self::$scheme;
354 6
    }
355
356
    /**
357
     * Set or get configuration information by agent name.
358
     *
359
     * @param mixed $agentName
360
     * @param mixed $config
361
     *
362
     * @throws PhpSmsException
363
     *
364 6
     * @return array
365
     */
366 6
    public static function config($agentName = null, $config = null)
367 3
    {
368 3 View Code Duplication
        if (($agentName === null || is_string($agentName)) && $config === null) {
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...
369 2
            return $agentName === null ? self::$agentsConfig :
370 6
                (isset(self::$agentsConfig[$agentName]) ? self::$agentsConfig[$agentName] : []);
371 6
        }
372
        if (is_array($agentName)) {
373
            foreach ($agentName as $name => $value) {
374 6
                self::config($name, $value);
375 4
            }
376 6
        } elseif ($agentName && is_array($config)) {
377
            if (preg_match('/^[0-9]+$/', $agentName)) {
378
                throw new PhpSmsException("Agent name [$agentName] must be string, can not be a pure digital");
379
            }
380
            self::$agentsConfig["$agentName"] = $config;
381
        }
382
383 12
        return self::$agentsConfig;
384
    }
385 12
386
    /**
387
     * Tear down agent use scheme and prepare to create and start a new balancing task,
388
     * so before do it must destroy old task instance.
389
     */
390
    public static function cleanScheme()
391
    {
392
        Balancer::destroy(self::TASK);
393 12
        self::$scheme = [];
394
    }
395 12
396
    /**
397
     * Tear down agent config and prepare to create and start a new balancing task,
398
     * so before do it must destroy old task instance.
399
     */
400
    public static function cleanAgentsConfig()
401
    {
402 6
        Balancer::destroy(self::TASK);
403
        self::$agentsConfig = [];
404 6
    }
405 6
406 6
    /**
407
     * Create a sms instance send SMS,
408
     * your can also set SMS templates or content at the same time.
409
     *
410
     * @param mixed $agentName
411
     * @param mixed $tempId
412 3
     *
413
     * @return Sms
414 3
     */
415 3
    public static function make($agentName = null, $tempId = null)
416 3
    {
417
        $sms = new self();
418
        if (is_array($agentName)) {
419
            $sms->template($agentName);
420
        } elseif ($agentName && is_string($agentName)) {
421
            if ($tempId === null) {
422
                $sms->content($agentName);
423
            } elseif (is_string("$tempId")) {
424
                $sms->template($agentName, $tempId);
425
            }
426
        }
427
428
        return $sms;
429
    }
430
431
    /**
432
     * Create a sms instance send voice verify,
433
     * your can also set verify code at the same time.
434
     *
435
     * @param string|int $code
436
     *
437
     * @return Sms
438
     */
439
    public static function voice($code)
440
    {
441
        $sms = new self();
442
        $sms->smsData['voiceCode'] = $code;
443
444
        return $sms;
445
    }
446
447
    /**
448
     * Set whether to use the queue system, and define how to use it.
449
     *
450
     * @param mixed $enable
451 3
     * @param mixed $handler
452
     *
453 3
     * @return bool
454 3
     */
455
    public static function queue($enable = null, $handler = null)
456 3
    {
457
        if ($enable === null && $handler === null) {
458
            return self::$enableQueue;
459
        }
460
        if (is_callable($enable)) {
461
            $handler = $enable;
462
            $enable = true;
463
        }
464
        self::$enableQueue = (bool) $enable;
465
        if (is_callable($handler)) {
466
            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...
467 3
        }
468
469 3
        return self::$enableQueue;
470 3
    }
471
472 3
    /**
473 3
     * Set the recipient`s mobile number.
474 3
     *
475 2
     * @param string $mobile
476 3
     *
477 3
     * @return $this
478 3
     */
479 2
    public function to($mobile)
480
    {
481 3
        $this->smsData['to'] = $mobile;
482
483
        return $this;
484
    }
485
486
    /**
487
     * Set the content for content SMS.
488
     *
489
     * @param string $content
490
     *
491 6
     * @return $this
492
     */
493 6
    public function content($content)
494
    {
495 6
        $this->smsData['content'] = trim((string) $content);
496
497
        return $this;
498
    }
499
500
    /**
501
     * Set the template id for template SMS.
502
     *
503
     * @param mixed $agentName
504
     * @param mixed $tempId
505 3
     *
506
     * @return $this
507 3
     */
508
    public function template($agentName, $tempId = null)
509 3
    {
510
        if (is_array($agentName)) {
511
            foreach ($agentName as $k => $v) {
512
                $this->template($k, $v);
513
            }
514
        } elseif ($agentName && $tempId) {
515
            if (!isset($this->smsData['templates']) || !is_array($this->smsData['templates'])) {
516
                $this->smsData['templates'] = [];
517
            }
518
            $this->smsData['templates']["$agentName"] = $tempId;
519
        }
520 3
521
        return $this;
522 3
    }
523 3
524 3
    /**
525 2
     * Set the template data for template SMS.
526 3
     *
527 3
     * @param array $data
528
     *
529
     * @return $this
530 3
     */
531 2
    public function data(array $data)
532
    {
533 3
        $this->smsData['templateData'] = $data;
534
535
        return $this;
536
    }
537
538
    /**
539
     * Set the first agent by name.
540
     *
541
     * @param string $name
542
     *
543 3
     * @return $this
544
     */
545 3
    public function agent($name)
546
    {
547 3
        $this->firstAgent = (string) $name;
548
549
        return $this;
550
    }
551
552
    /**
553
     * Start send SMS/voice verify.
554
     *
555
     * If give a true parameter, this system will immediately start request to send SMS/voice verify whatever whether to use the queue.
556
     * if you are already pushed sms instance to the queue, you can recall the method `send()` in queue system without `true` parameter,
557 3
     * so this mechanism in order to make you convenient use the method `send()` in queue system.
558
     *
559 3
     * @param bool $immediately
560
     *
561 3
     * @return mixed
562
     */
563
    public function send($immediately = false)
564
    {
565
        if (!self::$enableQueue || $this->pushedToQueue) {
566
            $immediately = true;
567
        }
568
        if ($immediately) {
569
            $result = Balancer::run(self::TASK, [
570
                'data'   => $this->getData(),
571
                'driver' => $this->firstAgent,
572
            ]);
573
        } else {
574
            $result = $this->push();
575 18
        }
576
577 18
        return $result;
578 18
    }
579 12
580 18
    /**
581 18
     * Push to the queue by a custom method.
582 18
     *
583 18
     * @throws \Exception | PhpSmsException
584 12
     *
585 12
     * @return mixed
586 3
     */
587
    public function push()
588
    {
589 18
        if (is_callable(self::$howToUseQueue)) {
590
            try {
591
                $this->pushedToQueue = true;
592
593
                return call_user_func_array(self::$howToUseQueue, [$this, $this->smsData]);
594
            } catch (\Exception $e) {
595
                $this->pushedToQueue = false;
596
                throw $e;
597
            }
598
        } else {
599 3
            throw new PhpSmsException('Please define how to use queue by method `queue($available, $handler)`');
600
        }
601 3
    }
602
603 3
    /**
604
     * Get all the data of SMS/voice verify.
605 3
     *
606
     * @param null|string $name
607
     *
608
     * @return mixed
609
     */
610
    public function getData($name = null)
611
    {
612
        if (is_string($name) && isset($this->smsData["$name"])) {
613
            return $this->smsData[$name];
614
        }
615
616
        return $this->smsData;
617
    }
618
619
    /**
620
     * Overload static method.
621
     *
622 36
     * @param string $name
623
     * @param array  $args
624 36
     *
625 3
     * @throws PhpSmsException
626
     */
627
    public static function __callStatic($name, $args)
628 36
    {
629
        $name = $name === 'beforeSend' ? 'beforeRun' : $name;
630
        $name = $name === 'afterSend' ? 'afterRun' : $name;
631
        $name = $name === 'beforeAgentSend' ? 'beforeDriverRun' : $name;
632
        $name = $name === 'afterAgentSend' ? 'afterDriverRun' : $name;
633
        if (in_array($name, self::$availableHooks)) {
634
            $handler = $args[0];
635
            $override = isset($args[1]) ? (bool) $args[1] : false;
636
            if (is_callable($handler)) {
637
                $task = self::getTask();
638
                $task->hook($name, $handler, $override);
639 9
            } else {
640
                throw new PhpSmsException("Please give method $name() a callable parameter");
641 9
            }
642 9
        } else {
643 9
            throw new PhpSmsException("Dont find method $name()");
644 9
        }
645 9
    }
646 9
647 9
    /**
648 9
     * Overload method.
649 9
     *
650 9
     * @param string $name
651 6
     * @param array  $args
652 3
     *
653
     * @throws PhpSmsException
654 6
     * @throws \Exception
655
     */
656
    public function __call($name, $args)
657 9
    {
658
        try {
659
            $this->__callStatic($name, $args);
660
        } catch (\Exception $e) {
661
            throw $e;
662
        }
663
    }
664
665
    /**
666
     * Serialize magic method.
667
     *
668 3
     * @return array
669
     */
670
    public function __sleep()
671 3
    {
672 2
        try {
673
            $this->_status_before_enqueue_['scheme'] = self::serializeOrDeserializeScheme(self::scheme());
674
            $this->_status_before_enqueue_['agentsConfig'] = self::config();
675 3
            $this->_status_before_enqueue_['handlers'] = self::serializeHandlers();
676
        } catch (\Exception $e) {
677
            //swallow exception
678
        }
679
680
        return ['pushedToQueue', 'smsData', 'firstAgent', '_status_before_enqueue_'];
681
    }
682
683 3
    /**
684
     * Deserialize magic method.
685
     */
686 3
    public function __wakeup()
687 3
    {
688 3
        if (empty($this->_status_before_enqueue_)) {
689 2
            return;
690
        }
691
        $status = $this->_status_before_enqueue_;
692
        self::$scheme = self::serializeOrDeserializeScheme($status['scheme']);
693 3
        self::$agentsConfig = $status['agentsConfig'];
694
        Balancer::destroy(self::TASK);
695
        self::bootstrap();
696
        self::reinstallHandlers($status['handlers']);
697
    }
698
699
    /**
700 3
     * Get a closure serializer.
701
     *
702 3
     * @return Serializer
703
     */
704
    protected static function getSerializer()
705 3
    {
706 3
        if (!self::$serializer) {
707 3
            self::$serializer = new Serializer();
708 3
        }
709 3
710 3
        return self::$serializer;
711 3
    }
712
713
    /**
714
     * Serialize or deserialize the agent use scheme.
715
     *
716
     * @param array $scheme
717
     *
718 3
     * @return array
719
     */
720 3
    protected static function serializeOrDeserializeScheme(array $scheme)
721 3
    {
722 2
        foreach ($scheme as $name => &$options) {
723
            if (is_array($options)) {
724 3
                self::serializeOrDeserializeClosureAndReplace($options, 'sendSms');
725
                self::serializeOrDeserializeClosureAndReplace($options, 'voiceVerify');
726
            }
727
        }
728
729
        return $scheme;
730
    }
731
732 3
    /**
733
     * Serialize the hooks` handlers of balancing task
734 3
     *
735 3
     * @return array
736 3
     */
737 3
    protected static function serializeHandlers()
738 3
    {
739 2
        $task = self::getTask();
740 2
        $hooks = $task->handlers;
741
        foreach ($hooks as &$handlers) {
742 3
            foreach (array_keys($handlers) as $key) {
743
                self::serializeOrDeserializeClosureAndReplace($handlers, $key);
744
            }
745
        }
746
747
        return $hooks;
748
    }
749
750
    /**
751
     * Reinstall hooks` handlers for balancing task.
752 3
     *
753
     * @param array $handlers
754 3
     */
755 3
    protected static function reinstallHandlers(array $handlers)
756 3
    {
757 3
        $serializer = self::getSerializer();
758 2
        foreach ($handlers as $hookName => $serializedHandlers) {
759 2
            foreach ($serializedHandlers as $index => $handler) {
760
                if (is_string($handler)) {
761 3
                    $handler = $serializer->unserialize($handler);
762
                }
763
                self::$hookName($handler, $index === 0);
764
            }
765
        }
766
    }
767
768
    /**
769
     * Serialize/deserialize the specified closure and replace the origin value.
770 3
     *
771
     * @param array      $options
772 3
     * @param int|string $key
773 3
     */
774 3
    protected static function serializeOrDeserializeClosureAndReplace(array &$options, $key)
775 2
    {
776 3
        if (!isset($options[$key])) {
777
            return;
778
        }
779
        $serializer = self::getSerializer();
780
        if (is_callable($options[$key])) {
781
            $options[$key] = (string) $serializer->serialize($options[$key]);
782
        } elseif (is_string($options[$key])) {
783
            $options[$key] = $serializer->unserialize($options[$key]);
784 3
        }
785
    }
786
}
787