Passed
Pull Request — master (#22)
by Viktor
01:47
created

ListenerConfigurationChecker::check()   B

Complexity

Conditions 7
Paths 8

Size

Total Lines 31
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 7

Importance

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