Passed
Pull Request — master (#22)
by Alexander
02:04
created

ListenerConfigurationChecker   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 93
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 40
c 1
b 0
f 0
dl 0
loc 93
ccs 41
cts 41
cp 1
rs 10
wmc 19

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
B isCallable() 0 36 11
B check() 0 31 7
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 ReflectionException;
10
use ReflectionMethod;
11
use function is_array;
12
use function is_callable;
13
use function is_string;
14
15
final class ListenerConfigurationChecker
16
{
17
    private ContainerInterface $container;
18
19 14
    public function __construct(ContainerInterface $container)
20
    {
21 14
        $this->container = $container;
22 14
    }
23
24
    /**
25
     * Checks the given event configuration and throws an exception in some cases:
26
     * - incorrect configuration format
27
     * - incorrect listener format
28
     * - listener is not a callable
29
     * - listener is meant to be a method of an object which can't be instantiated
30
     *
31
     * @param array $configuration An array in format of [eventClassName => [listeners]]
32
     *
33
     * @psalm-suppress InvalidCatch
34
     */
35 14
    public function check(array $configuration): void
36
    {
37 14
        foreach ($configuration as $eventName => $listeners) {
38 14
            if (!is_string($eventName)) {
39 1
                throw new InvalidEventConfigurationFormatException(
40 1
                    'Incorrect event listener format. Format with event name must be used.'
41
                );
42
            }
43
44 13
            if (!is_iterable($listeners)) {
45 1
                $type = gettype($listeners);
46
47 1
                throw new InvalidEventConfigurationFormatException(
48 1
                    "Event listeners for $eventName must be an iterable, $type given."
49
                );
50
            }
51
52 12
            foreach ($listeners as $listener) {
53
                try {
54 12
                    if (!$this->isCallable($listener)) {
55 6
                        $type = gettype($listener);
56
57 6
                        throw new InvalidListenerConfigurationException(
58 11
                            "Listener must be a callable, $type given."
59
                        );
60
                    }
61 7
                } catch (ContainerExceptionInterface $exception) {
62 1
                    throw new InvalidListenerConfigurationException(
63 1
                        'Could not instantiate event listener or listener class has invalid configuration.',
64 1
                        0,
65
                        $exception
66
                    );
67
                }
68
            }
69
        }
70 5
    }
71
72 12
    private function isCallable($definition): bool
73
    {
74
        if (
75 12
            is_array($definition)
76 12
            && array_keys($definition) === [0, 1]
77 12
            && is_string($definition[0])
78
        ) {
79 6
            if (class_exists($definition[0])) {
80
                try {
81 5
                    $method = new ReflectionMethod($definition[0], $definition[1]);
82 4
                    if ($method->isStatic()) {
83 4
                        return true;
84
                    }
85 1
                } catch (ReflectionException $exception) {
86 1
                    return false;
87
                }
88
            }
89
90 4
            if ($this->container->has($definition[0])) {
91 2
                $object = $this->container->get($definition[0]);
92
93 1
                return method_exists($object, $definition[1]);
94
            }
95
96 2
            return false;
97
        }
98
99 6
        if (is_callable($definition)) {
100 2
            return true;
101
        }
102
103 4
        if (is_string($definition) && $this->container->has($definition)) {
104 1
            return is_callable($this->container->get($definition));
105
        }
106
107 3
        return false;
108
    }
109
}
110