10000 [Doctrine Bridge] fix priority for doctrine event listeners · symfony/symfony@e47cf38 · GitHub
[go: up one dir, main page]

Skip to content

Commit e47cf38

Browse files
committed
[Doctrine Bridge] fix priority for doctrine event listeners
1 parent c18c93b commit e47cf38

File tree

2 files changed

+210
-114
lines changed

2 files changed

+210
-114
lines changed

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

Lines changed: 89 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,30 @@
1111

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

14+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1415
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
17+
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
1518
use Symfony\Component\DependencyInjection\Reference;
16-
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1719

1820
/**
1921
* Registers event listeners and subscribers to the available doctrine connections.
2022
*
2123
* @author Jeremy Mikola <jmikola@gmail.com>
2224
* @author Alexander <iam.asm89@gmail.com>
25+
* @author David Maicher <mail@dmaicher.de>
2326
*/
2427
class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface
2528
{
29+
/**
30+
* @var string|string[]
31+
*/
2632
private $connections;
27-
private $container;
2833
private $eventManagers;
2934
private $managerTemplate;
3035
private $tagPrefix;
3136

3237
/**
33-
* Constructor.
34-
*
3538
* @param string $connections Parameter ID for connections
3639
* @param string $managerTemplate sprintf() template for generating the event
3740
* manager's service ID for a connection name
@@ -53,105 +56,112 @@ public function process(ContainerBuilder $container)
5356
return;
5457
}
5558

56-
$taggedSubscribers = $container->findTaggedServiceIds($this->tagPrefix.'.event_subscriber');
57-
$taggedListeners = $container->findTaggedServiceIds($this->tagPrefix.'.event_listener');
58-
59-
if (empty($taggedSubscribers) && empty($taggedListeners)) {
60-
return;
61-
}
62-
63-
$this->container = $container;
6459
$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-
};
60+
$this->addTaggedSubscribers($container);
61+
$this->addTaggedListeners($container);
62+
}
7163

72-
if (!empty($taggedSubscribers)) {
73-
$subscribersPerCon = $this->groupByConnection($taggedSubscribers);
74-
foreach ($subscribersPerCon as $con => $subscribers) {
75-
$em = $this->getEventManager($con);
64+
private function addTaggedSubscribers(ContainerBuilder $container)
65+
{
66+
$subscriberTag = $this->tagPrefix.'.event_subscriber';
67+
$taggedSubscribers = $this->findAndSortTags($subscriberTag, $container);
7668

B41A
77-
uasort($subscribers, $sortFunc);
78-
foreach ($subscribers as $id => $instance) {
79-
if ($container->getDefinition($id)->isAbstract()) {
80-
throw new \InvalidArgumentException(sprintf('The abstract service "%s" cannot be tagged as a doctrine event subscriber.', $id));
81-
}
69+
foreach ($taggedSubscribers as $taggedSubscriber) {
70+
$id = $taggedSubscriber[0];
71+
$taggedSubscriberDef = $container->getDefinition($id);
8272

83-
$em->addMethodCall('addEventSubscriber', array(new Reference($id)));
84-
}
73+
if ($taggedSubscriberDef->isAbstract()) {
74+
throw new InvalidArgumentException(sprintf('The abstract service "%s" cannot be tagged as a doctrine event subscriber.', $id));
8575
}
86-
}
8776

88-
if (!empty($taggedListeners)) {
89-
$listenersPerCon = $this->groupByConnection($taggedListeners, true);
90-
foreach ($listenersPerCon as $con => $listeners) {
91-
$em = $this->getEventManager($con);
92-
93-
uasort($listeners, $sortFunc);
94-
foreach ($listeners as $id => $instance) {
95-
if ($container->getDefinition($id)->isAbstract()) {
96-
throw new \InvalidArgumentException(sprintf('The abstract service "%s" cannot be tagged as a doctrine event listener.', $id));
97-
}
98-
99-
$em->addMethodCall('addEventListener', array(
100-
array_unique($instance['event']),
101-
isset($instance['lazy']) && $instance['lazy'] ? $id : new Reference($id),
102-
));
77+
$tag = $taggedSubscriber[1];
78+
$connections = isset($tag['connection']) ? array($tag['connection']) : array_keys($this->connections);
79+
foreach ($connections as $con) {
80+
if (!isset($this->connections[$con])) {
81+
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))));
10382
}
83+
84+
$this->getEventManagerDef($container, $con)->addMethodCall('addEventSubscriber', array(new Reference($id)));
10485
}
10586
}
10687
}
10788

108-
private function groupByConnection(array $services, $isListener = false)
89+
private function addTaggedListeners(ContainerBuilder $container)
10990
{
110-
$grouped = array();
111-
foreach ($allCons = array_keys($this->connections) as $con) {
112-
$grouped[$con] = array();
113-
}
91+
$listenerTag = $this->tagPrefix.'.event_listener';
92+
$taggedListeners = $this->findAndSortTags($listenerTag, $container);
93+
94+
foreach ($taggedListeners as $taggedListener) {
95+
$id = $taggedListener[0];
96+
$taggedListenerDef = $container->getDefinition($taggedListener[0]);
97+
if ($taggedListenerDef->isAbstract()) {
98+
throw new InvalidArgumentException(sprintf('The abstract service "%s" cannot be tagged as a doctrine event listener.', $id));
99+
}
114100

115-
foreach ($services as $id => $instances) {
116-
foreach ($instances as $instance) {
117-
if ($isListener) {
118-
if (!isset($instance['event'])) {
119-
throw new \InvalidArgumentException(sprintf('Doctrine event listener "%s" must specify the "event" attribute.', $id));
120-
}
121-
$instance['event'] = array($instance['event']);
122-
123-
if (isset($instance['lazy']) && $instance['lazy']) {
124-
$this->container->getDefinition($id)->setPublic(true);
125-
}
101+
$tag = $taggedListener[1];
102+
if (!isset($tag['event'])) {
103+
throw new InvalidArgumentException(sprintf('Doctrine event listener "%s" must specify the "event" attribute.', $id));
104+
}
105+
106+
$connections = isset($tag['connection']) ? array($tag['connection']) : array_keys($this->connections);
107+
foreach ($connections as $con) {
108+
if (!isset($this->connections[$con])) {
109+
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))));
126110
}
127111

128-
$cons = isset($instance['connection']) ? array($instance['connection']) : $allCons;
129-
foreach ($cons as $con) {
130-
if (!isset($grouped[$con])) {
131-
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))));
132-
}
133-
134-
if ($isListener && isset($grouped[$con][$id])) {
135-
$grouped[$con][$id]['event'] = array_merge($grouped[$con][$id]['event'], $instance['event']);
136-
} else {
137-
$grouped[$con][$id] = $instance;
138-
}
112+
if ($lazy = isset($tag['lazy']) && $tag['lazy']) {
113+
$taggedListenerDef->setPublic(true);
139114
}
115+
116+
// we add one call per event per service so we have the correct order
117+
$this->getEventManagerDef($container, $con)->addMethodCall('addEventListener', array(
118+
$tag['event'],
119+
$lazy ? $id : new Reference($id),
120+
));
140121
}
141122
}
123+
}
142124

143-
return $grouped;
125+
private function getEventManagerDef(ContainerBuilder $container, $name)
126+
{
127+
if (!isset($this->eventManagers[$name])) {
128+
$this->eventManagers[$name] = $container->getDefinition(sprintf($this->managerTemplate, $name));
129+
}
130+
131+
return $this->eventManagers[$name];
144132
}
145133

146-
private function getEventManager($name)
134+
/**
135+
* Finds and orders all service tags with the given name by their priority.
136+
*
137+
* The order of additions must be respected for services having the same priority,
138+
* and knowing that the \SplPriorityQueue class does not respect the FIFO method,
139+
* we should not use this class.
140+
*
141+
* @see https://bugs.php.net/bug.php?id=53710
142+
* @see https://bugs.php.net/bug.php?id=60926
143+
*
144+
* @param string $tagName
145+
* @param ContainerBuilder $container
146+
*
147+
* @return array
148+
*/
149+
private function findAndSortTags($tagName, ContainerBuilder $container)
147150
{
148-
if (null === $this->eventManagers) {
149-
$this->eventManagers = array();
150-
foreach ($this->connections as $n => $id) {
151-
$this->eventManagers[$n] = $this->container->getDefinition(sprintf($this->managerTemplate, $n));
151+
$sortedTags = array();
152+
153+
foreach ($container->findTaggedServiceIds($tagName) as $serviceId => $tags) {
154+
foreach ($tags as $attributes) {
155+
$priority = isset($attributes['priority']) ? $attributes['priority'] : 0;
156+
$sortedTags[$priority][] = array($serviceId, $attributes);
152157
}
153158
}
154159

155-
return $this->eventManagers[$name];
160+
if ($sortedTags) {
161+
krsort($sortedTags);
162+
$sortedTags = call_user_func_array('array_merge', $sortedTags);
163+
}
164+
165+
return $sortedTags;
156166
}
157167
}

0 commit comments

Comments
 (0)
0