Passed
Push — master ( 231cbb...ec2252 )
by Alexander
01:20
created

ListenerCollectionFactory::isCallable()   A

Complexity

Conditions 6
Paths 3

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 6.972

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 10
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 18
ccs 7
cts 10
cp 0.7
crap 6.972
rs 9.2222
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Event;
6
7
use Psr\Container\ContainerExceptionInterface;
8
use Psr\Container\ContainerInterface;
9
use Yiisoft\EventDispatcher\Provider\ListenerCollection;
10
use Yiisoft\Injector\Injector;
11
12
final class ListenerCollectionFactory
13
{
14
    private Injector $injector;
15
    private ContainerInterface $container;
16
17 6
    public function __construct(Injector $injector, ContainerInterface $container)
18
    {
19 6
        $this->injector = $injector;
20 6
        $this->container = $container;
21 6
    }
22
23
    /**
24
     * @param array $eventListeners Event listener list in format ['eventName1' => [$listener1, $listener2, ...]]
25
     * @param bool $precheck If true, all the listeners will be checked strictly if they are valid callables
26
     *                       before attaching to the ListenerCollection
27
     *
28
     * @return ListenerCollection
29
     */
30 6
    public function create(array $eventListeners, bool $precheck = false): ListenerCollection
31
    {
32 6
        $listenerCollection = new ListenerCollection();
33
34 6
        foreach ($eventListeners as $eventName => $listeners) {
35 6
            if (!is_string($eventName)) {
36 1
                throw new InvalidEventConfigurationFormatException(
37 1
                    'Incorrect event listener format. Format with event name must be used.'
38
                );
39
            }
40
41 5
            if (!is_array($listeners)) {
42 1
                $type = $this->isCallable($listeners) ? 'callable' : gettype($listeners);
43
44 1
                throw new InvalidEventConfigurationFormatException(
45 1
                    "Event listeners for $eventName must be an array, $type given."
46
                );
47
            }
48
49 4
            foreach ($listeners as $callable) {
50 4
                if ($precheck) {
51
                    /** @psalm-suppress InvalidCatch */
52
                    try {
53 1
                        if (!$this->isCallable($callable)) {
54 1
                            $type = gettype($callable);
55
56 1
                            throw new InvalidListenerConfigurationException(
57 1
                                "Listener must be a callable, $type given."
58
                            );
59
                        }
60 1
                    } catch (ContainerExceptionInterface $exception) {
61
                        throw new InvalidListenerConfigurationException(
62
                            "Could not instantiate event listener or listener class has invalid configuration.",
63
                            0,
64
                            $exception
65
                        );
66
                    }
67
                }
68
69 3
                $listener = function (object $event) use ($callable) {
70 1
                    if (is_array($callable) && !is_object($callable[0])) {
71
                        $callable = [$this->container->get($callable[0]), $callable[1]];
72
                    }
73
74 1
                    return $this->injector->invoke($callable, [$event]);
75 3
                };
76 3
                $listenerCollection = $listenerCollection->add($listener, $eventName);
77
            }
78
        }
79
80 3
        return $listenerCollection;
81
    }
82
83 2
    private function isCallable($definition): bool
84
    {
85 2
        if (is_callable($definition)) {
86
            return true;
87
        }
88
89
        if (
90 2
            is_array($definition)
91 2
            && array_keys($definition) === [0, 1]
92 2
            && is_string($definition[0])
93 2
            && $this->container->has($definition[0])
94
        ) {
95
            $object = $this->container->get($definition[0]);
96
97
            return method_exists($object, $definition[1]);
98
        }
99
100 2
        return false;
101
    }
102
}
103