Passed
Push — master ( 3914e6...19d311 )
by Alexander
01:36
created

EventDispatcherProvider::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
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
use Psr\EventDispatcher\EventDispatcherInterface;
9
use Psr\EventDispatcher\ListenerProviderInterface;
10
use Yiisoft\Di\Container;
11
use Yiisoft\Di\Support\ServiceProvider;
12
use Yiisoft\EventDispatcher\Dispatcher\Dispatcher;
13
use Yiisoft\EventDispatcher\Provider\ListenerCollection;
0 ignored issues
show
Bug introduced by
The type Yiisoft\EventDispatcher\...ider\ListenerCollection was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use Yiisoft\EventDispatcher\Provider\Provider;
15
use Yiisoft\Injector\Injector;
16
17
final class EventDispatcherProvider extends ServiceProvider
18
{
19
    private array $eventListeners;
20
21
    /**
22
     * @param array $eventListeners Event listener list in format ['eventName1' => [$listener1, $listener2, ...]]
23
     */
24 2
    public function __construct(array $eventListeners)
25
    {
26 2
        $this->eventListeners = $eventListeners;
27 2
    }
28
29
    /**
30
     * @suppress PhanAccessMethodProtected
31
     */
32 2
    public function register(Container $container): void
33
    {
34 2
        $listenerCollection = new ListenerCollection();
35
36 2
        $injector = new Injector($container);
37
38 2
        foreach ($this->eventListeners as $eventName => $listeners) {
39 2
            if (!is_string($eventName)) {
40
                throw new InvalidEventConfigurationFormatException(
41
                    'Incorrect event listener format. Format with event name must be used.'
42
                );
43
            }
44
45 2
            if (!is_array($listeners)) {
46
                $type = $this->isCallable($listeners, $container) ? 'callable' : gettype($listeners);
47
48
                throw new InvalidEventConfigurationFormatException(
49
                    "Event listeners for $eventName must be an array, $type given."
50
                );
51
            }
52
53 2
            foreach ($listeners as $callable) {
54
                try {
55 2
                    if (!$this->isCallable($callable, $container)) {
56
                        $type = gettype($listeners);
57
58
                        throw new InvalidListenerConfigurationException(
59 2
                            "Listener must be a callable. $type given."
60
                        );
61
                    }
62
                } catch (ContainerExceptionInterface $exception) {
63
                    throw new InvalidListenerConfigurationException(
64
                        "Could not instantiate event listener or listener class has invalid configuration.",
65
                        0,
66
                        $exception
67
                    );
68
                }
69
70 2
                $listener = static function (object $event) use ($injector, $callable, $container) {
71 1
                    if (is_array($callable) && !is_object($callable[0])) {
72
                        $callable = [$container->get($callable[0]), $callable[1]];
73
                    }
74
75 1
                    return $injector->invoke($callable, [$event]);
76 2
                };
77 2
                $listenerCollection = $listenerCollection->add($listener, $eventName);
78
            }
79
        }
80
81 2
        $provider = new Provider($listenerCollection);
0 ignored issues
show
Unused Code introduced by
The call to Yiisoft\EventDispatcher\...Provider::__construct() has too many arguments starting with $listenerCollection. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

81
        $provider = /** @scrutinizer ignore-call */ new Provider($listenerCollection);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
82
83
        $container->set(ListenerProviderInterface::class, $provider);
84
        $container->set(EventDispatcherInterface::class, Dispatcher::class);
85 2
    }
86
87 2
    private function isCallable($definition, Container $container): bool
88
    {
89 2
        if (is_callable($definition)) {
90 2
            return true;
91
        }
92
93
        if (
94 1
            is_array($definition)
95 1
            && array_keys($definition) === [0, 1]
96 1
            && is_string($definition[0])
97 1
            && $container->has($definition[0])
98
        ) {
99 1
            $object = $container->get($definition[0]);
100
101 1
            return method_exists($object, $definition[1]);
102
        }
103
104
        return false;
105
    }
106
}
107