@@ -164,7 +164,9 @@ public function attach($eventName, callable $listener, $priority = 1)
164
164
));
165
165
}
166
166
167
- $ this ->events [$ eventName ][(int ) $ priority ][] = $ listener ;
167
+ // see performance note of triggerListeners() why the internal
168
+ // event array is structured this way.
169
+ $ this ->events [$ eventName ][(int ) $ priority ][0 ][] = $ listener ;
168
170
169
171
return $ listener ;
170
172
}
@@ -197,16 +199,16 @@ public function detach(callable $listener, $eventName = null, $force = false)
197
199
}
198
200
199
201
foreach ($ this ->events [$ eventName ] as $ priority => $ listeners ) {
200
- foreach ($ listeners as $ index => $ evaluatedListener ) {
202
+ foreach ($ listeners[ 0 ] as $ index => $ evaluatedListener ) {
201
203
if ($ evaluatedListener !== $ listener ) {
202
204
continue ;
203
205
}
204
206
205
207
// Found the listener; remove it.
206
- unset($ this ->events [$ eventName ][$ priority ][$ index ]);
208
+ unset($ this ->events [$ eventName ][$ priority ][0 ][ $ index ]);
207
209
208
210
// If the queue for the given priority is empty, remove it.
209
- if (empty ($ this ->events [$ eventName ][$ priority ])) {
211
+ if (empty ($ this ->events [$ eventName ][$ priority ][ 0 ] )) {
210
212
unset($ this ->events [$ eventName ][$ priority ]);
211
213
break ;
212
214
}
@@ -262,64 +264,71 @@ protected function triggerListeners(EventInterface $event, callable $callback =
262
264
throw new Exception \RuntimeException ('Event is missing a name; cannot trigger! ' );
263
265
}
264
266
265
- // Initial value of stop propagation flag should be false
266
- $ event ->stopPropagation (false );
267
-
268
- $ responses = new ResponseCollection ();
269
-
270
- foreach ($ this ->getListenersByEventName ($ name ) as $ listener ) {
271
- $ response = $ listener ($ event );
272
- $ responses ->push ($ response );
273
-
274
- // If the event was asked to stop propagating, do so
275
- if ($ event ->propagationIsStopped ()) {
276
- $ responses ->setStopped (true );
277
- break ;
278
- }
279
-
280
- // If the result causes our validation callback to return true,
281
- // stop propagation
282
- if ($ callback && $ callback ($ response )) {
283
- $ responses ->setStopped (true );
284
- break ;
285
- }
286
- }
287
-
288
- return $ responses ;
289
- }
290
-
291
- /**
292
- * Get listeners for the currently triggered event.
293
- *
294
- * @param string $eventName
295
- * @return callable[]
296
- */
297
- private function getListenersByEventName ($ eventName )
298
- {
299
- $ listeners = isset ($ this ->events [$ eventName ]) ? $ this ->events [$ eventName ] : [];
267
+ // Merge all listeners by priority together with format
268
+ // [
269
+ // <int priority1> => [
270
+ // [
271
+ // [<callable listener1>, <callable listener2>, ...]
272
+ // ],
273
+ // ...
274
+ // ],
275
+ // <int priority2> => [
276
+ // [
277
+ // [<callable listener1>, <callable listener2>, ...]
278
+ // ],
279
+ // ...
280
+ // ],
281
+ // ...
282
+ // ]
283
+ //
284
+ // PERFORMANCE NOTE:
285
+ // This way pushing the list of listeners into a new generated list
286
+ // helps us to not call "array_merge_[recursive]"
287
+ // and reusing the list of listeners we already have
288
+ // instead of iterating over it and generating a new one.
289
+ // -> In result it improves performance by up to 25% even if it looks a bit strange.
290
+ $ listOfListenersByPriority = isset ($ this ->events [$ name ]) ? $ this ->events [$ name ] : [];
300
291
if (isset ($ this ->events ['* ' ])) {
301
292
foreach ($ this ->events ['* ' ] as $ p => $ l ) {
302
- $ listeners [$ p ] = isset ( $ listeners [ $ p ]) ? array_merge ( $ listeners [ $ p ], $ l ) : $ l ;
293
+ $ listOfListenersByPriority [$ p ][] = $ l [ 0 ] ;
303
294
}
304
295
}
305
296
if ($ this ->sharedManager ) {
306
- foreach ($ this ->sharedManager ->getListeners ($ this ->identifiers , $ eventName ) as $ p => $ l ) {
307
- $ listeners [$ p ] = isset ( $ listeners [ $ p ]) ? array_merge ( $ listeners [ $ p ], $ l ) : $ l ;
297
+ foreach ($ this ->sharedManager ->getListeners ($ this ->identifiers , $ name ) as $ p => $ l ) {
298
+ $ listOfListenersByPriority [$ p ][] = $ l ;
308
299
}
309
300
}
310
301
311
- krsort ($ listeners );
302
+ // Sort by priority in reverse order
303
+ krsort ($ listOfListenersByPriority );
312
304
313
- $ listenersForEvent = [];
305
+ // Initial value of stop propagation flag should be false
306
+ $ event ->stopPropagation (false );
314
307
315
- foreach ($ listeners as $ priority => $ listenersByPriority ) {
316
- foreach ($ listenersByPriority as $ listener ) {
317
- // Performance note: after some testing, it appears that accumulating listeners and sending
318
- // them at the end of the method is FASTER than using generators (ie. yielding)
319
- $ listenersForEvent [] = $ listener ;
308
+ // Execute listeners
309
+ $ responses = new ResponseCollection ();
310
+ foreach ($ listOfListenersByPriority as $ listenersList ) {
311
+ foreach ($ listenersList as $ listeners ) {
312
+ foreach ($ listeners as $ listener ) {
313
+ $ response = $ listener ($ event );
314
+ $ responses ->push ($ response );
315
+
316
+ // If the event was asked to stop propagating, do so
317
+ if ($ event ->propagationIsStopped ()) {
318
+ $ responses ->setStopped (true );
319
+ break 3 ;
320
+ }
321
+
322
+ // If the result causes our validation callback to return true,
323
+ // stop propagation
324
+ if ($ callback && $ callback ($ response )) {
325
+ $ responses ->setStopped (true );
326
+ break 3 ;
327
+ }
328
+ }
320
329
}
321
330
}
322
331
323
- return $ listenersForEvent ;
332
+ return $ responses ;
324
333
}
325
334
}
0 commit comments