Skip to content

Commit a12db94

Browse files
committed
feature #39978 [DoctrineBridge] Make subscriber and listeners prioritizable (jderusse)
This PR was merged into the 5.3-dev branch. Discussion ---------- [DoctrineBridge] Make subscriber and listeners prioritizable | Q | A | ------------- | --- | Branch? | 5.x | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | Fix #28090 | License | MIT | Doc PR | - handle Doctrine's eventSubscriber and eventListener priority (listener exposed by the eventSubscriber will have the same priority than the eventListener) Commits ------- 14a613b Make subscriber and listeners prioritizable
2 parents 47da664 + 14a613b commit a12db94

File tree

4 files changed

+198
-67
lines changed

4 files changed

+198
-67
lines changed

src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Doctrine\Common\EventArgs;
1515
use Doctrine\Common\EventManager;
16+
use Doctrine\Common\EventSubscriber;
1617
use Psr\Container\ContainerInterface;
1718

1819
/**
@@ -34,6 +35,9 @@ class ContainerAwareEventManager extends EventManager
3435
private $methods = [];
3536
private $container;
3637

38+
/**
39+
* @param list<string|EventSubscriber|array{string[], string|object}> $subscriberIds List of subscribers, subscriber ids, or [events, listener] tuples
40+
*/
3741
public function __construct(ContainerInterface $container, array $subscriberIds = [])
3842
{
3943
$this->container = $container;
@@ -113,6 +117,10 @@ public function hasListeners($event)
113117
*/
114118
public function addEventListener($events, $listener)
115119
{
120+
if (!$this->initializedSubscribers) {
121+
$this->initializeSubscribers();
122+
}
123+
116124
$hash = $this->getHash($listener);
117125

118126
foreach ((array) $events as $event) {
@@ -135,6 +143,10 @@ public function addEventListener($events, $listener)
135143
*/
136144
public function removeEventListener($events, $listener)
137145
{
146+
if (!$this->initializedSubscribers) {
147+
$this->initializeSubscribers();
148+
}
149+
138150
$hash = $this->getHash($listener);
139151

140152
foreach ((array) $events as $event) {
@@ -149,6 +161,24 @@ public function removeEventListener($events, $listener)
149161
}
150162
}
151163

164+
public function addEventSubscriber(EventSubscriber $subscriber): void
165+
{
166+
if (!$this->initializedSubscribers) {
167+
$this->initializeSubscribers();
168+
}
169+
170+
parent::addEventSubscriber($subscriber);
171+
}
172+
173+
public function removeEventSubscriber(EventSubscriber $subscriber): void
174+
{
175+
if (!$this->initializedSubscribers) {
176+
$this->initializeSubscribers();
177+
}
178+
179+
parent::removeEventSubscriber($subscriber);
180+
}
181+
152182
private function initializeListeners(string $eventName)
153183
{
154184
$this->initialized[$eventName] = true;
@@ -164,20 +194,15 @@ private function initializeListeners(string $eventName)
164194
private function initializeSubscribers()
165195
{
166196
$this->initializedSubscribers = true;
167-
168-
$eventListeners = $this->listeners;
169-
// reset eventListener to respect priority: EventSubscribers have a higher priority
170-
$this->listeners = [];
171-
foreach ($this->subscribers as $id => $subscriber) {
172-
if (\is_string($subscriber)) {
173-
parent::addEventSubscriber($this->subscribers[$id] = $this->container->get($subscriber));
197+
foreach ($this->subscribers as $subscriber) {
198+
if (\is_array($subscriber)) {
199+
$this->addEventListener(...$subscriber);
200+
continue;
174201
}
175-
}
176-
foreach ($eventListeners as $event => $listeners) {
177-
if (!isset($this->listeners[$event])) {
178-
$this->listeners[$event] = [];
202+
if (\is_string($subscriber)) {
203+
$subscriber = $this->container->get($subscriber);
179204
}
180-
$this->listeners[$event] += $listeners;
205+
parent::addEventSubscriber($subscriber);
181206
}
182207
$this->subscribers = [];
183208
}

src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php

Lines changed: 29 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,7 @@ public function process(ContainerBuilder $container)
5757
}
5858

5959
$this->connections = $container->getParameter($this->connections);
60-
$listenerRefs = [];
61-
$this->addTaggedSubscribers($container, $listenerRefs);
62-
$this->addTaggedListeners($container, $listenerRefs);
60+
$listenerRefs = $this->addTaggedServices($container);
6361

6462
// replace service container argument of event managers with smaller service locator
6563
// so services can even remain private
@@ -69,15 +67,20 @@ public function process(ContainerBuilder $container)
6967
}
7068
}
7169

72-
private function addTaggedSubscribers(ContainerBuilder $container, array &$listenerRefs)
70+
private function addTaggedServices(ContainerBuilder $container): array
7371
{
72+
$listenerTag = $this->tagPrefix.'.event_listener';
7473
$subscriberTag = $this->tagPrefix.'.event_subscriber';
75-
$taggedSubscribers = $this->findAndSortTags($subscriberTag, $container);
74+
$listenerRefs = [];
75+
$taggedServices = $this->findAndSortTags([$subscriberTag, $listenerTag], $container);
7676

7777
$managerDefs = [];
78-
foreach ($taggedSubscribers as $taggedSubscriber) {
79-
[$id, $tag] = $taggedSubscriber;
78+
foreach ($taggedServices as $taggedSubscriber) {
79+
[$tagName, $id, $tag] = $taggedSubscriber;
8080
$connections = isset($tag['connection']) ? [$tag['connection']] : array_keys($this->connections);
81+
if ($listenerTag === $tagName && !isset($tag['event'])) {
82+
throw new InvalidArgumentException(sprintf('Doctrine event listener "%s" must specify the "event" attribute.', $id));
83+
}
8184
foreach ($connections as $con) {
8285
if (!isset($this->connections[$con])) {
8386
throw new RuntimeException(sprintf('The Doctrine connection "%s" referenced in service "%s" does not exist. Available connections names: "%s".', $con, $id, implode('", "', array_keys($this->connections))));
@@ -95,39 +98,25 @@ private function addTaggedSubscribers(ContainerBuilder $container, array &$liste
9598
}
9699

97100
if (ContainerAwareEventManager::class === $managerClass) {
98-
$listenerRefs[$con][$id] = new Reference($id);
99101
$refs = $managerDef->getArguments()[1] ?? [];
100-
$refs[] = $id;
102+
$listenerRefs[$con][$id] = new Reference($id);
103+
if ($subscriberTag === $tagName) {
104+
$refs[] = $id;
105+
} else {
106+
$refs[] = [[$tag['event']], $id];
107+
}
101108
$managerDef->setArgument(1, $refs);
102109
} else {
103-
$managerDef->addMethodCall('addEventSubscriber', [new Reference($id)]);
110+
if ($subscriberTag === $tagName) {
111+
$managerDef->addMethodCall('addEventSubscriber', [new Reference($id)]);
112+
} else {
113+
$managerDef->addMethodCall('addEventListener', [[$tag['event']], new Reference($id)]);
114+
}
104115
}
105116
}
106117
}
107-
}
108-
109-
private function addTaggedListeners(ContainerBuilder $container, array &$listenerRefs)
110-
{
111-
$listenerTag = $this->tagPrefix.'.event_listener';
112-
$taggedListeners = $this->findAndSortTags($listenerTag, $container);
113118

114-
foreach ($taggedListeners as $taggedListener) {
115-
[$id, $tag] = $taggedListener;
116-
if (!isset($tag['event'])) {
117-
throw new InvalidArgumentException(sprintf('Doctrine event listener "%s" must specify the "event" attribute.', $id));
118-
}
119-
120-
$connections = isset($tag['connection']) ? [$tag['connection']] : array_keys($this->connections);
121-
foreach ($connections as $con) {
122-
if (!isset($this->connections[$con])) {
123-
throw new RuntimeException(sprintf('The Doctrine connection "%s" referenced in service "%s" does not exist. Available connections names: "%s".', $con, $id, implode('", "', array_keys($this->connections))));
124-
}
125-
$listenerRefs[$con][$id] = new Reference($id);
126-
127-
// we add one call per event per service so we have the correct order
128-
$this->getEventManagerDef($container, $con)->addMethodCall('addEventListener', [[$tag['event']], $id]);
129-
}
130-
}
119+
return $listenerRefs;
131120
}
132121

133122
private function getEventManagerDef(ContainerBuilder $container, string $name)
@@ -149,14 +138,16 @@ private function getEventManagerDef(ContainerBuilder $container, string $name)
149138
* @see https://bugs.php.net/53710
150139
* @see https://bugs.php.net/60926
151140
*/
152-
private function findAndSortTags(string $tagName, ContainerBuilder $container): array
141+
private function findAndSortTags(array $tagNames, ContainerBuilder $container): array
153142
{
154143
$sortedTags = [];
155144

156-
foreach ($container->findTaggedServiceIds($tagName, true) as $serviceId => $tags) {
157-
foreach ($tags as $attributes) {
158-
$priority = $attributes['priority'] ?? 0;
159-
$sortedTags[$priority][] = [$serviceId, $attributes];
145+
foreach ($tagNames as $tagName) {
146+
foreach ($container->findTaggedServiceIds($tagName, true) as $serviceId => $tags) {
147+
foreach ($tags as $attributes) {
148+
$priority = $attributes['priority'] ?? 0;
149+
$sortedTags[$priority][] = [$tagName, $serviceId, $attributes];
150+
}
160151
}
161152
}
162153

src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,24 @@ protected function setUp(): void
2727
$this->evm = new ContainerAwareEventManager($this->container);
2828
}
2929

30+
public function testDispatchEventRespectOrder()
31+
{
32+
$this->evm = new ContainerAwareEventManager($this->container, ['sub1', [['foo'], 'list1'], 'sub2']);
33+
34+
$this->container->set('list1', $listener1 = new MyListener());
35+
$this->container->set('sub1', $subscriber1 = new MySubscriber(['foo']));
36+
$this->container->set('sub2', $subscriber2 = new MySubscriber(['foo']));
37+
38+
$this->assertSame([$subscriber1, $listener1, $subscriber2], array_values($this->evm->getListeners('foo')));
39+
}
40+
3041
public function testDispatchEvent()
3142
{
3243
$this->evm = new ContainerAwareEventManager($this->container, ['lazy4']);
3344

45+
$this->container->set('lazy4', $subscriber1 = new MySubscriber(['foo']));
46+
$this->assertSame(0, $subscriber1->calledSubscribedEventsCount);
47+
3448
$this->container->set('lazy1', $listener1 = new MyListener());
3549
$this->evm->addEventListener('foo', 'lazy1');
3650
$this->evm->addEventListener('foo', $listener2 = new MyListener());
@@ -40,10 +54,8 @@ public function testDispatchEvent()
4054
$this->container->set('lazy3', $listener5 = new MyListener());
4155
$this->evm->addEventListener('foo', $listener5 = new MyListener());
4256
$this->evm->addEventListener('bar', $listener5);
43-
$this->container->set('lazy4', $subscriber1 = new MySubscriber(['foo']));
4457
$this->evm->addEventSubscriber($subscriber2 = new MySubscriber(['bar']));
4558

46-
$this->assertSame(0, $subscriber1->calledSubscribedEventsCount);
4759
$this->assertSame(1, $subscriber2->calledSubscribedEventsCount);
4860

4961
$this->evm->dispatchEvent('foo');
@@ -72,19 +84,22 @@ public function testAddEventListenerAndSubscriberAfterDispatchEvent()
7284
{
7385
$this->evm = new ContainerAwareEventManager($this->container, ['lazy7']);
7486

87+
$this->container->set('lazy7', $subscriber1 = new MySubscriber(['foo']));
88+
$this->assertSame(0, $subscriber1->calledSubscribedEventsCount);
89+
7590
$this->container->set('lazy1', $listener1 = new MyListener());
7691
$this->evm->addEventListener('foo', 'lazy1');
92+
$this->assertSame(1, $subscriber1->calledSubscribedEventsCount);
93+
7794
$this->evm->addEventListener('foo', $listener2 = new MyListener());
7895
$this->container->set('lazy2', $listener3 = new MyListener());
7996
$this->evm->addEventListener('bar', 'lazy2');
8097
$this->evm->addEventListener('bar', $listener4 = new MyListener());
8198
$this->container->set('lazy3', $listener5 = new MyListener());
8299
$this->evm->addEventListener('foo', $listener5 = new MyListener());
83100
$this->evm->addEventListener('bar', $listener5);
84-
$this->container->set('lazy7', $subscriber1 = new MySubscriber(['foo']));
85101
$this->evm->addEventSubscriber($subscriber2 = new MySubscriber(['bar']));
86102

87-
$this->assertSame(0, $subscriber1->calledSubscribedEventsCount);
88103
$this->assertSame(1, $subscriber2->calledSubscribedEventsCount);
89104

90105
$this->evm->dispatchEvent('foo');

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy