11
11
12
12
namespace Symfony \Bridge \Doctrine \DependencyInjection \CompilerPass ;
13
13
14
+ use Symfony \Component \DependencyInjection \Compiler \CompilerPassInterface ;
14
15
use Symfony \Component \DependencyInjection \ContainerBuilder ;
16
+ use Symfony \Component \DependencyInjection \Exception \InvalidArgumentException ;
17
+ use Symfony \Component \DependencyInjection \Exception \RuntimeException ;
15
18
use Symfony \Component \DependencyInjection \Reference ;
16
- use Symfony \Component \DependencyInjection \Compiler \CompilerPassInterface ;
17
19
18
20
/**
19
21
* Registers event listeners and subscribers to the available doctrine connections.
20
22
*
21
23
* @author Jeremy Mikola <jmikola@gmail.com>
22
24
* @author Alexander <iam.asm89@gmail.com>
25
+ * @author David Maicher <mail@dmaicher.de>
23
26
*/
24
27
class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface
25
28
{
29
+ /**
30
+ * @var string|string[]
31
+ */
26
32
private $ connections ;
27
- private $ container ;
28
33
private $ eventManagers ;
29
34
private $ managerTemplate ;
30
35
private $ tagPrefix ;
31
36
32
37
/**
33
- * Constructor.
34
- *
35
38
* @param string $connections Parameter ID for connections
36
39
* @param string $managerTemplate sprintf() template for generating the event
37
40
* manager's service ID for a connection name
@@ -53,105 +56,112 @@ public function process(ContainerBuilder $container)
53
56
return ;
54
57
}
55
58
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 ;
64
59
$ 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
+ }
71
63
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 );
76
68
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 );
82
72
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 ));
85
75
}
86
- }
87
76
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 ))));
103
82
}
83
+
84
+ $ this ->getEventManagerDef ($ container , $ con )->addMethodCall ('addEventSubscriber ' , array (new Reference ($ id )));
104
85
}
105
86
}
106
87
}
107
88
108
- private function groupByConnection ( array $ services , $ isListener = false )
89
+ private function addTaggedListeners ( ContainerBuilder $ container )
109
90
{
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
+ }
114
100
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 ))));
126
110
}
127
111
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 );
139
114
}
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
+ ));
140
121
}
141
122
}
123
+ }
142
124
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 ];
144
132
}
145
133
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 )
147
150
{
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 );
152
157
}
153
158
}
154
159
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 ;
156
166
}
157
167
}
0 commit comments