Permalink
Browse files

feature #10200 [EventDispatcher] simplified code for TraceableEventDi…

…spatcher (fabpot)

This PR was merged into the 2.5-dev branch.

Discussion
----------

[EventDispatcher] simplified code for TraceableEventDispatcher

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | n/a
| License       | MIT
| Doc PR        | n/a

Commits
-------

42e4c7b [EventDispatcher] simplified code for TraceableEventDispatcher
  • Loading branch information...
2 parents 6dfdb97 + 42e4c7b commit fe86efd3f256c5bda845cf23bf8a5400ae6a295e @fabpot fabpot committed Feb 6, 2014
@@ -28,11 +28,9 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
{
protected $logger;
protected $stopwatch;
- private $called = array();
+
+ private $called;
private $dispatcher;
- private $wrappedListeners = array();
- private $firstCalledEvent = array();
- private $lastEventId = 0;
/**
* Constructor.
@@ -46,6 +44,7 @@ public function __construct(EventDispatcherInterface $dispatcher, Stopwatch $sto
$this->dispatcher = $dispatcher;
$this->stopwatch = $stopwatch;
$this->logger = $logger;
+ $this->called = array();
}
/**
@@ -105,47 +104,19 @@ public function dispatch($eventName, Event $event = null)
$event = new Event();
}
- $eventId = ++$this->lastEventId;
-
- // Wrap all listeners before they are called
- $this->wrappedListeners[$eventId] = new \SplObjectStorage();
-
- $listeners = $this->dispatcher->getListeners($eventName);
-
- foreach ($listeners as $listener) {
- $this->dispatcher->removeListener($eventName, $listener);
- $wrapped = $this->wrapListener($eventName, $eventId, $listener);
- $this->wrappedListeners[$eventId][$wrapped] = $listener;
- $this->dispatcher->addListener($eventName, $wrapped);
- }
-
+ $this->preProcess($eventName);
$this->preDispatch($eventName, $event);
$e = $this->stopwatch->start($eventName, 'section');
- $this->firstCalledEvent[$eventName] = $this->stopwatch->start($eventName.'.loading', 'event_listener_loading');
-
- if (!$this->dispatcher->hasListeners($eventName)) {
- $this->firstCalledEvent[$eventName]->stop();
- }
-
$this->dispatcher->dispatch($eventName, $event);
- unset($this->firstCalledEvent[$eventName]);
-
if ($e->isStarted()) {
$e->stop();
}
$this->postDispatch($eventName, $event);
-
- // Unwrap all listeners after they are called
- foreach ($this->wrappedListeners[$eventId] as $wrapped) {
- $this->dispatcher->removeListener($eventName, $wrapped);
- $this->dispatcher->addListener($eventName, $this->wrappedListeners[$eventId][$wrapped]);
- }
-
- unset($this->wrappedListeners[$eventId]);
+ $this->postProcess($eventName);
return $event;
}
@@ -155,7 +126,15 @@ public function dispatch($eventName, Event $event = null)
*/
public function getCalledListeners()
{
- return $this->called;
+ $called = array();
+ foreach ($this->called as $eventName => $listeners) {
+ foreach ($listeners as $listener) {
+ $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName);
+ $called[$eventName.'.'.$info['pretty']] = $info;
+ }
+ }
+
+ return $called;
}
/**
@@ -164,12 +143,22 @@ public function getCalledListeners()
public function getNotCalledListeners()
{
$notCalled = array();
-
- foreach ($this->getListeners() as $name => $listeners) {
+ foreach ($this->getListeners() as $eventName => $listeners) {
foreach ($listeners as $listener) {
- $info = $this->getListenerInfo($listener, $name, null);
- if (!isset($this->called[$name.'.'.$info['pretty']])) {
- $notCalled[$name.'.'.$info['pretty']] = $info;
+ $called = false;
+ if (isset($this->called[$eventName])) {
+ foreach ($this->called[$eventName] as $l) {
+ if ($l->getWrappedListener() === $listener) {
+ $called = true;
+
+ break;
+ }
+ }
+ }
+
+ if (!$called) {
+ $info = $this->getListenerInfo($listener, $eventName);
+ $notCalled[$eventName.'.'.$info['pretty']] = $info;
}
}
}
@@ -191,64 +180,68 @@ public function __call($method, $arguments)
}
/**
- * This is a private method and must not be used.
+ * Called before dispatching the event.
*
- * This method is public because it is used in a closure.
- * Whenever Symfony will require PHP 5.4, this could be changed
- * to a proper private method.
+ * @param string $eventName The event name
+ * @param Event $event The event
*/
- public function logSkippedListeners($eventName, $eventId, Event $event, $listener)
+ protected function preDispatch($eventName, Event $event)
{
- if (null === $this->logger) {
- return;
- }
-
- $info = $this->getListenerInfo($listener, $eventName, $eventId);
-
- $this->logger->debug(sprintf('Listener "%s" stopped propagation of the event "%s".', $info['pretty'], $eventName));
-
- $skippedListeners = $this->getListeners($eventName);
- $skipped = false;
-
- foreach ($skippedListeners as $skippedListener) {
- $skippedListener = $this->unwrapListener($skippedListener, $eventId);
-
- if ($skipped) {
- $info = $this->getListenerInfo($skippedListener, $eventName, $eventId);
- $this->logger->debug(sprintf('Listener "%s" was not called for event "%s".', $info['pretty'], $eventName));
- }
-
- if ($skippedListener === $listener) {
- $skipped = true;
- }
- }
}
/**
- * This is a private method.
+ * Called after dispatching the event.
*
- * This method is public because it is used in a closure.
- * Whenever Symfony will require PHP 5.4, this could be changed
- * to a proper private method.
+ * @param string $eventName The event name
+ * @param Event $event The event
*/
- public function preListenerCall($eventName, $eventId, $listener)
+ protected function postDispatch($eventName, Event $event)
{
- // is it the first called listener?
- if (isset($this->firstCalledEvent[$eventName])) {
- $this->firstCalledEvent[$eventName]->stop();
+ }
- unset($this->firstCalledEvent[$eventName]);
+ private function preProcess($eventName)
+ {
+ foreach ($this->dispatcher->getListeners($eventName) as $listener) {
+ $this->dispatcher->removeListener($eventName, $listener);
+ $info = $this->getListenerInfo($listener, $eventName);
+ $name = isset($info['class']) ? $info['class'] : $info['type'];
+ $this->dispatcher->addListener($eventName, new WrappedListener($listener, $name, $this->stopwatch));
}
+ }
- $info = $this->getListenerInfo($listener, $eventName, $eventId);
+ private function postProcess($eventName)
+ {
+ $skipped = false;
+ foreach ($this->dispatcher->getListeners($eventName) as $listener) {
+ // Unwrap listener
+ $this->dispatcher->removeListener($eventName, $listener);
+ $this->dispatcher->addListener($eventName, $listener->getWrappedListener());
- if (null !== $this->logger) {
- $this->logger->debug(sprintf('Notified event "%s" to listener "%s".', $eventName, $info['pretty']));
- }
+ $info = $this->getListenerInfo($listener->getWrappedListener(), $eventName);
+ if ($listener->wasCalled()) {
+ if (null !== $this->logger) {
+ $this->logger->debug(sprintf('Notified event "%s" to listener "%s".', $eventName, $info['pretty']));
+ }
+
+ if (!isset($this->called[$eventName])) {
+ $this->called[$eventName] = new \SplObjectStorage();
+ }
- $this->called[$eventName.'.'.$info['pretty']] = $info;
+ $this->called[$eventName]->attach($listener);
+ }
+
+ if (null !== $this->logger && $skipped) {
+ $this->logger->debug(sprintf('Listener "%s" was not called for event "%s".', $info['pretty'], $eventName));
+ }
- return $this->stopwatch->start(isset($info['class']) ? $info['class'] : $info['type'], 'event_listener');
+ if ($listener->stoppedPropagation()) {
+ if (null !== $this->logger) {
+ $this->logger->debug(sprintf('Listener "%s" stopped propagation of the event "%s".', $info['pretty'], $eventName));
+ }
+
+ $skipped = true;
+ }
+ }
}
/**
@@ -259,10 +252,8 @@ public function preListenerCall($eventName, $eventId, $listener)
*
* @return array Information about the listener
*/
- private function getListenerInfo($listener, $eventName, $eventId)
+ private function getListenerInfo($listener, $eventName)
{
- $listener = $this->unwrapListener($listener, $eventId);
-
$info = array(
'event' => $eventName,
);
@@ -312,61 +303,4 @@ private function getListenerInfo($listener, $eventName, $eventId)
return $info;
}
-
- /**
- * Called before dispatching the event.
- *
- * @param string $eventName The event name
- * @param Event $event The event
- */
- protected function preDispatch($eventName, Event $event)
- {
- }
-
- /**
- * Called after dispatching the event.
- *
- * @param string $eventName The event name
- * @param Event $event The event
- */
- protected function postDispatch($eventName, Event $event)
- {
- }
-
- private function wrapListener($eventName, $eventId, $listener)
- {
- $self = $this;
-
- return function (Event $event) use ($self, $eventName, $eventId, $listener) {
- $e = $self->preListenerCall($eventName, $eventId, $listener);
-
- call_user_func($listener, $event, $eventName, $self);
-
- if ($e->isStarted()) {
- $e->stop();
- }
-
- if ($event->isPropagationStopped()) {
- $self->logSkippedListeners($eventName, $eventId, $event, $listener);
- }
- };
- }
-
- private function unwrapListener($listener, $eventId)
- {
- // get the original listener
- if (is_object($listener)) {
- if (null === $eventId) {
- foreach (array_keys($this->wrappedListeners) as $eventId) {
- if (isset($this->wrappedListeners[$eventId][$listener])) {
- return $this->wrappedListeners[$eventId][$listener];
- }
- }
- } elseif (isset($this->wrappedListeners[$eventId][$listener])) {
- return $this->wrappedListeners[$eventId][$listener];
- }
- }
-
- return $listener;
- }
}
@@ -0,0 +1,69 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\EventDispatcher\Debug;
+
+use Symfony\Component\Stopwatch\Stopwatch;
+use Symfony\Component\EventDispatcher\Event;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+
+/**
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class WrappedListener
+{
+ private $listener;
+ private $name;
+ private $called;
+ private $stoppedPropagation;
+ private $stopwatch;
+
+ public function __construct($listener, $name, Stopwatch $stopwatch)
+ {
+ $this->listener = $listener;
+ $this->name = $name;
+ $this->stopwatch = $stopwatch;
+ $this->called = false;
+ $this->stoppedPropagation = false;
+ }
+
+ public function getWrappedListener()
+ {
+ return $this->listener;
+ }
+
+ public function wasCalled()
+ {
+ return $this->called;
+ }
+
+ public function stoppedPropagation()
+ {
+ return $this->stoppedPropagation;
+ }
+
+ public function __invoke(Event $event, $eventName, EventDispatcherInterface $dispatcher)
+ {
+ $this->called = true;
+
+ $e = $this->stopwatch->start($this->name, 'event_listener');
+
+ call_user_func($this->listener, $event, $eventName, $dispatcher);
+
+ if ($e->isStarted()) {
+ $e->stop();
+ }
+
+ if ($event->isPropagationStopped()) {
+ $this->stoppedPropagation = true;
+ }
+ }
+}
@@ -32,14 +32,10 @@ public function testStopwatchSections()
$this->assertEquals(array(
'__section__',
'kernel.request',
- 'kernel.request.loading',
'kernel.controller',
- 'kernel.controller.loading',
'controller',
'kernel.response',
- 'kernel.response.loading',
'kernel.terminate',
- 'kernel.terminate.loading',
), array_keys($events));
}

0 comments on commit fe86efd

Please sign in to comment.