Skip to content

Commit b3ac938

Browse files
committed
[Doctrine Bridge] fix priority for doctrine event listeners
1 parent 95ceeab commit b3ac938

File tree

2 files changed

+194
-107
lines changed

2 files changed

+194
-107
lines changed

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

+73-73
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,22 @@
1111

1212
namespace Symfony\Bridge\Doctrine\DependencyInjection\CompilerPass;
1313

14-
use Symfony\Component\DependencyInjection\ContainerBuilder;
15-
use Symfony\Component\DependencyInjection\Reference;
1614
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
15+
use Symfony\Component\DependencyInjection\ContainerBuilder;
1716
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1817
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
18+
use Symfony\Component\DependencyInjection\Reference;
1919

2020
/**
2121
* Registers event listeners and subscribers to the available doctrine connections.
2222
*
2323
* @author Jeremy Mikola <jmikola@gmail.com>
2424
* @author Alexander <iam.asm89@gmail.com>
25+
* @author David Maicher <mail@dmaicher.de>
2526
*/
2627
class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface
2728
{
2829
private $connections;
29-
private $container;
3030
private $eventManagers;
3131
private $managerTemplate;
3232
private $tagPrefix;
@@ -53,97 +53,97 @@ public function process(ContainerBuilder $container)
5353
return;
5454
}
5555

56-
$taggedSubscribers = $container->findTaggedServiceIds($this->tagPrefix.'.event_subscriber', true);
57-
$taggedListeners = $container->findTaggedServiceIds($this->tagPrefix.'.event_listener', true);
58-
59-
if (empty($taggedSubscribers) && empty($taggedListeners)) {
60-
return;
61-
}
62-
63-
$this->container = $container;
6456
$this->connections = $container->getParameter($this->connections);
65-
$sortFunc = function ($a, $b) {
66-
$a = isset($a['priority']) ? $a['priority'] : 0;
67-
$b = isset($b['priority']) ? $b['priority'] : 0;
68-
69-
return $a > $b ? -1 : 1;
70-
};
71-
72-
if (!empty($taggedSubscribers)) {
73-
$subscribersPerCon = $this->groupByConnection($taggedSubscribers);
74-
foreach ($subscribersPerCon as $con => $subscribers) {
75-
$em = $this->getEventManager($con);
57+
$this->addTaggedSubscribers($container);
58+
$this->addTaggedListeners($container);
59+
}
7660

77-
uasort($subscribers, $sortFunc);
78-
foreach ($subscribers as $id => $instance) {
79-
$em->addMethodCall('addEventSubscriber', array(new Reference($id)));
61+
private function addTaggedSubscribers(ContainerBuilder $container)
62+
{
63+
$subscriberTag = $this->tagPrefix.'.event_subscriber';
64+
$taggedSubscribers = $this->findAndSortTags($subscriberTag, $container);
65+
66+
foreach ($taggedSubscribers as $taggedSubscriber) {
67+
list($id, $tag) = $taggedSubscriber;
68+
$connections = isset($tag['connection']) ? array($tag['connection']) : array_keys($this->connections);
69+
foreach ($connections as $con) {
70+
if (!isset($this->connections[$con])) {
71+
throw new RuntimeException(sprintf('The Doctrine connection "%s" referenced in service "%s" does not exist. Available connections names: %s', $con, $taggedSubscriber, implode(', ', array_keys($this->connections))));
8072
}
81-
}
82-
}
8373

84-
if (!empty($taggedListeners)) {
85-
$listenersPerCon = $this->groupByConnection($taggedListeners, true);
86-
foreach ($listenersPerCon as $con => $listeners) {
87-
$em = $this->getEventManager($con);
88-
89-
uasort($listeners, $sortFunc);
90-
foreach ($listeners as $id => $instance) {
91-
$em->addMethodCall('addEventListener', array(
92-
array_unique($instance['event']),
93-
isset($instance['lazy']) && $instance['lazy'] ? $id : new Reference($id),
94-
));
95-
}
74+
$this->getEventManagerDef($container, $con)->addMethodCall('addEventSubscriber', array(new Reference($id)));
9675
}
9776
}
9877
}
9978

100-
private function groupByConnection(array $services, $isListener = false)
79+
private function addTaggedListeners(ContainerBuilder $container)
10180
{
102-
$grouped = array();
103-
foreach ($allCons = array_keys($this->connections) as $con) {
104-
$grouped[$con] = array();
105-
}
81+
$listenerTag = $this->tagPrefix.'.event_listener';
82+
$taggedListeners = $this->findAndSortTags($listenerTag, $container);
83+
84+
foreach ($taggedListeners as $taggedListener) {
85+
list($id, $tag) = $taggedListener;
86+
$taggedListenerDef = $container->getDefinition($id);
87+
if (!isset($tag['event'])) {
88+
throw new InvalidArgumentException(sprintf('Doctrine event listener "%s" must specify the "event" attribute.', $id));
89+
}
10690

107-
foreach ($services as $id => $instances) {
108-
foreach ($instances as $instance) {
109-
if ($isListener) {
110-
if (!isset($instance['event'])) {
111-
throw new InvalidArgumentException(sprintf('Doctrine event listener "%s" must specify the "event" attribute.', $id));
112-
}
113-
$instance['event'] = array($instance['event']);
114-
115-
if (!empty($instance['lazy'])) {
116-
$this->container->getDefinition($id)->setPublic(true);
117-
}
91+
$connections = isset($tag['connection']) ? array($tag['connection']) : array_keys($this->connections);
92+
foreach ($connections as $con) {
93+
if (!isset($this->connections[$con])) {
94+
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))));
11895
}
11996

120-
$cons = isset($instance['connection']) ? array($instance['connection']) : $allCons;
121-
foreach ($cons as $con) {
122-
if (!isset($grouped[$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-
126-
if ($isListener && isset($grouped[$con][$id])) {
127-
$grouped[$con][$id]['event'] = array_merge($grouped[$con][$id]['event'], $instance['event']);
128-
} else {
129-
$grouped[$con][$id] = $instance;
130-
}
97+
if ($lazy = !empty($tag['lazy'])) {
98+
$taggedListenerDef->setPublic(true);
13199
}
100+
101+
// we add one call per event per service so we have the correct order
102+
$this->getEventManagerDef($container, $con)->addMethodCall('addEventListener', array(array($tag['event']), $lazy ? $id : new Reference($id)));
132103
}
133104
}
105+
}
106+
107+
private function getEventManagerDef(ContainerBuilder $container, $name)
108+
{
109+
if (!isset($this->eventManagers[$name])) {
110+
$this->eventManagers[$name] = $container->getDefinition(sprintf($this->managerTemplate, $name));
111+
}
134112

135-
return $grouped;
113+
return $this->eventManagers[$name];
136114
}
137115

138-
private function getEventManager($name)
116+
/**
117+
* Finds and orders all service tags with the given name by their priority.
118+
*
119+
* The order of additions must be respected for services having the same priority,
120+
* and knowing that the \SplPriorityQueue class does not respect the FIFO method,
121+
* we should not use this class.
122+
*
123+
* @see https://bugs.php.net/bug.php?id=53710
124+
* @see https://bugs.php.net/bug.php?id=60926
125+
*
126+
* @param string $tagName
127+
* @param ContainerBuilder $container
128+
*
129+
* @return array
130+
*/
131+
private function findAndSortTags($tagName, ContainerBuilder $container)
139132
{
140-
if (null === $this->eventManagers) {
141-
$this->eventManagers = array();
142-
foreach ($this->connections as $n => $id) {
143-
$this->eventManagers[$n] = $this->container->getDefinition(sprintf($this->managerTemplate, $n));
133+
$sortedTags = array();
134+
135+
foreach ($container->findTaggedServiceIds($tagName, true) as $serviceId => $tags) {
136+
foreach ($tags as $attributes) {
137+
$priority = isset($attributes['priority']) ? $attributes['priority'] : 0;
138+
$sortedTags[$priority][] = array($serviceId, $attributes);
144139
}
145140
}
146141

147-
return $this->eventManagers[$name];
142+
if ($sortedTags) {
143+
krsort($sortedTags);
144+
$sortedTags = call_user_func_array('array_merge', $sortedTags);
145+
}
146+
147+
return $sortedTags;
148148
}
149149
}

0 commit comments

Comments
 (0)