@@ -22,6 +22,22 @@ class EventManager implements EventManagerInterface
2222 /**
2323 * Subscribed events and their listeners
2424 *
25+ * STRUCTURE:
26+ * [
27+ * <string name> => [
28+ * <int priority> => [
29+ * 0 => [<callable listener>, ...]
30+ * ],
31+ * ...
32+ * ],
33+ * ...
34+ * ]
35+ *
36+ * NOTE:
37+ * This structure helps us to reuse the list of listeners
38+ * instead of first iterating over it and generating a new one
39+ * -> In result it improves performance by up to 25% even if it looks a bit strange
40+ *
2541 * @var array[]
2642 */
2743 protected $ events = [];
@@ -116,8 +132,14 @@ public function trigger($eventName, $target = null, $argv = [])
116132 {
117133 $ event = clone $ this ->eventPrototype ;
118134 $ event ->setName ($ eventName );
119- $ event ->setTarget ($ target );
120- $ event ->setParams ($ argv );
135+
136+ if ($ target !== null ) {
137+ $ event ->setTarget ($ target );
138+ }
139+
140+ if ($ argv ) {
141+ $ event ->setParams ($ argv );
142+ }
121143
122144 return $ this ->triggerListeners ($ event );
123145 }
@@ -129,8 +151,14 @@ public function triggerUntil(callable $callback, $eventName, $target = null, $ar
129151 {
130152 $ event = clone $ this ->eventPrototype ;
131153 $ event ->setName ($ eventName );
132- $ event ->setTarget ($ target );
133- $ event ->setParams ($ argv );
154+
155+ if ($ target !== null ) {
156+ $ event ->setTarget ($ target );
157+ }
158+
159+ if ($ argv ) {
160+ $ event ->setParams ($ argv );
161+ }
134162
135163 return $ this ->triggerListeners ($ event , $ callback );
136164 }
@@ -164,8 +192,7 @@ public function attach($eventName, callable $listener, $priority = 1)
164192 ));
165193 }
166194
167- $ this ->events [$ eventName ][((int ) $ priority ) . '.0 ' ][] = $ listener ;
168-
195+ $ this ->events [$ eventName ][(int ) $ priority ][0 ][] = $ listener ;
169196 return $ listener ;
170197 }
171198
@@ -197,36 +224,35 @@ public function detach(callable $listener, $eventName = null, $force = false)
197224 }
198225
199226 foreach ($ this ->events [$ eventName ] as $ priority => $ listeners ) {
200- foreach ($ listeners as $ index => $ evaluatedListener ) {
227+ foreach ($ listeners[ 0 ] as $ index => $ evaluatedListener ) {
201228 if ($ evaluatedListener !== $ listener ) {
202229 continue ;
203230 }
204231
205232 // Found the listener; remove it.
206- unset($ this ->events [$ eventName ][$ priority ][$ index ]);
233+ unset($ this ->events [$ eventName ][$ priority ][0 ][ $ index ]);
207234
208235 // If the queue for the given priority is empty, remove it.
209- if (empty ($ this ->events [$ eventName ][$ priority ])) {
236+ if (empty ($ this ->events [$ eventName ][$ priority ][ 0 ] )) {
210237 unset($ this ->events [$ eventName ][$ priority ]);
211238 break ;
212239 }
213240 }
241+ }
214242
215- // If the queue for the given event is empty, remove it.
216- if (empty ($ this ->events [$ eventName ])) {
217- unset($ this ->events [$ eventName ]);
218- break ;
219- }
243+ // If the queue for the given event is empty, remove it.
244+ if (empty ($ this ->events [$ eventName ])) {
245+ unset($ this ->events [$ eventName ]);
220246 }
221247 }
222248
223249 /**
224250 * @inheritDoc
225251 */
226- public function clearListeners ($ event )
252+ public function clearListeners ($ eventName )
227253 {
228- if (isset ($ this ->events [$ event ])) {
229- unset($ this ->events [$ event ]);
254+ if (isset ($ this ->events [$ eventName ])) {
255+ unset($ this ->events [$ eventName ]);
230256 }
231257 }
232258
@@ -262,58 +288,56 @@ protected function triggerListeners(EventInterface $event, callable $callback =
262288 throw new Exception \RuntimeException ('Event is missing a name; cannot trigger! ' );
263289 }
264290
265- // Initial value of stop propagation flag should be false
266- $ event ->stopPropagation (false );
267-
268- $ responses = new ResponseCollection ();
291+ if (isset ($ this ->events [$ name ])) {
292+ $ listOfListenersByPriority = $ this ->events [$ name ];
269293
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 ;
294+ if (isset ($ this ->events ['* ' ])) {
295+ foreach ($ this ->events ['* ' ] as $ priority => $ listOfListeners ) {
296+ $ listOfListenersByPriority [$ priority ][] = $ listOfListeners [0 ];
297+ }
278298 }
299+ } elseif (isset ($ this ->events ['* ' ])) {
300+ $ listOfListenersByPriority = $ this ->events ['* ' ];
301+ } else {
302+ $ listOfListenersByPriority = [];
303+ }
279304
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 ;
305+ if ($ this ->sharedManager ) {
306+ foreach ($ this ->sharedManager ->getListeners ($ this ->identifiers , $ name ) as $ priority => $ listeners ) {
307+ $ listOfListenersByPriority [$ priority ][] = $ listeners ;
285308 }
286309 }
287310
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 = array_merge_recursive (
300- isset ($ this ->events [$ eventName ]) ? $ this ->events [$ eventName ] : [],
301- isset ($ this ->events ['* ' ]) ? $ this ->events ['* ' ] : [],
302- $ this ->sharedManager ? $ this ->sharedManager ->getListeners ($ this ->identifiers , $ eventName ) : []
303- );
304-
305- krsort ($ listeners , SORT_NUMERIC );
311+ // Sort by priority in reverse order
312+ krsort ($ listOfListenersByPriority );
306313
307- $ listenersForEvent = [];
314+ // Initial value of stop propagation flag should be false
315+ $ event ->stopPropagation (false );
308316
309- foreach ($ listeners as $ priority => $ listenersByPriority ) {
310- foreach ($ listenersByPriority as $ listener ) {
311- // Performance note: after some testing, it appears that accumulating listeners and sending
312- // them at the end of the method is FASTER than using generators (ie. yielding)
313- $ listenersForEvent [] = $ listener ;
317+ // Execute listeners
318+ $ responses = new ResponseCollection ();
319+ foreach ($ listOfListenersByPriority as $ listOfListeners ) {
320+ foreach ($ listOfListeners as $ listeners ) {
321+ foreach ($ listeners as $ listener ) {
322+ $ response = $ listener ($ event );
323+ $ responses ->push ($ response );
324+
325+ // If the event was asked to stop propagating, do so
326+ if ($ event ->propagationIsStopped ()) {
327+ $ responses ->setStopped (true );
328+ return $ responses ;
329+ }
330+
331+ // If the result causes our validation callback to return true,
332+ // stop propagation
333+ if ($ callback && $ callback ($ response )) {
334+ $ responses ->setStopped (true );
335+ return $ responses ;
336+ }
337+ }
314338 }
315339 }
316340
317- return $ listenersForEvent ;
341+ return $ responses ;
318342 }
319343}
0 commit comments