[EventDispatcher] handle lazy-callable invokable

This commit is contained in:
Nicolas Grekas 2019-10-18 14:16:09 +02:00
parent f771faf925
commit 9df4c7d32b
2 changed files with 31 additions and 8 deletions

View File

@ -111,14 +111,16 @@ class EventDispatcher implements EventDispatcherInterface
return null;
}
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
$listener[0] = $listener[0]();
$listener[1] = $listener[1] ?? '__invoke';
}
foreach ($this->listeners[$eventName] as $priority => &$listeners) {
foreach ($listeners as &$v) {
if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure) {
if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure && 2 >= \count($v)) {
$v[0] = $v[0]();
$v[1] = $v[1] ?? '__invoke';
}
if ($v === $listener) {
return $priority;
@ -165,14 +167,16 @@ class EventDispatcher implements EventDispatcherInterface
return;
}
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
$listener[0] = $listener[0]();
$listener[1] = $listener[1] ?? '__invoke';
}
foreach ($this->listeners[$eventName] as $priority => &$listeners) {
foreach ($listeners as $k => &$v) {
if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure) {
if ($v !== $listener && \is_array($v) && isset($v[0]) && $v[0] instanceof \Closure && 2 >= \count($v)) {
$v[0] = $v[0]();
$v[1] = $v[1] ?? '__invoke';
}
if ($v === $listener) {
unset($listeners[$k], $this->sorted[$eventName], $this->optimized[$eventName]);
@ -271,8 +275,9 @@ class EventDispatcher implements EventDispatcherInterface
foreach ($this->listeners[$eventName] as &$listeners) {
foreach ($listeners as $k => $listener) {
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
$listener[0] = $listener[0]();
$listener[1] = $listener[1] ?? '__invoke';
}
$this->sorted[$eventName][] = $listener;
}
@ -290,10 +295,11 @@ class EventDispatcher implements EventDispatcherInterface
foreach ($this->listeners[$eventName] as &$listeners) {
foreach ($listeners as &$listener) {
$closure = &$this->optimized[$eventName][];
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure) {
if (\is_array($listener) && isset($listener[0]) && $listener[0] instanceof \Closure && 2 >= \count($listener)) {
$closure = static function (...$args) use (&$listener, &$closure) {
if ($listener[0] instanceof \Closure) {
$listener[0] = $listener[0]();
$listener[1] = $listener[1] ?? '__invoke';
}
($closure = \Closure::fromCallable($listener))(...$args);
};

View File

@ -334,17 +334,26 @@ class EventDispatcherTest extends TestCase
public function testDispatchLazyListener()
{
$dispatcher = new TestWithDispatcher();
$called = 0;
$factory = function () use (&$called) {
$factory = function () use (&$called, $dispatcher) {
++$called;
return new TestWithDispatcher();
return $dispatcher;
};
$this->dispatcher->addListener('foo', [$factory, 'foo']);
$this->assertSame(0, $called);
$this->dispatcher->dispatch(new Event(), 'foo');
$this->assertFalse($dispatcher->invoked);
$this->dispatcher->dispatch(new Event(), 'foo');
$this->assertSame(1, $called);
$this->dispatcher->addListener('bar', [$factory]);
$this->assertSame(1, $called);
$this->dispatcher->dispatch(new Event(), 'bar');
$this->assertTrue($dispatcher->invoked);
$this->dispatcher->dispatch(new Event(), 'bar');
$this->assertSame(2, $called);
}
public function testRemoveFindsLazyListeners()
@ -472,12 +481,20 @@ class TestWithDispatcher
{
public $name;
public $dispatcher;
public $invoked = false;
public function foo($e, $name, $dispatcher)
{
$this->name = $name;
$this->dispatcher = $dispatcher;
}
public function __invoke($e, $name, $dispatcher)
{
$this->name = $name;
$this->dispatcher = $dispatcher;
$this->invoked = true;
}
}
class TestEventSubscriber implements EventSubscriberInterface