11
11
12
12
namespace Symfony \Bridge \Doctrine \DependencyInjection \CompilerPass ;
13
13
14
- use Symfony \Component \DependencyInjection \ContainerBuilder ;
15
- use Symfony \Component \DependencyInjection \Reference ;
16
14
use Symfony \Component \DependencyInjection \Compiler \CompilerPassInterface ;
15
+ use Symfony \Component \DependencyInjection \ContainerBuilder ;
17
16
use Symfony \Component \DependencyInjection \Exception \InvalidArgumentException ;
18
17
use Symfony \Component \DependencyInjection \Exception \RuntimeException ;
18
+ use Symfony \Component \DependencyInjection \Reference ;
19
19
20
20
/**
21
21
* Registers event listeners and subscribers to the available doctrine connections.
22
22
*
23
23
* @author Jeremy Mikola <jmikola@gmail.com>
24
24
* @author Alexander <iam.asm89@gmail.com>
25
+ * @author David Maicher <mail@dmaicher.de>
25
26
*/
26
27
class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface
27
28
{
28
29
private $ connections ;
29
- private $ container ;
30
30
private $ eventManagers ;
31
31
private $ managerTemplate ;
32
32
private $ tagPrefix ;
@@ -53,97 +53,97 @@ public function process(ContainerBuilder $container)
53
53
return ;
54
54
}
55
55
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 ;
64
56
$ 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
+ }
76
60
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 ))));
80
72
}
81
- }
82
- }
83
73
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 )));
96
75
}
97
76
}
98
77
}
99
78
100
- private function groupByConnection ( array $ services , $ isListener = false )
79
+ private function addTaggedListeners ( ContainerBuilder $ container )
101
80
{
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
+ }
106
90
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 ))));
118
95
}
119
96
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 );
131
99
}
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 )));
132
103
}
133
104
}
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
+ }
134
112
135
- return $ grouped ;
113
+ return $ this -> eventManagers [ $ name ] ;
136
114
}
137
115
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 )
139
132
{
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 );
144
139
}
145
140
}
146
141
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 ;
148
148
}
149
149
}
0 commit comments