8000 feature #39978 [DoctrineBridge] Make subscriber and listeners priorit… · symfony/symfony@a12db94 · GitHub
[go: up one dir, main page]

Skip to content < 8000 link crossorigin="anonymous" media="all" rel="stylesheet" href="https://github.githubassets.com/assets/primer-react.8d5e42bdd3cd6a27871d.module.css" />

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)
0