annotate vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php @ 14:1fec387a4317

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