Issues (27)

src/Application/EventDispatcher.php (1 issue)

Labels
Severity
1
<?php
2
3
/**
4
 * This file is part of slick/event package
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace Slick\Event\Application;
11
12
use Psr\EventDispatcher\ListenerProviderInterface;
13
use Psr\EventDispatcher\StoppableEventInterface;
14
use Slick\Event\Domain\EventGeneratorMethods;
15
use Slick\Event\Event;
16
use Slick\Event\EventDispatcher as SlickEventDispatcher;
17
use Slick\Event\EventGenerator;
18
use Slick\Event\EventListener;
19
20
/**
21
 * EventDispatcher
22
 *
23
 * @package Slick\Event\Application
24
 */
25
final class EventDispatcher implements SlickEventDispatcher, EventGenerator
26
{
27
28
    use EventGeneratorMethods;
29
    use DispatcherTools;
0 ignored issues
show
The trait Slick\Event\Application\DispatcherTools requires the property $priority which is not provided by Slick\Event\Application\EventDispatcher.
Loading history...
30
31
    /**
32
     * @var bool
33
     */
34
    private static bool $dispatching = false;
35
36
    /**
37
     * @var array<string, array{listener: EventListener|callable|ListenerProviderInterface, priority: int}>
38
     */
39
    private array $listeners = [];
40
41
    /**
42
     * Dispatches events form an event generator
43
     *
44
     * @param EventGenerator $generator
45
     * @return Event[] the list of dispatched events
46
     */
47
    public function dispatchEventsFrom(EventGenerator $generator): array
48
    {
49
        $events = [];
50
        foreach ($generator->releaseEvents() as $event) {
51
            $events[] = $this->dispatch($event);
52
        }
53
        return $events;
54
    }
55
56
    /**
57
     * Adds an event listener
58
     *
59
     * @param string $event
60
     * @param EventListener|Callable|ListenerProviderInterface $listener
61
     * @param int $priority
62
     */
63
    public function addListener(string $event, $listener, int $priority = SlickEventDispatcher::P_NORMAL): void
64
    {
65
        $this->listeners[$event][] = (object) [
66
            'listener' => $listener,
67
            'priority' => $priority
68
        ];
69
    }
70
71
    /**
72
     * Provide all relevant listeners with an event to process.
73
     *
74
     * @param object $event
75
     *   The object to process.
76
     *
77
     * @return object
78
     *   The Event that was passed, now modified by listeners.
79
     */
80
    public function dispatch(object $event)
81
    {
82
        if (self::$dispatching) {
83
            $this->recordThat($event);
84
            return $event;
85
        }
86
87
        self::$dispatching = true;
88
89
        foreach ($this->listenersFor($event) as $listener) {
90
            $event = $this->invokeListener($listener, $event);
91
            if ($event instanceof StoppableEventInterface && $event->isPropagationStopped()) {
92
                break;
93
            }
94
        }
95
96
        self::$dispatching = false;
97
        $this->dispatchEventsFrom($this);
98
        return $event;
99
    }
100
101
    /**
102
     * Prioritized list of listeners for provided event
103
     *
104
     * @param object $event
105
     * @return array
106
     */
107
    private function listenersFor(object $event): array
108
    {
109
        $listeners = [];
110
        $unordered = $this->orderedListeners(
111
            $this->matchedListeners($event)
112
        );
113
        foreach ($unordered as $data) {
114
            if ($data->listener instanceof ListenerProviderInterface === false) {
115
                $listeners[] = $data->listener;
116
                continue;
117
            }
118
119
            foreach ($data->listener->getListenersForEvent($event) as $listener) {
120
                $listeners[] = $listener;
121
            }
122
        }
123
        return $listeners;
124
    }
125
126
    /**
127
     * Invoke listener
128
     *
129
     * @param $listener
130
     * @param Object $event
131
     * @return Object
132
     */
133
    private function invokeListener($listener, Object $event): object
134
    {
135
        if (is_callable($listener)) {
136
            return $listener($event);
137
        }
138
139
        if ($listener instanceof  EventListener) {
140
            return $listener->handle($event);
141
        }
142
143
        return $event;
144
    }
145
146
    /**
147
     * Filters the matched listeners
148
     *
149
     * @param object $event
150
     * @return array
151
     */
152
    private function matchedListeners(object $event): array
153
    {
154
        $unordered = [];
155
        foreach ($this->listeners as $pattern => $data) {
156
            if (!$this->match($pattern, $event)) {
157
                continue;
158
            }
159
160
            foreach ($data as $datum) {
161
                $unordered[] = $datum;
162
            }
163
        }
164
        return $unordered;
165
    }
166
}
167