Skip to content
This repository was archived by the owner on Jan 29, 2020. It is now read-only.

Commit 9d10eec

Browse files
committed
Performance increasement for MultipleEventMultipleSharedListener
1 parent 263a6eb commit 9d10eec

File tree

3 files changed

+66
-57
lines changed

3 files changed

+66
-57
lines changed

src/EventManager.php

Lines changed: 34 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -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 = [];
@@ -164,10 +180,7 @@ public function attach($eventName, callable $listener, $priority = 1)
164180
));
165181
}
166182

167-
// see performance note of triggerListeners() why the internal
168-
// event array is structured this way.
169183
$this->events[$eventName][(int) $priority][0][] = $listener;
170-
171184
return $listener;
172185
}
173186

@@ -264,38 +277,23 @@ protected function triggerListeners(EventInterface $event, callable $callback =
264277
throw new Exception\RuntimeException('Event is missing a name; cannot trigger!');
265278
}
266279

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] : [];
291-
if (isset($this->events['*'])) {
292-
foreach ($this->events['*'] as $p => $l) {
293-
$listOfListenersByPriority[$p][] = $l[0];
280+
if (isset($this->events[$name])) {
281+
$listOfListenersByPriority = $this->events[$name];
282+
283+
if (isset($this->events['*'])) {
284+
foreach ($this->events['*'] as $priority => $listOfListeners) {
285+
$listOfListenersByPriority[$priority][] = $listOfListeners[0];
286+
}
294287
}
288+
} elseif (isset($this->events['*'])) {
289+
$listOfListenersByPriority = $this->events['*'];
290+
} else {
291+
$listOfListenersByPriority = [];
295292
}
293+
296294
if ($this->sharedManager) {
297-
foreach ($this->sharedManager->getListeners($this->identifiers, $name) as $p => $l) {
298-
$listOfListenersByPriority[$p][] = $l;
295+
foreach ($this->sharedManager->getListeners($this->identifiers, $name) as $priority => $listeners) {
296+
$listOfListenersByPriority[$priority][] = $listeners;
299297
}
300298
}
301299

@@ -307,23 +305,23 @@ protected function triggerListeners(EventInterface $event, callable $callback =
307305

308306
// Execute listeners
309307
$responses = new ResponseCollection();
310-
foreach ($listOfListenersByPriority as $listenersList) {
311-
foreach ($listenersList as $listeners) {
308+
foreach ($listOfListenersByPriority as $listOfListeners) {
309+
foreach ($listOfListeners as $listeners) {
312310
foreach ($listeners as $listener) {
313311
$response = $listener($event);
314312
$responses->push($response);
315313

316314
// If the event was asked to stop propagating, do so
317315
if ($event->propagationIsStopped()) {
318316
$responses->setStopped(true);
319-
break 3;
317+
return $responses;
320318
}
321319

322320
// If the result causes our validation callback to return true,
323321
// stop propagation
324322
if ($callback && $callback($response)) {
325323
$responses->setStopped(true);
326-
break 3;
324+
return $responses;
327325
}
328326
}
329327
}

src/SharedEventManager.php

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ public function detach(callable $listener, $identifier = null, $eventName = null
153153
/**
154154
* Retrieve all listeners for a given identifier and event
155155
*
156-
* @param array $identifiers
157-
* @param string $eventName
156+
* @param string[] $identifiers
157+
* @param string $eventName
158158
* @return array[]
159159
* @throws Exception\InvalidArgumentException
160160
*/
@@ -167,7 +167,7 @@ public function getListeners(array $identifiers, $eventName)
167167
));
168168
}
169169

170-
$listeners = [];
170+
$returnListeners = [];
171171

172172
foreach ($identifiers as $identifier) {
173173
if ('*' === $identifier || ! is_string($identifier) || empty($identifier)) {
@@ -177,42 +177,50 @@ public function getListeners(array $identifiers, $eventName)
177177
));
178178
}
179179

180-
$listenersByIdentifier = isset($this->identifiers[$identifier]) ? $this->identifiers[$identifier] : [];
181-
if (isset($listenersByIdentifier[$eventName])) {
182-
foreach ($listenersByIdentifier[$eventName] as $p => $l) {
183-
$listeners[$p] = isset($listeners[$p]) ? array_merge($listeners[$p], $l) : $l;
180+
if (isset($this->identifiers[$identifier])) {
181+
$listenersByIdentifier = $this->identifiers[$identifier];
182+
if (isset($listenersByIdentifier[$eventName])) {
183+
foreach ($listenersByIdentifier[$eventName] as $priority => $listeners) {
184+
$returnListeners[$priority][] = $listeners;
185+
}
184186
}
185-
}
186-
if (isset($listenersByIdentifier['*'])) {
187-
foreach ($listenersByIdentifier['*'] as $p => $l) {
188-
$listeners[$p] = isset($listeners[$p]) ? array_merge($listeners[$p], $l) : $l;
187+
if (isset($listenersByIdentifier['*'])) {
188+
foreach ($listenersByIdentifier['*'] as $priority => $listeners) {
189+
$returnListeners[$priority][] = $listeners;
190+
}
189191
}
190192
}
191193
}
192194

193195
if (isset($this->identifiers['*'])) {
194196
$wildcardIdentifier = $this->identifiers['*'];
195197
if (isset($wildcardIdentifier[$eventName])) {
196-
foreach ($wildcardIdentifier[$eventName] as $p => $l) {
197-
$listeners[$p] = isset($listeners[$p]) ? array_merge($listeners[$p], $l) : $l;
198+
foreach ($wildcardIdentifier[$eventName] as $priority => $listeners) {
199+
$returnListeners[$priority][] = $listeners;
198200
}
199201
}
200202
if (isset($wildcardIdentifier['*'])) {
201-
foreach ($wildcardIdentifier['*'] as $p => $l) {
202-
$listeners[$p] = isset($listeners[$p]) ? array_merge($listeners[$p], $l) : $l;
203+
foreach ($wildcardIdentifier['*'] as $priority => $listeners) {
204+
$returnListeners[$priority][] = $listeners;
203205
}
204206
}
205207
}
206208

207-
return $listeners;
209+
foreach ($returnListeners as $priority => $listOfListeners) {
210+
// Argument unpacking requires PHP-5.6
211+
// $listeners[$priority] = array_merge(...$listOfListeners);
212+
$returnListeners[$priority] = call_user_func_array('array_merge', $listOfListeners);
213+
}
214+
215+
return $returnListeners;
208216
}
209217

210218
/**
211219
* @inheritDoc
212220
*/
213221
public function clearListeners($identifier, $eventName = null)
214222
{
215-
if (! array_key_exists($identifier, $this->identifiers)) {
223+
if (! isset($this->identifiers[$identifier])) {
216224
return false;
217225
}
218226

src/Test/EventListenerIntrospectionTrait.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,16 @@ private function getListenersForEvent($event, EventManager $events, $withPriorit
6666
{
6767
$r = new ReflectionProperty($events, 'events');
6868
$r->setAccessible(true);
69-
$listeners = $r->getValue($events);
69+
$internal = $r->getValue($events);
7070

71-
if (! isset($listeners[$event])) {
72-
return $this->traverseListeners([]);
71+
$listeners = [];
72+
foreach (isset($internal[$event]) ? $internal[$event] : [] as $p => $listOfListeners) {
73+
foreach ($listOfListeners as $l) {
74+
$listeners[$p] = isset($listeners[$p]) ? array_merge($listeners[$p], $l) : $l;
75+
}
7376
}
7477

75-
return $this->traverseListeners($listeners[$event], $withPriority);
78+
return $this->traverseListeners($listeners, $withPriority);
7679
}
7780

7881
/**

0 commit comments

Comments
 (0)