Passed
Push — master ( f52ab1...b28f2e )
by Alexey
02:44
created

src/Event/EventDispatcher/SyncEventDispatcher.php (1 issue)

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 Webmozart\Assert\Assert;
43
44
/**
45
 * @internal
46
 */
47
final class SyncEventDispatcher implements EventDispatcher
48
{
49
    /**
50
     * @var callable[][]
51
     */
52
    private $listeners = [];
53
54
    public function dispatch(object $event): void
55
    {
56
        $name = get_class($event);
57
58
        foreach ($this->getListeners($name) as $listener) {
59
            $listener($event);
60
        }
61
    }
62
63
    public function addSubscriber(EventSubscriber $eventSubscriber): void
64
    {
65
        foreach ($this->inferSubscribedEvents($eventSubscriber) as $eventName => $listener) {
66
            $this->listeners[$eventName][] = $listener;
67
        }
68
    }
69
70
    /**
71
     * @return iterable|callable[]
72
     */
73
    private function inferSubscribedEvents(EventSubscriber $eventSubscriber): iterable
74
    {
75
        $class = new ReflectionClass($eventSubscriber);
76
        $methods = $class->getMethods(ReflectionMethod::IS_PUBLIC);
77
78
        foreach ($methods as $method) {
79
            /** @var ReflectionMethod $method */
80
            if ($method->isConstructor()) {
81
                continue;
82
            }
83
84
            foreach ($method->getParameters() as $param) {
85
                $paramClass = $param->getClass();
86
                Assert::notNull($paramClass);
87
88
                $closure = $method->getClosure($eventSubscriber);
89
                Assert::notNull($closure);
90
91
                // Returning a closure instead of array [$eventSubscriber, $method->name], should work the same
92
                yield $paramClass->name => $closure;
0 ignored issues
show
Bug Best Practice introduced by
The expression yield $paramClass->name => $closure returns the type Generator which is incompatible with the documented return type array<mixed,callable>|iterable.
Loading history...
93
94
                break;
95
            }
96
        }
97
    }
98
99
    /**
100
     * @return callable[]
101
     */
102
    private function getListeners(string $eventName): array
103
    {
104
        return $this->listeners[$eventName] ?? [];
105
    }
106
}
107