Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <fabien@symfony.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
 
12
namespace Symfony\Component\EventDispatcher;
13
 
14
use Psr\EventDispatcher\StoppableEventInterface;
15
use Symfony\Component\EventDispatcher\Debug\WrappedListener;
16
 
17
/**
18
 * The EventDispatcherInterface is the central point of Symfony's event listener system.
19
 *
20
 * Listeners are registered on the manager and events are dispatched through the
21
 * manager.
22
 *
23
 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
24
 * @author Jonathan Wage <jonwage@gmail.com>
25
 * @author Roman Borschel <roman@code-factory.org>
26
 * @author Bernhard Schussek <bschussek@gmail.com>
27
 * @author Fabien Potencier <fabien@symfony.com>
28
 * @author Jordi Boggiano <j.boggiano@seld.be>
29
 * @author Jordan Alliot <jordan.alliot@gmail.com>
30
 * @author Nicolas Grekas <p@tchwork.com>
31
 */
32
class EventDispatcher implements EventDispatcherInterface
33
{
34
    private array $listeners = [];
35
    private array $sorted = [];
36
    private array $optimized;
37
 
38
    public function __construct()
39
    {
40
        if (__CLASS__ === static::class) {
41
            $this->optimized = [];
42
        }
43
    }
44
 
45
    public function dispatch(object $event, string $eventName = null): object
46
    {
47
        $eventName ??= $event::class;
48
 
49
        if (isset($this->optimized)) {
50
            $listeners = $this->optimized[$eventName] ?? (empty($this->listeners[$eventName]) ? [] : $this->optimizeListeners($eventName));
51
        } else {
52
            $listeners = $this->getListeners($eventName);
53
        }
54
 
55
        if ($listeners) {
56
            $this->callListeners($listeners, $eventName, $event);
57
        }
58
 
59
        return $event;
60
    }
61
 
62
    public function getListeners(string $eventName = null): array
63
    {
64
        if (null !== $eventName) {
65
            if (empty($this->listeners[$eventName])) {
66
                return [];
67
            }
68
 
69
            if (!isset($this->sorted[$eventName])) {
70
                $this->sortListeners($eventName);
71
            }
72
 
73
            return $this->sorted[$eventName];
74
        }
75
 
76
        foreach ($this->listeners as $eventName => $eventListeners) {
77
            if (!isset($this->sorted[$eventName])) {
78
                $this->sortListeners($eventName);
79
            }
80
        }
81
 
82
        return array_filter($this->sorted);
83
    }
84
 
85
    public function getListenerPriority(string $eventName, callable|array $listener): ?int
86
    {
87
        if (empty($this->listeners[$eventName])) {
88
            return null;
89
        }
90
 
91
        if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
92
            $listener[0] = $listener[0]();
93
            $listener[1] ??= '__invoke';
94
        }
95
 
96
        foreach ($this->listeners[$eventName] as $priority => &$listeners) {
97
            foreach ($listeners as &$v) {
98
                if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure && 2 >= \count($v)) {
99
                    $v[0] = $v[0]();
100
                    $v[1] ??= '__invoke';
101
                }
102
                if ($v === $listener || ($listener instanceof \Closure && $v == $listener)) {
103
                    return $priority;
104
                }
105
            }
106
        }
107
 
108
        return null;
109
    }
110
 
111
    public function hasListeners(string $eventName = null): bool
112
    {
113
        if (null !== $eventName) {
114
            return !empty($this->listeners[$eventName]);
115
        }
116
 
117
        foreach ($this->listeners as $eventListeners) {
118
            if ($eventListeners) {
119
                return true;
120
            }
121
        }
122
 
123
        return false;
124
    }
125
 
126
    public function addListener(string $eventName, callable|array $listener, int $priority = 0)
127
    {
128
        $this->listeners[$eventName][$priority][] = $listener;
129
        unset($this->sorted[$eventName], $this->optimized[$eventName]);
130
    }
131
 
132
    public function removeListener(string $eventName, callable|array $listener)
133
    {
134
        if (empty($this->listeners[$eventName])) {
135
            return;
136
        }
137
 
138
        if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
139
            $listener[0] = $listener[0]();
140
            $listener[1] ??= '__invoke';
141
        }
142
 
143
        foreach ($this->listeners[$eventName] as $priority => &$listeners) {
144
            foreach ($listeners as $k => &$v) {
145
                if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure && 2 >= \count($v)) {
146
                    $v[0] = $v[0]();
147
                    $v[1] ??= '__invoke';
148
                }
149
                if ($v === $listener || ($listener instanceof \Closure && $v == $listener)) {
150
                    unset($listeners[$k], $this->sorted[$eventName], $this->optimized[$eventName]);
151
                }
152
            }
153
 
154
            if (!$listeners) {
155
                unset($this->listeners[$eventName][$priority]);
156
            }
157
        }
158
    }
159
 
160
    public function addSubscriber(EventSubscriberInterface $subscriber)
161
    {
162
        foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
163
            if (\is_string($params)) {
164
                $this->addListener($eventName, [$subscriber, $params]);
165
            } elseif (\is_string($params[0])) {
166
                $this->addListener($eventName, [$subscriber, $params[0]], $params[1] ?? 0);
167
            } else {
168
                foreach ($params as $listener) {
169
                    $this->addListener($eventName, [$subscriber, $listener[0]], $listener[1] ?? 0);
170
                }
171
            }
172
        }
173
    }
174
 
175
    public function removeSubscriber(EventSubscriberInterface $subscriber)
176
    {
177
        foreach ($subscriber->getSubscribedEvents() as $eventName => $params) {
178
            if (\is_array($params) && \is_array($params[0])) {
179
                foreach ($params as $listener) {
180
                    $this->removeListener($eventName, [$subscriber, $listener[0]]);
181
                }
182
            } else {
183
                $this->removeListener($eventName, [$subscriber, \is_string($params) ? $params : $params[0]]);
184
            }
185
        }
186
    }
187
 
188
    /**
189
     * Triggers the listeners of an event.
190
     *
191
     * This method can be overridden to add functionality that is executed
192
     * for each listener.
193
     *
194
     * @param callable[] $listeners The event listeners
195
     * @param string     $eventName The name of the event to dispatch
196
     * @param object     $event     The event object to pass to the event handlers/listeners
197
     */
198
    protected function callListeners(iterable $listeners, string $eventName, object $event)
199
    {
200
        $stoppable = $event instanceof StoppableEventInterface;
201
 
202
        foreach ($listeners as $listener) {
203
            if ($stoppable && $event->isPropagationStopped()) {
204
                break;
205
            }
206
            $listener($event, $eventName, $this);
207
        }
208
    }
209
 
210
    /**
211
     * Sorts the internal list of listeners for the given event by priority.
212
     */
213
    private function sortListeners(string $eventName)
214
    {
215
        krsort($this->listeners[$eventName]);
216
        $this->sorted[$eventName] = [];
217
 
218
        foreach ($this->listeners[$eventName] as &$listeners) {
219
            foreach ($listeners as &$listener) {
220
                if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
221
                    $listener[0] = $listener[0]();
222
                    $listener[1] ??= '__invoke';
223
                }
224
                $this->sorted[$eventName][] = $listener;
225
            }
226
        }
227
    }
228
 
229
    /**
230
     * Optimizes the internal list of listeners for the given event by priority.
231
     */
232
    private function optimizeListeners(string $eventName): array
233
    {
234
        krsort($this->listeners[$eventName]);
235
        $this->optimized[$eventName] = [];
236
 
237
        foreach ($this->listeners[$eventName] as &$listeners) {
238
            foreach ($listeners as &$listener) {
239
                $closure = &$this->optimized[$eventName][];
240
                if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
241
                    $closure = static function (...$args) use (&$listener, &$closure) {
242
                        if ($listener[0] instanceof \Closure) {
243
                            $listener[0] = $listener[0]();
244
                            $listener[1] ??= '__invoke';
245
                        }
246
                        ($closure = $listener(...))(...$args);
247
                    };
248
                } else {
249
                    $closure = $listener instanceof WrappedListener ? $listener : $listener(...);
250
                }
251
            }
252
        }
253
 
254
        return $this->optimized[$eventName];
255
    }
256
}