9b5c689ad7137d302c362e7fc9dfaaf627ee19d2
[yaffs-website] / vendor / symfony / event-dispatcher / Debug / TraceableEventDispatcher.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\EventDispatcher\Debug;
13
14 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
15 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
16 use Symfony\Component\EventDispatcher\Event;
17 use Symfony\Component\Stopwatch\Stopwatch;
18 use Psr\Log\LoggerInterface;
19
20 /**
21  * Collects some data about event listeners.
22  *
23  * This event dispatcher delegates the dispatching to another one.
24  *
25  * @author Fabien Potencier <fabien@symfony.com>
26  */
27 class TraceableEventDispatcher implements TraceableEventDispatcherInterface
28 {
29     protected $logger;
30     protected $stopwatch;
31
32     private $called;
33     private $dispatcher;
34     private $wrappedListeners;
35
36     public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $stopwatch, LoggerInterface $logger = null)
37     {
38         $this->dispatcher = $dispatcher;
39         $this->stopwatch = $stopwatch;
40         $this->logger = $logger;
41         $this->called = array();
42         $this->wrappedListeners = array();
43     }
44
45     /**
46      * {@inheritdoc}
47      */
48     public function addListener($eventName, $listener, $priority = 0)
49     {
50         $this->dispatcher->addListener($eventName, $listener, $priority);
51     }
52
53     /**
54      * {@inheritdoc}
55      */
56     public function addSubscriber(EventSubscriberInterface $subscriber)
57     {
58         $this->dispatcher->addSubscriber($subscriber);
59     }
60
61     /**
62      * {@inheritdoc}
63      */
64     public function removeListener($eventName, $listener)
65     {
66         if (isset($this->wrappedListeners[$eventName])) {
67             foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) {
68                 if ($wrappedListener->getWrappedListener() === $listener) {
69                     $listener = $wrappedListener;
70                     unset($this->wrappedListeners[$eventName][$index]);
71                     break;
72                 }
73             }
74         }
75
76         return $this->dispatcher->removeListener($eventName, $listener);
77     }
78
79     /**
80      * {@inheritdoc}
81      */
82     public function removeSubscriber(EventSubscriberInterface $subscriber)
83     {
84         return $this->dispatcher->removeSubscriber($subscriber);
85     }
86
87     /**
88      * {@inheritdoc}
89      */
90     public function getListeners($eventName = null)
91     {
92         return $this->dispatcher->getListeners($eventName);
93     }
94
95     /**
96      * {@inheritdoc}
97      */
98     public function getListenerPriority($eventName, $listener)
99     {
100         // we might have wrapped listeners for the event (if called while dispatching)
101         // in that case get the priority by wrapper
102         if (isset($this->wrappedListeners[$eventName])) {
103             foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) {
104                 if ($wrappedListener->getWrappedListener() === $listener) {
105                     return $this->dispatcher->getListenerPriority($eventName, $wrappedListener);
106                 }
107             }
108         }
109
110         return $this->dispatcher->getListenerPriority($eventName, $listener);
111     }
112
113     /**
114      * {@inheritdoc}
115      */
116     public function hasListeners($eventName = null)
117     {
118         return $this->dispatcher->hasListeners($eventName);
119     }
120
121     /**
122      * {@inheritdoc}
123      */
124     public function dispatch($eventName, Event $event = null)
125     {
126         if (null === $event) {
127             $event = new Event();
128         }
129
130         if (null !== $this->logger && $event->isPropagationStopped()) {
131             $this->logger->debug(sprintf('The "%s" event is already stopped. No listeners have been called.', $eventName));
132         }
133
134         $this->preProcess($eventName);
135         $this->preDispatch($eventName, $event);
136
137         $e = $this->stopwatch->start($eventName, 'section');
138
139         $this->dispatcher->dispatch($eventName, $event);
140
141         if ($e->isStarted()) {
142             $e->stop();
143         }
144
145         $this->postDispatch($eventName, $event);
146         $this->postProcess($eventName);
147
148         return $event;
149     }
150
151     /**
152      * {@inheritdoc}
153      */
154     public function getCalledListeners()
155     {
156         $called = array();
157         foreach ($this->called as $eventName => $listeners) {
158             foreach ($listeners as $listener) {
159                 $called[$eventName.'.'.$listener->getPretty()] = $listener->getInfo($eventName);
160             }
161         }
162
163         return $called;
164     }
165
166     /**
167      * {@inheritdoc}
168      */
169     public function getNotCalledListeners()
170     {
171         try {
172             $allListeners = $this->getListeners();
173         } catch (\Exception $e) {
174             if (null !== $this->logger) {
175                 $this->logger->info('An exception was thrown while getting the uncalled listeners.', array('exception' => $e));
176             }
177
178             // unable to retrieve the uncalled listeners
179             return array();
180         }
181
182         $notCalled = array();
183         foreach ($allListeners as $eventName => $listeners) {
184             foreach ($listeners as $listener) {
185                 $called = false;
186                 if (isset($this->called[$eventName])) {
187                     foreach ($this->called[$eventName] as $l) {
188                         if ($l->getWrappedListener() === $listener) {
189                             $called = true;
190
191                             break;
192                         }
193                     }
194                 }
195
196                 if (!$called) {
197                     if (!$listener instanceof WrappedListener) {
198                         $listener = new WrappedListener($listener, null, $this->stopwatch, $this);
199                     }
200                     $notCalled[$eventName.'.'.$listener->getPretty()] = $listener->getInfo($eventName);
201                 }
202             }
203         }
204
205         uasort($notCalled, array($this, 'sortListenersByPriority'));
206
207         return $notCalled;
208     }
209
210     public function reset()
211     {
212         $this->called = array();
213     }
214
215     /**
216      * Proxies all method calls to the original event dispatcher.
217      *
218      * @param string $method    The method name
219      * @param array  $arguments The method arguments
220      *
221      * @return mixed
222      */
223     public function __call($method, $arguments)
224     {
225         return call_user_func_array(array($this->dispatcher, $method), $arguments);
226     }
227
228     /**
229      * Called before dispatching the event.
230      *
231      * @param string $eventName The event name
232      * @param Event  $event     The event
233      */
234     protected function preDispatch($eventName, Event $event)
235     {
236     }
237
238     /**
239      * Called after dispatching the event.
240      *
241      * @param string $eventName The event name
242      * @param Event  $event     The event
243      */
244     protected function postDispatch($eventName, Event $event)
245     {
246     }
247
248     private function preProcess($eventName)
249     {
250         foreach ($this->dispatcher->getListeners($eventName) as $listener) {
251             $priority = $this->getListenerPriority($eventName, $listener);
252             $wrappedListener = new WrappedListener($listener, null, $this->stopwatch, $this);
253             $this->wrappedListeners[$eventName][] = $wrappedListener;
254             $this->dispatcher->removeListener($eventName, $listener);
255             $this->dispatcher->addListener($eventName, $wrappedListener, $priority);
256         }
257     }
258
259     private function postProcess($eventName)
260     {
261         unset($this->wrappedListeners[$eventName]);
262         $skipped = false;
263         foreach ($this->dispatcher->getListeners($eventName) as $listener) {
264             if (!$listener instanceof WrappedListener) { // #12845: a new listener was added during dispatch.
265                 continue;
266             }
267             // Unwrap listener
268             $priority = $this->getListenerPriority($eventName, $listener);
269             $this->dispatcher->removeListener($eventName, $listener);
270             $this->dispatcher->addListener($eventName, $listener->getWrappedListener(), $priority);
271
272             if (null !== $this->logger) {
273                 $context = array('event' => $eventName, 'listener' => $listener->getPretty());
274             }
275
276             if ($listener->wasCalled()) {
277                 if (null !== $this->logger) {
278                     $this->logger->debug('Notified event "{event}" to listener "{listener}".', $context);
279                 }
280
281                 if (!isset($this->called[$eventName])) {
282                     $this->called[$eventName] = new \SplObjectStorage();
283                 }
284
285                 $this->called[$eventName]->attach($listener);
286             }
287
288             if (null !== $this->logger && $skipped) {
289                 $this->logger->debug('Listener "{listener}" was not called for event "{event}".', $context);
290             }
291
292             if ($listener->stoppedPropagation()) {
293                 if (null !== $this->logger) {
294                     $this->logger->debug('Listener "{listener}" stopped propagation of the event "{event}".', $context);
295                 }
296
297                 $skipped = true;
298             }
299         }
300     }
301
302     private function sortListenersByPriority($a, $b)
303     {
304         if (is_int($a['priority']) && !is_int($b['priority'])) {
305             return 1;
306         }
307
308         if (!is_int($a['priority']) && is_int($b['priority'])) {
309             return -1;
310         }
311
312         if ($a['priority'] === $b['priority']) {
313             return 0;
314         }
315
316         if ($a['priority'] > $b['priority']) {
317             return -1;
318         }
319
320         return 1;
321     }
322 }