ChannelComposite::onInput()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Dazzle\Channel;
4
5
use Dazzle\Channel\Protocol\Protocol;
6
use Dazzle\Channel\Protocol\ProtocolInterface;
7
use Dazzle\Channel\Router\RouterCompositeInterface;
8
use Dazzle\Event\BaseEventEmitter;
9
use Dazzle\Event\EventListener;
10
use Dazzle\Loop\LoopAwareTrait;
11
use Dazzle\Loop\LoopInterface;
12
use Dazzle\Throwable\Exception\Logic\ResourceOccupiedException;
13
use Dazzle\Throwable\Exception\Logic\ResourceUndefinedException;
14
use Dazzle\Util\Support\GeneratorSupport;
15
use Dazzle\Util\Support\TimeSupport;
16
17
class ChannelComposite extends BaseEventEmitter implements ChannelCompositeInterface
18
{
19
    use LoopAwareTrait;
20
21
    /**
22
     * @var string
23
     */
24
    protected $name;
25
26
    /**
27
     * @var ChannelInterface[]|ChannelCompositeInterface[]
28
     */
29
    protected $buses;
30
31
    /**
32
     * @var RouterCompositeInterface
33
     */
34
    protected $router;
35
36
    /**
37
     * @var EventListener[][]
38
     */
39
    protected $events;
40
41
    /**
42
     * @var string
43
     */
44
    protected $seed;
45
46
    /**
47
     * @var int
48
     */
49
    protected $counter;
50
51
    /**
52
     * @param string $name
53
     * @param ChannelInterface[]|ChannelCompositeInterface[] $buses
54
     * @param RouterCompositeInterface $router
55
     * @param LoopInterface $loop
56
     */
57 79
    public function __construct($name, $buses = [], RouterCompositeInterface $router, LoopInterface $loop)
58
    {
59 79
        $this->name = $name;
60 79
        $this->buses = [];
61 79
        $this->router = $router;
62 79
        $this->loop = $loop;
63 79
        $this->events = [];
64 79
        $this->seed = GeneratorSupport::genId($this->name);
65 79
        $this->counter = 1e9;
0 ignored issues
show
Documentation Bug introduced by
The property $counter was declared of type integer, but 1000000000.0 is of type double. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
66
67 79
        foreach ($buses as $name=>$channel)
68
        {
69 17
            $this->setBus($name, $channel);
70
        }
71 79
    }
72
73
    /**
74
     *
75
     */
76 2
    public function __destruct()
77
    {
78 2
        foreach ($this->buses as $name=>$channel)
79
        {
80
            $this->removeBus($name);
81
        }
82
83 2
        unset($this->name);
84 2
        unset($this->buses);
85 2
        unset($this->router);
86 2
        unset($this->events);
87 2
        unset($this->seed);
88 2
        unset($this->counter);
89 2
        unset($this->loop);
90 2
    }
91
92
    /**
93
     * @override
94
     * @inheritDoc
95
     */
96 3
    public function existsBus($name)
97
    {
98 3
        return isset($this->buses[$name]);
99
    }
100
101
    /**
102
     * @override
103
     * @inheritDoc
104
     */
105 2 View Code Duplication
    public function getBus($name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
106
    {
107 2
        if (!isset($this->buses[$name]))
108
        {
109 1
            throw new ResourceUndefinedException(sprintf("Channel [%s] has no registered bus [$name].", $this->getName()));
110
        }
111
112 1
        return $this->buses[$name];
113
    }
114
115
    /**
116
     * @override
117
     * @inheritDoc
118
     */
119 18
    public function setBus($name, $channel)
120
    {
121 18
        if (isset($this->buses[$name]))
122
        {
123 1
            throw new ResourceOccupiedException(sprintf("Channel [%s] has already registered bus [$name].", $this->getName()));
124
        }
125
126 18
        $this->buses[$name] = $channel;
127 18
        $this->events[$name] = $channel->copyEvents($this, [ 'connect', 'disconnect' ]);
128 18
        $this->events[$name][] = $channel->on('input', [ $this, 'handleReceive' ]);
129
130 18
        return $this;
131
    }
132
133
    /**
134
     * @override
135
     * @inheritDoc
136
     */
137 2
    public function removeBus($name)
138
    {
139 2
        if (isset($this->buses[$name]))
140
        {
141 1
            foreach ($this->events[$name] as $handler)
142
            {
143 1
                $handler->cancel();
144
            }
145
146 1
            unset($this->buses[$name]);
147 1
            unset($this->events[$name]);
148
        }
149
150 2
        return $this;
151
    }
152
153
    /**
154
     * @override
155
     * @inheritDoc
156
     */
157 2
    public function getBuses()
158
    {
159 2
        return $this->buses;
160
    }
161
162
    /**
163
     * @override
164
     * @inheritDoc
165
     */
166 3
    public function getName()
167
    {
168 3
        return $this->name;
169
    }
170
171
    /**
172
     * @override
173
     * @inheritDoc
174
     */
175 1
    public function getModel()
176
    {
177 1
        return null;
178
    }
179
180
    /**
181
     * @override
182
     * @inheritDoc
183
     */
184 1
    public function getRouter()
185
    {
186 1
        return $this->router;
187
    }
188
189
    /**
190
     * @override
191
     * @inheritDoc
192
     */
193 1
    public function getInput()
194
    {
195 1
        return $this->router->getBus('input');
196
    }
197
198
    /**
199
     * @override
200
     * @inheritDoc
201
     */
202 1
    public function getOutput()
203
    {
204 1
        return $this->router->getBus('output');
205
    }
206
207
    /**
208
     * @override
209
     * @inheritDoc
210
     */
211 4 View Code Duplication
    public function createProtocol($message = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
212
    {
213 4
        if ($message === null)
214
        {
215 2
            $message = '';
216
        }
217 2
        else if (!is_array($message))
218
        {
219 2
            $message = (string) $message;
220
        }
221
222 4
        return new Protocol('', $this->genID(), '', '', $message);
0 ignored issues
show
Bug introduced by
It seems like $message defined by parameter $message on line 211 can also be of type array; however, Dazzle\Channel\Protocol\Protocol::__construct() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
223
    }
224
225
    /**
226
     * @override
227
     * @inheritDoc
228
     */
229 1
    public function onStart(callable $handler)
230
    {
231 1
        return $this->on('start', $handler);
232
    }
233
234
    /**
235
     * @override
236
     * @inheritDoc
237
     */
238 1
    public function onStop(callable $handler)
239
    {
240 1
        return $this->on('stop', $handler);
241
    }
242
243
    /**
244
     * @override
245
     * @inheritDoc
246
     */
247 1
    public function onConnect(callable $handler)
248
    {
249 1
        return $this->on('connect', $handler);
250
    }
251
252
    /**
253
     * @override
254
     * @inheritDoc
255
     */
256 1
    public function onDisconnect(callable $handler)
257
    {
258 1
        return $this->on('disconnect', $handler);
259
    }
260
261
    /**
262
     * @override
263
     * @inheritDoc
264
     */
265 1
    public function onInput(callable $handler)
266
    {
267 1
        return $this->on('input', $handler);
268
    }
269
270
    /**
271
     * @override
272
     * @inheritDoc
273
     */
274 1
    public function onOutput(callable $handler)
275
    {
276 1
        return $this->on('output', $handler);
277
    }
278
279
    /**
280
     * @override
281
     * @inheritDoc
282
     */
283 1
    public function start()
284
    {
285 1
        foreach ($this->buses as $channel)
286
        {
287 1
            $channel->start();
288
        }
289
290 1
        $this->emit('start');
291 1
    }
292
293
    /**
294
     * @override
295
     * @inheritDoc
296
     */
297 1
    public function stop()
298
    {
299 1
        foreach ($this->buses as $channel)
300
        {
301 1
            $channel->stop();
302
        }
303
304 1
        $this->emit('stop');
305 1
    }
306
307
    /**
308
     * @override
309
     * @inheritDoc
310
     */
311 2 View Code Duplication
    public function send($name, $message, $flags = Channel::MODE_DEFAULT, callable $success = null, callable $failure = null, callable $cancel = null, $timeout = 0.0)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
312
    {
313 2
        if ($success !== null || $failure !== null || $cancel !== null)
314
        {
315 1
            return $this->sendRequest($name, $message, $flags, $success, $failure, $cancel, $timeout);
316
        }
317
318 1
        return $this->sendAsync($name, $message, $flags);
319
    }
320
321
    /**
322
     * @override
323
     * @inheritDoc
324
     */
325 2 View Code Duplication
    public function push($name, $message, $flags = Channel::MODE_DEFAULT, callable $success = null, callable $failure = null, callable $cancel = null, $timeout = 0.0)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
326
    {
327 2
        if ($success !== null || $failure !== null || $cancel !== null)
328
        {
329 1
            return $this->pushRequest($name, $message, $flags, $success, $failure, $cancel, $timeout);
330
        }
331
332 1
        return $this->pushAsync($name, $message, $flags);
333
    }
334
335
    /**
336
     * @override
337
     * @inheritDoc
338
     */
339 5 View Code Duplication
    public function sendAsync($name, $message, $flags = Channel::MODE_DEFAULT)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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
    {
341 5
        $message = $this->createMessageProtocol($message);
342
343 5
        $names = (array) $name;
344 5
        $status = [];
345 5
        foreach ($names as $name)
346
        {
347 4
            $status[] = $this->handleSendAsync($name, $message, $flags);
348
        }
349
350 5
        return !isset($status[0]) || isset($status[1]) ? $status : $status[0];
351
    }
352
353
    /**
354
     * @override
355
     * @inheritDoc
356
     */
357 5 View Code Duplication
    public function pushAsync($name, $message, $flags = Channel::MODE_DEFAULT)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
358
    {
359 5
        $message = $this->createMessageProtocol($message);
360
361 5
        $names = (array) $name;
362 5
        $status = [];
363 5
        foreach ($names as $name)
364
        {
365 4
            $status[] = $this->handlePushAsync($name, $message, $flags);
366
        }
367
368 5
        return !isset($status[0]) || isset($status[1]) ? $status : $status[0];
369
    }
370
371
    /**
372
     * @override
373
     * @inheritDoc
374
     */
375 5 View Code Duplication
    public function sendRequest($name, $message, $flags = Channel::MODE_DEFAULT, callable $success = null, callable $failure = null, callable $cancel = null, $timeout = 0.0)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
376
    {
377 5
        $message = $this->createMessageProtocol($message);
378
379 5
        $names = (array) $name;
380 5
        $status = [];
381 5
        foreach ($names as $name)
382
        {
383 4
            $status[] = $this->handleSendRequest($name, $message, $flags, $success, $failure, $cancel, $timeout);
384
        }
385
386 5
        return !isset($status[0]) || isset($status[1]) ? $status : $status[0];
387
    }
388
389
    /**
390
     * @override
391
     * @inheritDoc
392
     */
393 5 View Code Duplication
    public function pushRequest($name, $message, $flags = Channel::MODE_DEFAULT, callable $success = null, callable $failure = null, callable $cancel = null, $timeout = 0.0)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
394
    {
395 5
        $message = $this->createMessageProtocol($message);
396
397 5
        $names = (array) $name;
398 5
        $status = [];
399 5
        foreach ($names as $name)
400
        {
401 4
            $status[] = $this->handlePushRequest($name, $message, $flags, $success, $failure, $cancel, $timeout);
402
        }
403
404 5
        return !isset($status[0]) || isset($status[1]) ? $status : $status[0];
405
    }
406
407
    /**
408
     * @override
409
     * @inheritDoc
410
     */
411 1
    public function receive($sender, ProtocolInterface $protocol)
412
    {
413 1
        if ($this->getInput()->handle($sender, $protocol))
414
        {
415 1
            $this->emit('input', [ $sender, $protocol ]);
416
        }
417 1
    }
418
419
    /**
420
     * @override
421
     * @inheritDoc
422
     */
423 1
    public function pull($sender, ProtocolInterface $protocol)
424
    {
425 1
        $this->emit('input', [ $sender, $protocol ]);
426 1
    }
427
428
    /**
429
     * @override
430
     * @inheritDoc
431
     */
432 2
    public function isStarted()
433
    {
434 2
        $statuses = [];
435
436 2
        foreach ($this->buses as $bus=>$channel)
437
        {
438 1
            $statuses[$bus] = $channel->isStarted();
439
        }
440
441 2
        return $statuses;
442
    }
443
444
    /**
445
     * @override
446
     * @inheritDoc
447
     */
448 2
    public function isStopped()
449
    {
450 2
        $statuses = [];
451
452 2
        foreach ($this->buses as $bus=>$channel)
453
        {
454 1
            $statuses[$bus] = $channel->isStopped();
455
        }
456
457 2
        return $statuses;
458
    }
459
460
    /**
461
     * @override
462
     * @inheritDoc
463
     */
464 3
    public function isConnected($name)
465
    {
466 3
        $status = null;
467
468 3
        foreach ($this->buses as $channel)
469
        {
470 2
            $status = $this->combine(
471 2
                $status,
472 2
                $channel->isConnected($name),
473
                function($in, $out) {
474 2
                    return $in || $out;
475 2
                }
476
            );
477
        }
478
479 3
        return $status === null ? false : $status;
480
    }
481
482
    /**
483
     * @override
484
     * @inheritDoc
485
     */
486 2 View Code Duplication
    public function getConnected()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
487
    {
488 2
        $conns = [];
489
490 2
        foreach ($this->buses as $channel)
491
        {
492 1
            $conns = array_merge($conns, $channel->getConnected());
493
        }
494
495 2
        return array_values(array_unique($conns));
496
    }
497
498
    /**
499
     * @override
500
     * @inheritDoc
501
     */
502 2 View Code Duplication
    public function filterConnected($pattern)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
503
    {
504 2
        $conns = [];
505
506 2
        foreach ($this->buses as $channel)
507
        {
508 1
            $conns = array_merge($conns, $channel->filterConnected($pattern));
509
        }
510
511 2
        return array_values(array_unique($conns));
512
    }
513
514
    /**
515
     * @param string $name
516
     * @param ProtocolInterface $message
517
     * @param int $flags
518
     * @return bool
519
     */
520 1 View Code Duplication
    protected function handleSendAsync($name, $message, $flags = Channel::MODE_DEFAULT)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
521
    {
522 1
        if ($message->getType() === '')
523
        {
524 1
            $message->setType(Channel::TYPE_SND);
525
        }
526 1
        if ($message->getDestination() === '')
527
        {
528 1
            $message->setDestination($name);
529
        }
530
531 1
        return $this->getOutput()->handle($name, $message, $flags);
532
    }
533
534
    /**
535
     * @param string $name
536
     * @param ProtocolInterface $message
537
     * @param int $flags
538
     * @return bool
539
     */
540 2
    protected function handlePushAsync($name, $message, $flags = Channel::MODE_DEFAULT)
541
    {
542 2
        $statusArray = null;
543
544 2
        foreach ($this->buses as $channel)
545
        {
546 2
            $statusArray = $this->combine(
547 2
                $statusArray,
548 2
                $channel->push($name, $message, $flags),
549
                function($in, $out) {
550 2
                    return $in || $out;
551 2
                }
552
            );
553
        }
554
555 2
        $statusArray = (array) $statusArray;
556
557 2
        $cnt = 0;
558 2
        $len = count($statusArray);
559
560 2
        foreach ($statusArray as $statusElement)
561
        {
562 2
            if ($statusElement === true)
563
            {
564 2
                $cnt++;
565
            }
566
        }
567
568 2
        $status = ($cnt === $len);
569
570 2
        if ($status)
571
        {
572 1
            $this->emit('output', [ $name, $message ]);
573
        }
574
575 2
        return $status;
576
    }
577
578
    /**
579
     * @param string $name
580
     * @param string|ProtocolInterface $message
581
     * @param int $flags
582
     * @param callable|null $success
583
     * @param callable|null $failure
584
     * @param callable|null $cancel
585
     * @param float $timeout
586
     * @return bool[]
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
587
     */
588 1
    protected function handleSendRequest($name, $message, $flags = Channel::MODE_DEFAULT, callable $success = null, callable $failure = null, callable $cancel = null, $timeout = 0.0)
589
    {
590 1
        return $this->getOutput()->handle($name, $message, $flags, $success, $failure, $cancel, $timeout);
0 ignored issues
show
Bug introduced by
It seems like $message defined by parameter $message on line 588 can also be of type string; however, Dazzle\Channel\Router\RouterInterface::handle() does only seem to accept object<Dazzle\Channel\Protocol\ProtocolInterface>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
591
    }
592
593
    /**
594
     * @param string $name
595
     * @param string|ProtocolInterface $message
596
     * @param int $flags
597
     * @param callable|null $success
598
     * @param callable|null $failure
599
     * @param callable|null $cancel
600
     * @param float $timeout
601
     * @return bool
602
     */
603 2
    protected function handlePushRequest($name, $message, $flags = Channel::MODE_DEFAULT, callable $success = null, callable $failure = null, callable $cancel = null, $timeout = 0.0)
604
    {
605 2
        $statusArray = null;
606
607 2
        foreach ($this->buses as $channel)
608
        {
609 2
            $statusArray = $this->combine(
610 2
                $statusArray,
611 2
                $channel->push($name, $message, $flags, $success, $failure, $cancel, $timeout),
612 2
                function($in, $out) {
613 2
                    return $in || $out;
614 2
                }
615
            );
616
        }
617
618 2
        $statusArray = (array) $statusArray;
619
620 2
        $cnt = 0;
621 2
        $len = count($statusArray);
622
623 2
        foreach ($statusArray as $statusElement)
624
        {
625 2
            if ($statusElement === true)
626
            {
627 2
                $cnt++;
628
            }
629
        }
630
631 2
        $status = ($cnt === $len);
632
633 2
        if ($status)
634
        {
635 1
            $this->emit('output', [ $name, $message ]);
636
        }
637
638 2
        return $status;
639
    }
640
641
    /**
642
     * @internal
643
     * @param string $sender
644
     * @param ProtocolInterface $protocol
645
     */
646
    public function handleReceive($sender, ProtocolInterface $protocol)
647
    {
648
        $this->getInput()->handle($sender, $protocol);
649
    }
650
651
    /**
652
     * @return string
653
     */
654 7
    protected function genID()
655
    {
656 7
        return $this->seed . $this->getNextSuffix();
657
    }
658
659
    /**
660
     * @return float
661
     */
662 5
    protected function getTime()
663
    {
664 5
        return TimeSupport::now();
665
    }
666
667
    /**
668
     * @return int
0 ignored issues
show
Documentation introduced by
Should the return type not be string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
669
     */
670 9 View Code Duplication
    protected function getNextSuffix()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
671
    {
672 9
        if ($this->counter > 2e9)
673
        {
674 1
            $this->counter = 1e9;
0 ignored issues
show
Documentation Bug introduced by
The property $counter was declared of type integer, but 1000000000.0 is of type double. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
675 1
            $this->seed = GeneratorSupport::genId($this->name);
676
        }
677
678 9
        return (string) $this->counter++;
679
    }
680
681
    /**
682
     * @param ProtocolInterface|null|string|string[] $message
683
     * @return ProtocolInterface
684
     */
685 5
    protected function createMessageProtocol($message)
686
    {
687 5
        if (!($message instanceof ProtocolInterface))
688
        {
689 2
            $message = $this->createProtocol($message);
690
        }
691
692 5
        if ($message->getPid() === '')
693
        {
694 2
            $message->setPid($this->genID());
695
        }
696 5
        if ($message->getTimestamp() == 0)
697
        {
698 4
            $message->setTimestamp($this->getTime());
699
        }
700
701 5
        return $message;
702
    }
703
704
    /**
705
     * @param mixed|mixed[]|null $in
706
     * @param mixed|mixed[]|null $out
707
     * @param callable $combinator
708
     * @return mixed|mixed[]
709
     */
710 6
    private function combine($in, $out, callable $combinator)
711
    {
712 6
        if ($in === null)
713
        {
714 6
            return $out;
715
        }
716
717 6
        if ($out === null)
718
        {
719
            return $in;
720
        }
721
722 6
        if (!is_array($in))
723
        {
724 6
            return $combinator($in, $out);
725
        }
726
727
        $result = [];
728
729
        foreach ($in as $index=>$status)
730
        {
731
            $result[$index] = $combinator($in[$index], $out[$index]);
732
        }
733
734
        return $result;
735
    }
736
}
737