Completed
Pull Request — master (#56)
by lan tian
02:24
created

Sms::cleanAgentsConfig()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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