@@ -30,6 +30,14 @@ class EventDispatcher implements EventDispatcherInterface
30
30
{
31
31
private $ listeners = array ();
32
32
private $ sorted = array ();
33
+ private $ optimized ;
34
+
35
+ public function __construct ()
36
+ {
37
+ if (__CLASS__ === \get_class ($ this )) {
38
+ $ this ->optimized = array ();
39
+ }
40
+ }
33
41
34
42
/**
35
43
* {@inheritdoc}
@@ -40,7 +48,13 @@ public function dispatch($eventName, Event $event = null)
40
48
$ event = new Event ();
41
49
}
42
50
43
- if ($ listeners = $ this ->getListeners ($ eventName )) {
51
+ if (null !== $ this ->optimized && null !== $ eventName ) {
52
+ $ listeners = $ this ->optimized [$ eventName ] ?? (empty ($ this ->listeners [$ eventName ]) ? array () : $ this ->optimizeListeners ($ eventName ));
53
+ } else {
54
+ $ listeners = $ this ->getListeners ($ eventName );
55
+ }
56
+
57
+ if ($ listeners ) {
44
58
$ this ->doDispatch ($ listeners , $ eventName , $ event );
45
59
}
46
60
@@ -86,11 +100,10 @@ public function getListenerPriority($eventName, $listener)
86
100
$ listener [0 ] = $ listener [0 ]();
87
101
}
88
102
89
- foreach ($ this ->listeners [$ eventName ] as $ priority => $ listeners ) {
90
- foreach ($ listeners as $ k => $ v ) {
103
+ foreach ($ this ->listeners [$ eventName ] as $ priority => & $ listeners ) {
104
+ foreach ($ listeners as & $ v ) {
91
105
if ($ v !== $ listener && \is_array ($ v ) && isset ($ v [0 ]) && $ v [0 ] instanceof \Closure) {
92
106
$ v [0 ] = $ v [0 ]();
93
- $ this ->listeners [$ eventName ][$ priority ][$ k ] = $ v ;
94
107
}
95
108
if ($ v === $ listener ) {
96
109
return $ priority ;
@@ -123,7 +136,7 @@ public function hasListeners($eventName = null)
123
136
public function addListener ($ eventName , $ listener , $ priority = 0 )
124
137
{
125
138
$ this ->listeners [$ eventName ][$ priority ][] = $ listener ;
126
- unset($ this ->sorted [$ eventName ]);
139
+ unset($ this ->sorted [$ eventName ], $ this -> optimized [ $ eventName ] );
127
140
}
128
141
129
142
/**
@@ -139,21 +152,17 @@ public function removeListener($eventName, $listener)
139
152
$ listener [0 ] = $ listener [0 ]();
140
153
}
141
154
142
- foreach ($ this ->listeners [$ eventName ] as $ priority => $ listeners ) {
143
- foreach ($ listeners as $ k => $ v ) {
155
+ foreach ($ this ->listeners [$ eventName ] as $ priority => & $ listeners ) {
156
+ foreach ($ listeners as $ k => & $ v ) {
144
157
if ($ v !== $ listener && \is_array ($ v ) && isset ($ v [0 ]) && $ v [0 ] instanceof \Closure) {
145
158
$ v [0 ] = $ v [0 ]();
146
159
}
147
160
if ($ v === $ listener ) {
148
- unset($ listeners [$ k ], $ this ->sorted [$ eventName ]);
149
- } else {
150
- $ listeners [$ k ] = $ v ;
161
+ unset($ listeners [$ k ], $ this ->sorted [$ eventName ], $ this ->optimized [$ eventName ]);
151
162
}
152
163
}
153
164
154
- if ($ listeners ) {
155
- $ this ->listeners [$ eventName ][$ priority ] = $ listeners ;
156
- } else {
165
+ if (!$ listeners ) {
157
166
unset($ this ->listeners [$ eventName ][$ priority ]);
158
167
}
159
168
}
@@ -215,22 +224,46 @@ protected function doDispatch($listeners, $eventName, Event $event)
215
224
216
225
/**
217
226
* Sorts the internal list of listeners for the given event by priority.
218
- *
219
- * @param string $eventName The name of the event
220
227
*/
221
- private function sortListeners ($ eventName )
228
+ private function sortListeners (string $ eventName )
222
229
{
223
230
krsort ($ this ->listeners [$ eventName ]);
224
231
$ this ->sorted [$ eventName ] = array ();
225
232
226
- foreach ($ this ->listeners [$ eventName ] as $ priority => $ listeners ) {
233
+ foreach ($ this ->listeners [$ eventName ] as & $ listeners ) {
227
234
foreach ($ listeners as $ k => $ listener ) {
228
235
if (\is_array ($ listener ) && isset ($ listener [0 ]) && $ listener [0 ] instanceof \Closure) {
229
236
$ listener [0 ] = $ listener [0 ]();
230
- $ this ->listeners [$ eventName ][$ priority ][$ k ] = $ listener ;
231
237
}
232
238
$ this ->sorted [$ eventName ][] = $ listener ;
233
239
}
234
240
}
235
241
}
242
+
243
+ /**
244
+ * Optimizes the internal list of listeners for the given event by priority.
245
+ */
246
+ private function optimizeListeners (string $ eventName ): array
247
+ {
248
+ krsort ($ this ->listeners [$ eventName ]);
249
+ $ this ->optimized [$ eventName ] = array ();
250
+
251
+ foreach ($ this ->listeners [$ eventName ] as &$ listeners ) {
252
+ foreach ($ listeners as &$ listener ) {
253
+ $ closure = &$ this ->optimized [$ eventName ][];
254
+ if (\is_array ($ listener ) && isset ($ listener [0 ]) && $ listener [0 ] instanceof \Closure) {
255
+ $ closure = static function (...$ args ) use (&$ listener , &$ closure ) {
256
+ if ($ listener [0 ] instanceof \Closure) {
257
+ $ listener [0 ] = $ listener [0 ]();
258
+ }
259
+ ($ closure = \Closure::fromCallable ($ listener ))(...$ args );
260
+ };
261
+ } else {
262
+ $ closure = $ listener instanceof \Closure ? $ listener : \Closure::fromCallable ($ listener );
263
+ }
264
+ }
265
+ }
266
+
267
+ return $ this ->optimized [$ eventName ];
268
+ }
236
269
}
0 commit comments