Passed
Pull Request — master (#282)
by Viktor
11:16
created

EventConfigurator::isCallable()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

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