SyncEventDispatcher   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 58
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 9
eloc 19
c 1
b 0
f 0
dl 0
loc 58
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A addSubscriber() 0 4 2
A getListeners() 0 3 1
A dispatch() 0 6 2
A inferSubscribedEvents() 0 22 4
1
<?php
2
/**
3
 * This code is licensed under the BSD 3-Clause License.
4
 *
5
 * Copyright (c) 2017, Maks Rafalko
6
 * All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions are met:
10
 *
11
 * * Redistributions of source code must retain the above copyright notice, this
12
 *   list of conditions and the following disclaimer.
13
 *
14
 * * Redistributions in binary form must reproduce the above copyright notice,
15
 *   this list of conditions and the following disclaimer in the documentation
16
 *   and/or other materials provided with the distribution.
17
 *
18
 * * Neither the name of the copyright holder nor the names of its
19
 *   contributors may be used to endorse or promote products derived from
20
 *   this software without specific prior written permission.
21
 *
22
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
26
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
 */
33
34
declare(strict_types=1);
35
36
namespace Infection\Event\EventDispatcher;
37
38
use function get_class;
39
use Infection\Event\Subscriber\EventSubscriber;
40
use ReflectionClass;
41
use ReflectionMethod;
42
use ReflectionNamedType;
43
use Webmozart\Assert\Assert;
44
45
/**
46
 * @internal
47
 */
48
final class SyncEventDispatcher implements EventDispatcher
49
{
50
    /**
51
     * @var callable[][]
52
     */
53
    private $listeners = [];
54
55
    public function dispatch(object $event): void
56
    {
57
        $name = get_class($event);
58
59
        foreach ($this->getListeners($name) as $listener) {
60
            $listener($event);
61
        }
62
    }
63
64
    public function addSubscriber(EventSubscriber $eventSubscriber): void
65
    {
66
        foreach ($this->inferSubscribedEvents($eventSubscriber) as $eventName => $listener) {
67
            $this->listeners[$eventName][] = $listener;
68
        }
69
    }
70
71
    /**
72
     * @return iterable|callable[]
73
     */
74
    private function inferSubscribedEvents(EventSubscriber $eventSubscriber): iterable
75
    {
76
        $class = new ReflectionClass($eventSubscriber);
77
        $methods = $class->getMethods(ReflectionMethod::IS_PUBLIC);
78
79
        foreach ($methods as $method) {
80
            /** @var ReflectionMethod $method */
81
            if ($method->isConstructor()) {
82
                continue;
83
            }
84
85
            foreach ($method->getParameters() as $param) {
86
                $paramClass = $param->getType();
87
                Assert::isInstanceOf($paramClass, ReflectionNamedType::class);
88
89
                $closure = $method->getClosure($eventSubscriber);
90
                Assert::notNull($closure);
91
92
                // Returning a closure instead of array [$eventSubscriber, $method->name], should work the same
93
                yield $paramClass->getName() => $closure;
0 ignored issues
show
Bug Best Practice introduced by
The expression yield $paramClass->getName() => $closure returns the type Generator which is incompatible with the documented return type array<mixed,callable>|iterable.
Loading history...
94
95
                break;
96
            }
97
        }
98
    }
99
100
    /**
101
     * @return callable[]
102
     */
103
    private function getListeners(string $eventName): array
104
    {
105
        return $this->listeners[$eventName] ?? [];
106
    }
107
}
108