Mercurial > hg > isophonics-drupal-site
comparison core/lib/Drupal/Component/EventDispatcher/ContainerAwareEventDispatcher.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 1fec387a4317 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\Component\EventDispatcher; | |
4 | |
5 use Symfony\Component\DependencyInjection\ContainerInterface; | |
6 use Symfony\Component\EventDispatcher\Event; | |
7 use Symfony\Component\EventDispatcher\EventDispatcherInterface; | |
8 use Symfony\Component\EventDispatcher\EventSubscriberInterface; | |
9 | |
10 /** | |
11 * A performance optimized container aware event dispatcher. | |
12 * | |
13 * This version of the event dispatcher contains the following optimizations | |
14 * in comparison to the Symfony event dispatcher component: | |
15 * | |
16 * <dl> | |
17 * <dt>Faster instantiation of the event dispatcher service</dt> | |
18 * <dd> | |
19 * Instead of calling <code>addSubscriberService</code> once for each | |
20 * subscriber, a precompiled array of listener definitions is passed | |
21 * directly to the constructor. This is faster by roughly an order of | |
22 * magnitude. The listeners are collected and prepared using a compiler | |
23 * pass. | |
24 * </dd> | |
25 * <dt>Lazy instantiation of listeners</dt> | |
26 * <dd> | |
27 * Services are only retrieved from the container just before invocation. | |
28 * Especially when dispatching the KernelEvents::REQUEST event, this leads | |
29 * to a more timely invocation of the first listener. Overall dispatch | |
30 * runtime is not affected by this change though. | |
31 * </dd> | |
32 * </dl> | |
33 */ | |
34 class ContainerAwareEventDispatcher implements EventDispatcherInterface { | |
35 | |
36 /** | |
37 * The service container. | |
38 * | |
39 * @var \Symfony\Component\DependencyInjection\ContainerInterface; | |
40 */ | |
41 protected $container; | |
42 | |
43 /** | |
44 * Listener definitions. | |
45 * | |
46 * A nested array of listener definitions keyed by event name and priority. | |
47 * A listener definition is an associative array with one of the following key | |
48 * value pairs: | |
49 * - callable: A callable listener | |
50 * - service: An array of the form [service id, method] | |
51 * | |
52 * A service entry will be resolved to a callable only just before its | |
53 * invocation. | |
54 * | |
55 * @var array | |
56 */ | |
57 protected $listeners; | |
58 | |
59 /** | |
60 * Whether listeners need to be sorted prior to dispatch, keyed by event name. | |
61 * | |
62 * @var TRUE[] | |
63 */ | |
64 protected $unsorted; | |
65 | |
66 /** | |
67 * Constructs a container aware event dispatcher. | |
68 * | |
69 * @param \Symfony\Component\DependencyInjection\ContainerInterface $container | |
70 * The service container. | |
71 * @param array $listeners | |
72 * A nested array of listener definitions keyed by event name and priority. | |
73 * The array is expected to be ordered by priority. A listener definition is | |
74 * an associative array with one of the following key value pairs: | |
75 * - callable: A callable listener | |
76 * - service: An array of the form [service id, method] | |
77 * A service entry will be resolved to a callable only just before its | |
78 * invocation. | |
79 */ | |
80 public function __construct(ContainerInterface $container, array $listeners = []) { | |
81 $this->container = $container; | |
82 $this->listeners = $listeners; | |
83 $this->unsorted = []; | |
84 } | |
85 | |
86 /** | |
87 * {@inheritdoc} | |
88 */ | |
89 public function dispatch($event_name, Event $event = NULL) { | |
90 if ($event === NULL) { | |
91 $event = new Event(); | |
92 } | |
93 | |
94 if (isset($this->listeners[$event_name])) { | |
95 // Sort listeners if necessary. | |
96 if (isset($this->unsorted[$event_name])) { | |
97 krsort($this->listeners[$event_name]); | |
98 unset($this->unsorted[$event_name]); | |
99 } | |
100 | |
101 // Invoke listeners and resolve callables if necessary. | |
102 foreach ($this->listeners[$event_name] as $priority => &$definitions) { | |
103 foreach ($definitions as $key => &$definition) { | |
104 if (!isset($definition['callable'])) { | |
105 $definition['callable'] = [$this->container->get($definition['service'][0]), $definition['service'][1]]; | |
106 } | |
107 | |
108 $definition['callable']($event, $event_name, $this); | |
109 if ($event->isPropagationStopped()) { | |
110 return $event; | |
111 } | |
112 } | |
113 } | |
114 } | |
115 | |
116 return $event; | |
117 } | |
118 | |
119 /** | |
120 * {@inheritdoc} | |
121 */ | |
122 public function getListeners($event_name = NULL) { | |
123 $result = []; | |
124 | |
125 if ($event_name === NULL) { | |
126 // If event name was omitted, collect all listeners of all events. | |
127 foreach (array_keys($this->listeners) as $event_name) { | |
128 $listeners = $this->getListeners($event_name); | |
129 if (!empty($listeners)) { | |
130 $result[$event_name] = $listeners; | |
131 } | |
132 } | |
133 } | |
134 elseif (isset($this->listeners[$event_name])) { | |
135 // Sort listeners if necessary. | |
136 if (isset($this->unsorted[$event_name])) { | |
137 krsort($this->listeners[$event_name]); | |
138 unset($this->unsorted[$event_name]); | |
139 } | |
140 | |
141 // Collect listeners and resolve callables if necessary. | |
142 foreach ($this->listeners[$event_name] as $priority => &$definitions) { | |
143 foreach ($definitions as $key => &$definition) { | |
144 if (!isset($definition['callable'])) { | |
145 $definition['callable'] = [$this->container->get($definition['service'][0]), $definition['service'][1]]; | |
146 } | |
147 | |
148 $result[] = $definition['callable']; | |
149 } | |
150 } | |
151 } | |
152 | |
153 return $result; | |
154 } | |
155 | |
156 /** | |
157 * {@inheritdoc} | |
158 */ | |
159 public function getListenerPriority($eventName, $listener) { | |
160 // Parts copied from \Symfony\Component\EventDispatcher, that's why you see | |
161 // a yoda condition here. | |
162 if (!isset($this->listeners[$eventName])) { | |
163 return; | |
164 } | |
165 foreach ($this->listeners[$eventName] as $priority => $listeners) { | |
166 if (FALSE !== ($key = array_search(['callable' => $listener], $listeners, TRUE))) { | |
167 return $priority; | |
168 } | |
169 } | |
170 // Resolve service definitions if the listener has not been found so far. | |
171 foreach ($this->listeners[$eventName] as $priority => &$definitions) { | |
172 foreach ($definitions as $key => &$definition) { | |
173 if (!isset($definition['callable'])) { | |
174 // Once the callable is retrieved we keep it for subsequent method | |
175 // invocations on this class. | |
176 $definition['callable'] = [$this->container->get($definition['service'][0]), $definition['service'][1]]; | |
177 if ($definition['callable'] === $listener) { | |
178 return $priority; | |
179 } | |
180 } | |
181 } | |
182 } | |
183 } | |
184 | |
185 /** | |
186 * {@inheritdoc} | |
187 */ | |
188 public function hasListeners($event_name = NULL) { | |
189 return (bool) count($this->getListeners($event_name)); | |
190 } | |
191 | |
192 /** | |
193 * {@inheritdoc} | |
194 */ | |
195 public function addListener($event_name, $listener, $priority = 0) { | |
196 $this->listeners[$event_name][$priority][] = ['callable' => $listener]; | |
197 $this->unsorted[$event_name] = TRUE; | |
198 } | |
199 | |
200 /** | |
201 * {@inheritdoc} | |
202 */ | |
203 public function removeListener($event_name, $listener) { | |
204 if (!isset($this->listeners[$event_name])) { | |
205 return; | |
206 } | |
207 | |
208 foreach ($this->listeners[$event_name] as $priority => $definitions) { | |
209 foreach ($definitions as $key => $definition) { | |
210 if (!isset($definition['callable'])) { | |
211 if (!$this->container->initialized($definition['service'][0])) { | |
212 continue; | |
213 } | |
214 $definition['callable'] = [$this->container->get($definition['service'][0]), $definition['service'][1]]; | |
215 } | |
216 | |
217 if ($definition['callable'] === $listener) { | |
218 unset($this->listeners[$event_name][$priority][$key]); | |
219 } | |
220 } | |
221 } | |
222 } | |
223 | |
224 /** | |
225 * {@inheritdoc} | |
226 */ | |
227 public function addSubscriber(EventSubscriberInterface $subscriber) { | |
228 foreach ($subscriber->getSubscribedEvents() as $event_name => $params) { | |
229 if (is_string($params)) { | |
230 $this->addListener($event_name, [$subscriber, $params]); | |
231 } | |
232 elseif (is_string($params[0])) { | |
233 $this->addListener($event_name, [$subscriber, $params[0]], isset($params[1]) ? $params[1] : 0); | |
234 } | |
235 else { | |
236 foreach ($params as $listener) { | |
237 $this->addListener($event_name, [$subscriber, $listener[0]], isset($listener[1]) ? $listener[1] : 0); | |
238 } | |
239 } | |
240 } | |
241 } | |
242 | |
243 /** | |
244 * {@inheritdoc} | |
245 */ | |
246 public function removeSubscriber(EventSubscriberInterface $subscriber) { | |
247 foreach ($subscriber->getSubscribedEvents() as $event_name => $params) { | |
248 if (is_array($params) && is_array($params[0])) { | |
249 foreach ($params as $listener) { | |
250 $this->removeListener($event_name, [$subscriber, $listener[0]]); | |
251 } | |
252 } | |
253 else { | |
254 $this->removeListener($event_name, [$subscriber, is_string($params) ? $params : $params[0]]); | |
255 } | |
256 } | |
257 } | |
258 | |
259 } |