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

Commit 3b884eb

Browse files
committed
Merging develop to master in preparation for 3.1.0 release
2 parents 70ca7e1 + 5ef4add commit 3b884eb

File tree

6 files changed

+151
-85
lines changed

6 files changed

+151
-85
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,24 @@
22

33
All notable changes to this project will be documented in this file, in reverse chronological order by release.
44

5+
## 3.1.0 - TBD
6+
7+
### Added
8+
9+
- Nothing.
10+
11+
### Deprecated
12+
13+
- Nothing.
14+
15+
### Removed
16+
17+
- Nothing.
18+
19+
### Fixed
20+
21+
- Nothing.
22+
523
## 3.0.2 - TBD
624

725
### Added

src/EventManager.php

Lines changed: 83 additions & 59 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 = [];
@@ -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
}

src/SharedEventManager.php

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public function attach($identifier, $event, callable $listener, $priority = 1)
7474
));
7575
}
7676

77-
$this->identifiers[$identifier][$event][((int) $priority) . '.0'][] = $listener;
77+
$this->identifiers[$identifier][$event][(int) $priority][] = $listener;
7878
}
7979

8080
/**
@@ -152,8 +152,8 @@ public function detach(callable $listener, $identifier = null, $eventName = null
152152
/**
153153
* Retrieve all listeners for a given identifier and event
154154
*
155-
* @param array $identifiers
156-
* @param string $eventName
155+
* @param string[] $identifiers
156+
* @param string $eventName
157157
* @return array[]
158158
* @throws Exception\InvalidArgumentException
159159
*/
@@ -166,7 +166,7 @@ public function getListeners(array $identifiers, $eventName)
166166
));
167167
}
168168

169-
$listeners = [];
169+
$returnListeners = [];
170170

171171
foreach ($identifiers as $identifier) {
172172
if ('*' === $identifier || ! is_string($identifier) || empty($identifier)) {
@@ -176,34 +176,50 @@ public function getListeners(array $identifiers, $eventName)
176176
));
177177
}
178178

179-
$listenersByIdentifier = isset($this->identifiers[$identifier]) ? $this->identifiers[$identifier] : [];
180-
181-
$listeners = array_merge_recursive(
182-
$listeners,
183-
isset($listenersByIdentifier[$eventName]) ? $listenersByIdentifier[$eventName] : [],
184-
isset($listenersByIdentifier['*']) ? $listenersByIdentifier['*'] : []
185-
);
179+
if (isset($this->identifiers[$identifier])) {
180+
$listenersByIdentifier = $this->identifiers[$identifier];
181+
if (isset($listenersByIdentifier[$eventName])) {
182+
foreach ($listenersByIdentifier[$eventName] as $priority => $listeners) {
183+
$returnListeners[$priority][] = $listeners;
184+
}
185+
}
186+
if (isset($listenersByIdentifier['*'])) {
187+
foreach ($listenersByIdentifier['*'] as $priority => $listeners) {
188+
$returnListeners[$priority][] = $listeners;
189+
}
190+
}
191+
}
186192
}
187193

188-
if (isset($this->identifiers['*']) && ! in_array('*', $identifiers)) {
194+
if (isset($this->identifiers['*'])) {
189195
$wildcardIdentifier = $this->identifiers['*'];
196+
if (isset($wildcardIdentifier[$eventName])) {
197+
foreach ($wildcardIdentifier[$eventName] as $priority => $listeners) {
198+
$returnListeners[$priority][] = $listeners;
199+
}
200+
}
201+
if (isset($wildcardIdentifier['*'])) {
202+
foreach ($wildcardIdentifier['*'] as $priority => $listeners) {
203+
$returnListeners[$priority][] = $listeners;
204+
}
205+
}
206+
}
190207

191-
$listeners = array_merge_recursive(
192-
$listeners,
193-
isset($wildcardIdentifier[$eventName]) ? $wildcardIdentifier[$eventName] : [],
194-
isset($wildcardIdentifier['*']) ? $wildcardIdentifier['*'] : []
195-
);
208+
foreach ($returnListeners as $priority => $listOfListeners) {
209+
// Argument unpacking requires PHP-5.6
210+
// $listeners[$priority] = array_merge(...$listOfListeners);
211+
$returnListeners[$priority] = call_user_func_array('array_merge', $listOfListeners);
196212
}
197213

198-
return $listeners;
214+
return $returnListeners;
199215
}
200216

201217
/**
202218
* @inheritDoc
203219
*/
204220
public function clearListeners($identifier, $eventName = null)
205221
{
206-
if (! array_key_exists($identifier, $this->identifiers)) {
222+
if (! isset($this->identifiers[$identifier])) {
207223
return false;
208224
}
209225

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
/**

test/EventManagerTest.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,12 @@ public function getListenersForEvent($event, EventManager $manager)
5656
$r->setAccessible(true);
5757
$events = $r->getValue($manager);
5858

59-
return isset($events[$event]) ? $events[$event] : [];
59+
$listenersByPriority = isset($events[$event]) ? $events[$event] : [];
60+
foreach ($listenersByPriority as $priority => & $listeners) {
61+
$listeners = $listeners[0];
62+
}
63+
64+
return $listenersByPriority;
6065
}
6166

6267
public function testAttachShouldAddListenerToEvent()

test/SharedEventManagerTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public function setUp()
2727

2828
public function getListeners(SharedEventManager $manager, array $identifiers, $event, $priority = 1)
2929
{
30-
$priority = (int) $priority . '.0';
30+
$priority = (int) $priority;
3131
$listeners = $manager->getListeners($identifiers, $event);
3232
if (! isset($listeners[$priority])) {
3333
return [];
@@ -247,7 +247,7 @@ public function testClearListenersDoesNothingIfNoEventsRegisteredForIdentifier()
247247
$this->manager->clearListeners('IDENTIFIER', 'EVENT');
248248

249249
// getListeners() always pulls in wildcard listeners
250-
$this->assertEquals(['1.0' => [
250+
$this->assertEquals([1 => [
251251
$this->callback,
252252
]], $this->manager->getListeners([ 'IDENTIFIER' ], 'EVENT'));
253253
}

0 commit comments

Comments
 (0)