[EventDispatcher] simplified code for TraceableEventDispatcher
This commit is contained in:
parent
22970e007e
commit
42e4c7bddf
@ -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 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->stopwatch = $stopwatch;
|
||||
$this->logger = $logger;
|
||||
$this->called = array();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,47 +104,19 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
$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 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
*/
|
||||
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 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
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 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* 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]->attach($listener);
|
||||
}
|
||||
|
||||
if (null !== $this->logger && $skipped) {
|
||||
$this->logger->debug(sprintf('Listener "%s" was not called for event "%s".', $info['pretty'], $eventName));
|
||||
}
|
||||
|
||||
if ($listener->stoppedPropagation()) {
|
||||
if (null !== $this->logger) {
|
||||
$this->logger->debug(sprintf('Listener "%s" stopped propagation of the event "%s".', $info['pretty'], $eventName));
|
||||
}
|
||||
|
||||
$skipped = true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->called[$eventName.'.'.$info['pretty']] = $info;
|
||||
|
||||
return $this->stopwatch->start(isset($info['class']) ? $info['class'] : $info['type'], 'event_listener');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -259,10 +252,8 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
*
|
||||
* @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 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
||||
|
||||
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 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
|
||||
$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));
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user