bug #14355 [2.6][EventDispatcher] make listeners removable from an executed listener (xabbuh)
This PR was merged into the 2.6 branch.
Discussion
----------
[2.6][EventDispatcher] make listeners removable from an executed listener
| Q | A
| ------------- | ---
| Bug fix? | yes
| New feature? | no
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | #13972
| License | MIT
| Doc PR |
Commits
-------
f36803e
[EventDispatcher] make listeners removable from an executed listener
This commit is contained in:
commit
f48cc1ba34
@ -31,6 +31,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
|||||||
|
|
||||||
private $called;
|
private $called;
|
||||||
private $dispatcher;
|
private $dispatcher;
|
||||||
|
private $wrappedListeners;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
@ -45,6 +46,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
|||||||
$this->stopwatch = $stopwatch;
|
$this->stopwatch = $stopwatch;
|
||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
$this->called = array();
|
$this->called = array();
|
||||||
|
$this->wrappedListeners = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -68,6 +70,16 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
|||||||
*/
|
*/
|
||||||
public function removeListener($eventName, $listener)
|
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);
|
return $this->dispatcher->removeListener($eventName, $listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,12 +228,15 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface
|
|||||||
$this->dispatcher->removeListener($eventName, $listener);
|
$this->dispatcher->removeListener($eventName, $listener);
|
||||||
$info = $this->getListenerInfo($listener, $eventName);
|
$info = $this->getListenerInfo($listener, $eventName);
|
||||||
$name = isset($info['class']) ? $info['class'] : $info['type'];
|
$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)
|
private function postProcess($eventName)
|
||||||
{
|
{
|
||||||
|
unset($this->wrappedListeners[$eventName]);
|
||||||
$skipped = false;
|
$skipped = false;
|
||||||
foreach ($this->dispatcher->getListeners($eventName) as $listener) {
|
foreach ($this->dispatcher->getListeners($eventName) as $listener) {
|
||||||
if (!$listener instanceof WrappedListener) { // #12845: a new listener was added during dispatch.
|
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 object $listener The listener
|
||||||
* @param string $eventName The event name
|
* @param string $eventName The event name
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace Symfony\Component\EventDispatcher\Tests\Debug;
|
namespace Symfony\Component\EventDispatcher\Tests\Debug;
|
||||||
|
|
||||||
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
|
use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher;
|
||||||
|
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||||
use Symfony\Component\EventDispatcher\Event;
|
use Symfony\Component\EventDispatcher\Event;
|
||||||
@ -174,6 +175,19 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase
|
|||||||
$dispatcher->dispatch('foo');
|
$dispatcher->dispatch('foo');
|
||||||
$this->assertTrue($nestedCall);
|
$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
|
class EventSubscriber implements EventSubscriberInterface
|
||||||
|
Reference in New Issue
Block a user