diff --git a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php index 2119b81b3a..e51aa2fafc 100644 --- a/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php +++ b/src/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php @@ -31,6 +31,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface private $called; private $dispatcher; + private $wrappedListeners; /** * Constructor. @@ -45,6 +46,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface $this->stopwatch = $stopwatch; $this->logger = $logger; $this->called = array(); + $this->wrappedListeners = array(); } /** @@ -68,6 +70,16 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface */ public function removeListener($eventName, $listener) { + if (isset($this->wrappedListeners[$eventName])) { + foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) { + if ($wrappedListener->getWrappedListener() === $listener) { + $listener = $wrappedListener; + unset($this->wrappedListeners[$eventName][$index]); + break; + } + } + } + return $this->dispatcher->removeListener($eventName, $listener); } @@ -216,12 +228,15 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface $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, $this)); + $wrappedListener = new WrappedListener($listener, $name, $this->stopwatch, $this); + $this->wrappedListeners[$eventName][] = $wrappedListener; + $this->dispatcher->addListener($eventName, $wrappedListener); } } private function postProcess($eventName) { + unset($this->wrappedListeners[$eventName]); $skipped = false; foreach ($this->dispatcher->getListeners($eventName) as $listener) { if (!$listener instanceof WrappedListener) { // #12845: a new listener was added during dispatch. @@ -259,7 +274,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface } /** - * Returns information about the listener + * Returns information about the listener. * * @param object $listener The listener * @param string $eventName The event name diff --git a/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php index 68b9523654..24e60024ff 100644 --- a/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php +++ b/src/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\EventDispatcher\Tests\Debug; use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\Event; @@ -174,6 +175,19 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase $dispatcher->dispatch('foo'); $this->assertTrue($nestedCall); } + + public function testListenerCanRemoveItselfWhenExecuted() + { + $eventDispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $listener1 = function ($event, $eventName, EventDispatcherInterface $dispatcher) use (&$listener1) { + $dispatcher->removeListener('foo', $listener1); + }; + $eventDispatcher->addListener('foo', $listener1); + $eventDispatcher->addListener('foo', function () {}); + $eventDispatcher->dispatch('foo'); + + $this->assertCount(1, $eventDispatcher->getListeners('foo'), 'expected listener1 to be removed'); + } } class EventSubscriber implements EventSubscriberInterface