Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 5c53f99

Browse filesBrowse files
committed
Make subscriber and listeners prioritizable
1 parent e607210 commit 5c53f99
Copy full SHA for 5c53f99

File tree

4 files changed

+200
-58
lines changed
Filter options

4 files changed

+200
-58
lines changed

‎src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php

Copy file name to clipboardExpand all lines: src/Symfony/Bridge/Doctrine/ContainerAwareEventManager.php
+38-2Lines changed: 38 additions & 2 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 subscriber, subscriberIds or couple of events+eventListener
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,11 +194,17 @@ private function initializeListeners(string $eventName)
164194
private function initializeSubscribers()
165195
{
166196
$this->initializedSubscribers = true;
167-
foreach ($this->subscribers as $id => $subscriber) {
197+
foreach ($this->subscribers as $subscriber) {
198+
if (\is_array($subscriber)) {
199+
$this->addEventListener(...$subscriber);
200+
continue;
201+
}
168202
if (\is_string($subscriber)) {
169-
parent::addEventSubscriber($this->subscribers[$id] = $this->container->get($subscriber));
203+
$subscriber = $this->container->get($subscriber);
170204
}
205+
parent::addEventSubscriber($subscriber);
171206
}
207+
$this->subscribers = [];
172208
}
173209

174210
/**

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

Copy file name to clipboardExpand all lines: src/Symfony/Bridge/Doctrine/DependencyInjection/CompilerPass/RegisterEventListenersAndSubscribersPass.php
+30-39Lines changed: 30 additions & 39 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,23 +67,28 @@ 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))));
8487
}
8588

8689
if (!isset($managerDefs[$con])) {
8790
$managerDef = $parentDef = $this->getEventManagerDef($container, $con);
88-
while ($parentDef instanceof ChildDefinition) {
91+
while (!$parentDef->getClass() && $parentDef instanceof ChildDefinition) {
8992
$parentDef = $container->findDefinition($parentDef->getParent());
9093
}
9194
$managerClass = $container->getParameterBag()->resolveValue($parentDef->getClass());
@@ -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

Copy file name to clipboardExpand all lines: src/Symfony/Bridge/Doctrine/Tests/ContainerAwareEventManagerTest.php
+19-4Lines 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)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.