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

Commit 5569b4e

Browse files
committed
feat: Creates a PrioritizedAggregateListenerProvider
This version acts like the combination of EventManager+SharedEventManager in terms of how it aggregates and resolves priority for listeners. The class aggregates a list of `PrioritizedListenerAttachmentInterface` instances (and implements the interface itself), looping over each in ordert to build up a prioritized list of all listeners from all providers. Since they are done in order, the order in which they should be attached generally is: - PrioritizedListenerProvider - PrioritizedIdentifierListenerProvider
1 parent c8b7d6a commit 5569b4e

File tree

3 files changed

+193
-3
lines changed

3 files changed

+193
-3
lines changed

TODO-PSR-14.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@
4040
- [x] implement `getListenersForEventByPriority`
4141
- [x] `SharedEventManager` will extend this class
4242
- [x] mark as deprecated (will not use this in v4)
43-
- [ ] Create a `PrioritizedAggregateListenerProvider` implementation
44-
- [ ] Accepts a list of `PrioritizedListenerProvider` instances
45-
- [ ] `getListenersByEvent()` will loop through each, in order, calling the
43+
- [x] Create a `PrioritizedAggregateListenerProvider` implementation
44+
- [x] Accepts a list of `PrioritizedListenerProvider` instances
45+
- [x] `getListenersByEvent()` will loop through each, in order, calling the
4646
`getListenersForEventByPriority()` method of each, returning the
4747
aggregated listeners in priority order.
4848
- [ ] Create `ListenerSubscriberInterface`
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
/**
3+
* @see https://github.com/zendframework/zend-eventmanager for the canonical source repository
4+
* @copyright Copyright (c) 2019 Zend Technologies USA Inc. (https://www.zend.com)
5+
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md New BSD License
6+
*/
7+
8+
namespace Zend\EventManager\ListenerProvider;
9+
10+
use Zend\EventManager\Exception;
11+
12+
class PrioritizedAggregateListenerProvider implements PrioritizedListenerProviderInterface
13+
{
14+
/**
15+
* @var PrioritizedListenerProviderInterface[]
16+
*/
17+
private $providers;
18+
19+
public function __construct(array $providers)
20+
{
21+
$this->validateProviders($providers);
22+
$this->providers = $providers;
23+
}
24+
25+
/**
26+
* {@inheritDoc}
27+
* @param string[] $identifiers Any identifiers to use when retrieving
28+
* listeners from child providers.
29+
*/
30+
public function getListenersForEvent($event, array $identifiers = [])
31+
{
32+
yield from $this->iterateByPriority(
33+
$this->getListenersForEventByPriority($event, $identifiers)
34+
);
35+
}
36+
37+
public function getListenersForEventByPriority($event, array $identifiers = [])
38+
{
39+
$prioritizedListeners = [];
40+
41+
foreach ($this->providers as $provider) {
42+
foreach ($provider->getListenersForEventByPriority($event, $identifiers) as $priority => $listeners) {
43+
$prioritizedListeners[$priority] = isset($prioritizedListeners[$priority])
44+
? array_merge($prioritizedListeners[$priority], $listeners)
45+
: $listeners;
46+
}
47+
}
48+
49+
return $prioritizedListeners;
50+
}
51+
52+
/**
53+
* @throws Exception\InvalidArgumentException if any provider is not a
54+
* PrioritizedListenerProviderInterface instance
55+
*/
56+
private function validateProviders(array $providers)
57+
{
58+
foreach ($providers as $index => $provider) {
59+
if (! $provider instanceof PrioritizedListenerProviderInterface) {
60+
throw new Exception\InvalidArgumentException(sprintf(
61+
'%s requires all providers be instances of %s; received provider of type "%s" at index %d',
62+
__CLASS__,
63+
PrioritizedListenerProviderInterface::class,
64+
gettype($provider),
65+
$index
66+
));
67+
}
68+
}
69+
}
70+
71+
/**
72+
* @param array $prioritizedListeners
73+
* @return iterable
74+
*/
75+
private function iterateByPriority($prioritizedListeners)
76+
{
77+
krsort($prioritizedListeners);
78+
foreach ($prioritizedListeners as $listeners) {
79+
yield from $listeners;
80+
}
81+
}
82+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<?php
2+
/**
3+
* @see https://github.com/zendframework/zend-eventmanager for the canonical source repository
4+
* @copyright Copyright (c) 2019 Zend Technologies USA Inc. (https://www.zend.com)
5+
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md New BSD License
6+
*/
7+
8+
namespace ZendTest\EventManager\ListenerProvider;
9+
10+
use PHPUnit\Framework\TestCase;
11+
use Zend\EventManager\Event;
12+
use Zend\EventManager\Exception;
13+
use Zend\EventManager\ListenerProvider\ListenerProviderInterface;
14+
use Zend\EventManager\ListenerProvider\PrioritizedAggregateListenerProvider;
15+
use Zend\EventManager\ListenerProvider\PrioritizedListenerProvider;
16+
use Zend\EventManager\ListenerProvider\PrioritizedListenerProviderInterface;
17+
use Zend\EventManager\ListenerProvider\PrioritizedIdentifierListenerProvider;
18+
19+
class PrioritizedAggregateListenerProviderTest extends TestCase
20+
{
21+
public function invalidProviders()
22+
{
23+
$genericProvider = $this->prophesize(ListenerProviderInterface::class)->reveal();
24+
return [
25+
'null' => [null],
26+
'true' => [true],
27+
'false' => [false],
28+
'zero' => [0],
29+
'int' => [1],
30+
'zero-float' => [0.0],
31+
'float' => [1.1],
32+
'string' => ['invalid'],
33+
'array' => [['invalid']],
34+
'object' => [(object) ['value' => 'invalid']],
35+
'non-prioritized-provider' => [$genericProvider],
36+
];
37+
}
38+
39+
/**
40+
* @dataProvider invalidProviders
41+
* @param mixed $provider
42+
*/
43+
public function testConstructorRaisesExceptionForInvalidProviders($provider)
44+
{
45+
$this->expectException(Exception\InvalidArgumentException::class);
46+
$this->expectExceptionMessage(PrioritizedListenerProviderInterface::class);
47+
new PrioritizedAggregateListenerProvider([$provider]);
48+
}
49+
50+
public function testIteratesProvidersInOrderExpected()
51+
{
52+
$event = new Event();
53+
$event->setName('test');
54+
55+
$baseListener = function () {
56+
};
57+
58+
$first = clone $baseListener;
59+
$second = clone $baseListener;
60+
$third = clone $baseListener;
61+
$fourth = clone $baseListener;
62+
$fifth = clone $baseListener;
63+
$sixth = clone $baseListener;
64+
$seventh = clone $baseListener;
65+
$eighth = clone $baseListener;
66+
$ninth = clone $baseListener;
67+
68+
$provider = new PrioritizedListenerProvider();
69+
$provider->attachWildcardListener($first);
70+
$provider->attach(Event::class, $second);
71+
$provider->attach('test', $third);
72+
73+
$identifiedProvider = new PrioritizedIdentifierListenerProvider();
74+
$identifiedProvider->attach(Event::class, '*', $fourth);
75+
$identifiedProvider->attach(Event::class, Event::class, $fifth);
76+
$identifiedProvider->attach(Event::class, 'test', $sixth);
77+
$identifiedProvider->attach('*', '*', $seventh);
78+
$identifiedProvider->attach('*', Event::class, $eighth);
79+
$identifiedProvider->attach('*', 'test', $ninth);
80+
81+
$aggregateProvider = new PrioritizedAggregateListenerProvider([
82+
$provider,
83+
$identifiedProvider,
84+
]);
85+
86+
$prioritizedListeners = [];
87+
$index = 1;
88+
89+
foreach ($aggregateProvider->getListenersForEvent($event, [Event::class]) as $listener) {
90+
$prioritizedListeners[$index] = spl_object_hash($listener);
91+
$index += 1;
92+
}
93+
94+
$expected = [
95+
1 => spl_object_hash($third),
96+
2 => spl_object_hash($second),
97+
3 => spl_object_hash($first),
98+
4 => spl_object_hash($sixth),
99+
5 => spl_object_hash($fifth),
100+
6 => spl_object_hash($fourth),
101+
7 => spl_object_hash($ninth),
102+
8 => spl_object_hash($eighth),
103+
9 => spl_object_hash($seventh),
104+
];
105+
106+
$this->assertSame($expected, $prioritizedListeners);
107+
}
108+
}

0 commit comments

Comments
 (0)