Passed
Pull Request — master (#33)
by Sergei
03:39
created

createNotCallableMessage()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 9
nc 4
nop 1
dl 0
loc 15
ccs 10
cts 10
cp 1
crap 7
rs 8.8333
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Yii\Event;
6
7
use Psr\Container\ContainerExceptionInterface;
8
9
use function get_class;
10
use function gettype;
11
use function is_array;
12
use function is_object;
13
use function is_string;
14
15
/**
16
 * ListenerConfigurationChecker could be used in development mode to check if listeners are defined correctly.
17
 *
18
 * ```php
19
 * $checker->check($configuration->get('events-web'));
20
 * ```
21
 */
22
final class ListenerConfigurationChecker
23
{
24
    private CallableFactory $callableFactory;
25
26 20
    public function __construct(CallableFactory $callableFactory)
27
    {
28 20
        $this->callableFactory = $callableFactory;
29 20
    }
30
31
    /**
32
     * Checks the given event configuration and throws an exception in some cases:
33
     * - incorrect configuration format
34
     * - incorrect listener format
35
     * - listener is not a callable
36
     * - listener is meant to be a method of an object which can't be instantiated
37
     *
38
     * @param array $configuration An array in format of [eventClassName => [listeners]]
39
     */
40 20
    public function check(array $configuration): void
41
    {
42 20
        foreach ($configuration as $eventName => $listeners) {
43 20
            if (!is_string($eventName) || !class_exists($eventName)) {
44 1
                throw new InvalidEventConfigurationFormatException(
45
                    'Incorrect event listener format. Format with event name must be used. Got ' .
46 1
                    var_export($eventName, true) . '.'
47
                );
48
            }
49
50 19
            if (!is_iterable($listeners)) {
51 1
                $type = is_object($listeners) ? get_class($listeners) : gettype($listeners);
52
53 1
                throw new InvalidEventConfigurationFormatException(
54 1
                    "Event listeners for $eventName must be an iterable, $type given."
55
                );
56
            }
57
58
            /** @var mixed */
59 18
            foreach ($listeners as $listener) {
60
                try {
61 18
                    if (!$this->isCallable($listener)) {
62 10
                        throw new InvalidListenerConfigurationException(
63 17
                            $this->createNotCallableMessage($listener)
64
                        );
65
                    }
66 11
                } catch (ContainerExceptionInterface $exception) {
67 1
                    throw new InvalidListenerConfigurationException(
68 1
                        'Could not instantiate event listener or listener class has invalid configuration. Got ' .
69 1
                        $this->listenerDump($listener) . '.',
70 1
                        0,
71
                        $exception
72
                    );
73
                }
74
            }
75
        }
76 7
    }
77
78
    /**
79
     * @param mixed $definition
80
     */
81 10
    private function createNotCallableMessage($definition): string
82
    {
83 10
        if (is_array($definition)
84 10
            && array_keys($definition) === [0, 1]
85 10
            && is_string($definition[1])
86
        ) {
87 6
            if (is_string($definition[0]) && class_exists($definition[0])) {
88 2
                return $definition[0] . ' could not instantiate or method "' .
89 2
                    $definition[1] . '" not exists in him.';
90
            }
91 4
            if (is_object($definition[0])) {
92 1
                return 'Method "' . $definition[1] . '" not exists in class ' . get_class($definition[0]) . '.';
93
            }
94
        }
95 7
        return 'Listener must be a callable. Got ' . $this->listenerDump($definition) . '.';
96
    }
97
98
    /**
99
     * @param mixed $definition
100
     *
101
     * @throws ContainerExceptionInterface Error while retrieving the entry from container.
102
     */
103 18
    private function isCallable($definition): bool
104
    {
105
        try {
106 18
            $this->callableFactory->create($definition);
107 11
        } catch (InvalidListenerConfigurationException $e) {
108 10
            return false;
109
        }
110
111 7
        return true;
112
    }
113
114
    /**
115
     * @param mixed $listener
116
     */
117 8
    private function listenerDump($listener): string
118
    {
119 8
        return is_object($listener) ? get_class($listener) : var_export($listener, true);
120
    }
121
}
122