View source
<?php
namespace Drupal\Tests\Component\EventDispatcher;
use Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher;
use PHPUnit\Framework\TestCase;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\EventDispatcher\Event as SymfonyEvent;
use Symfony\Component\EventDispatcher\GenericEvent;
use Drupal\Component\EventDispatcher\Event;
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
class ContainerAwareEventDispatcherTest extends TestCase {
use ExpectDeprecationTrait;
const PREFOO = 'pre.foo';
const POSTFOO = 'post.foo';
const PREBAR = 'pre.bar';
const POSTBAR = 'post.bar';
private $dispatcher;
private $listener;
protected function setUp() : void {
$this->dispatcher = $this
->createEventDispatcher();
$this->listener = new TestEventListener();
}
protected function tearDown() : void {
$this->dispatcher = NULL;
$this->listener = NULL;
}
protected function createEventDispatcher() {
$container = new Container();
return new ContainerAwareEventDispatcher($container);
}
public function testGetListenersWithCallables() {
$container = $this
->getMockBuilder(ContainerInterface::class)
->getMock();
$container
->expects($this
->never())
->method($this
->anything());
$firstListener = new CallableClass();
$secondListener = function () {
};
$thirdListener = [
new TestEventListener(),
'preFoo',
];
$listeners = [
'test_event' => [
0 => [
[
'callable' => $firstListener,
],
[
'callable' => $secondListener,
],
[
'callable' => $thirdListener,
],
],
],
];
$dispatcher = new ContainerAwareEventDispatcher($container, $listeners);
$actualListeners = $dispatcher
->getListeners();
$expectedListeners = [
'test_event' => [
$firstListener,
$secondListener,
$thirdListener,
],
];
$this
->assertSame($expectedListeners, $actualListeners);
}
public function testDispatchWithCallables() {
$container = $this
->getMockBuilder(ContainerInterface::class)
->getMock();
$container
->expects($this
->never())
->method($this
->anything());
$firstListener = new CallableClass();
$secondListener = function () {
};
$thirdListener = [
new TestEventListener(),
'preFoo',
];
$listeners = [
'test_event' => [
0 => [
[
'callable' => $firstListener,
],
[
'callable' => $secondListener,
],
[
'callable' => $thirdListener,
],
],
],
];
$dispatcher = new ContainerAwareEventDispatcher($container, $listeners);
$dispatcher
->dispatch(new Event(), 'test_event');
$this
->assertTrue($thirdListener[0]->preFooInvoked);
}
public function testGetListenersWithServices() {
$container = new ContainerBuilder();
$container
->register('listener_service', TestEventListener::class);
$listeners = [
'test_event' => [
0 => [
[
'service' => [
'listener_service',
'preFoo',
],
],
],
],
];
$dispatcher = new ContainerAwareEventDispatcher($container, $listeners);
$actualListeners = $dispatcher
->getListeners();
$listenerService = $container
->get('listener_service');
$expectedListeners = [
'test_event' => [
[
$listenerService,
'preFoo',
],
],
];
$this
->assertSame($expectedListeners, $actualListeners);
}
public function testDispatchArgumentOrderDeprecation() {
$this
->expectDeprecation('Calling the Symfony\\Component\\EventDispatcher\\EventDispatcherInterface::dispatch() method with a string event name as the first argument is deprecated in drupal:9.1.0, an Event object will be required instead in drupal:10.0.0. See https://www.drupal.org/node/3154407');
$container = new ContainerBuilder();
$dispatcher = new ContainerAwareEventDispatcher($container, []);
$dispatcher
->dispatch('foo');
}
public function testSymfonyEventDeprecation() {
$this
->expectDeprecation('Symfony\\Component\\EventDispatcher\\Event is deprecated in drupal:9.1.0 and will be replaced by Symfony\\Contracts\\EventDispatcher\\Event in drupal:10.0.0. A new Drupal\\Component\\EventDispatcher\\Event class is available to bridge the two versions of the class. See https://www.drupal.org/node/3159012');
$container = new ContainerBuilder();
$dispatcher = new ContainerAwareEventDispatcher($container, []);
$dispatcher
->dispatch(new SymfonyEvent());
}
public function testSymfonyEventDispatching() {
$container = new ContainerBuilder();
$dispatcher = new ContainerAwareEventDispatcher($container, []);
$dispatcher
->dispatch(new GenericEvent());
}
public function testSymfonyInheritedEventDeprecation() {
$this
->expectDeprecation('Symfony\\Component\\EventDispatcher\\Event is deprecated in drupal:9.1.0 and will be replaced by Symfony\\Contracts\\EventDispatcher\\Event in drupal:10.0.0. A new Drupal\\Component\\EventDispatcher\\Event class is available to bridge the two versions of the class. See https://www.drupal.org/node/3159012');
$container = new ContainerBuilder();
$dispatcher = new ContainerAwareEventDispatcher($container, []);
$dispatcher
->dispatch(new SymfonyInheritedEvent());
}
public function testDispatchWithServices() {
$container = new ContainerBuilder();
$container
->register('listener_service', TestEventListener::class);
$listeners = [
'test_event' => [
0 => [
[
'service' => [
'listener_service',
'preFoo',
],
],
],
],
];
$dispatcher = new ContainerAwareEventDispatcher($container, $listeners);
$dispatcher
->dispatch(new Event(), 'test_event');
$listenerService = $container
->get('listener_service');
$this
->assertTrue($listenerService->preFooInvoked);
}
public function testRemoveService() {
$container = new ContainerBuilder();
$container
->register('listener_service', TestEventListener::class);
$container
->register('other_listener_service', TestEventListener::class);
$listeners = [
'test_event' => [
0 => [
[
'service' => [
'listener_service',
'preFoo',
],
],
[
'service' => [
'other_listener_service',
'preFoo',
],
],
],
],
];
$dispatcher = new ContainerAwareEventDispatcher($container, $listeners);
$listenerService = $container
->get('listener_service');
$dispatcher
->removeListener('test_event', [
$listenerService,
'preFoo',
]);
$this
->assertFalse($container
->initialized('other_listener_service'));
$dispatcher
->dispatch(new Event(), 'test_event');
$this
->assertFalse($listenerService->preFooInvoked);
$otherService = $container
->get('other_listener_service');
$this
->assertTrue($otherService->preFooInvoked);
}
public function testGetListenerPriorityWithServices() {
$container = new ContainerBuilder();
$container
->register('listener_service', TestEventListener::class);
$listeners = [
'test_event' => [
5 => [
[
'service' => [
'listener_service',
'preFoo',
],
],
],
],
];
$dispatcher = new ContainerAwareEventDispatcher($container, $listeners);
$listenerService = $container
->get('listener_service');
$actualPriority = $dispatcher
->getListenerPriority('test_event', [
$listenerService,
'preFoo',
]);
$this
->assertSame(5, $actualPriority);
}
public function testInitialState() {
$this
->assertEquals([], $this->dispatcher
->getListeners());
$this
->assertFalse($this->dispatcher
->hasListeners(self::PREFOO));
$this
->assertFalse($this->dispatcher
->hasListeners(self::POSTFOO));
}
public function testAddListener() {
$this->dispatcher
->addListener('pre.foo', [
$this->listener,
'preFoo',
]);
$this->dispatcher
->addListener('post.foo', [
$this->listener,
'postFoo',
]);
$this
->assertTrue($this->dispatcher
->hasListeners());
$this
->assertTrue($this->dispatcher
->hasListeners(self::PREFOO));
$this
->assertTrue($this->dispatcher
->hasListeners(self::POSTFOO));
$this
->assertCount(1, $this->dispatcher
->getListeners(self::PREFOO));
$this
->assertCount(1, $this->dispatcher
->getListeners(self::POSTFOO));
$this
->assertCount(2, $this->dispatcher
->getListeners());
}
public function testGetListenersSortsByPriority() {
$listener1 = new TestEventListener();
$listener2 = new TestEventListener();
$listener3 = new TestEventListener();
$listener1->name = '1';
$listener2->name = '2';
$listener3->name = '3';
$this->dispatcher
->addListener('pre.foo', [
$listener1,
'preFoo',
], -10);
$this->dispatcher
->addListener('pre.foo', [
$listener2,
'preFoo',
], 10);
$this->dispatcher
->addListener('pre.foo', [
$listener3,
'preFoo',
]);
$expected = [
[
$listener2,
'preFoo',
],
[
$listener3,
'preFoo',
],
[
$listener1,
'preFoo',
],
];
$this
->assertSame($expected, $this->dispatcher
->getListeners('pre.foo'));
}
public function testGetAllListenersSortsByPriority() {
$listener1 = new TestEventListener();
$listener2 = new TestEventListener();
$listener3 = new TestEventListener();
$listener4 = new TestEventListener();
$listener5 = new TestEventListener();
$listener6 = new TestEventListener();
$this->dispatcher
->addListener('pre.foo', $listener1, -10);
$this->dispatcher
->addListener('pre.foo', $listener2);
$this->dispatcher
->addListener('pre.foo', $listener3, 10);
$this->dispatcher
->addListener('post.foo', $listener4, -10);
$this->dispatcher
->addListener('post.foo', $listener5);
$this->dispatcher
->addListener('post.foo', $listener6, 10);
$expected = [
'pre.foo' => [
$listener3,
$listener2,
$listener1,
],
'post.foo' => [
$listener6,
$listener5,
$listener4,
],
];
$this
->assertSame($expected, $this->dispatcher
->getListeners());
}
public function testGetListenerPriority() {
$listener1 = new TestEventListener();
$listener2 = new TestEventListener();
$this->dispatcher
->addListener('pre.foo', $listener1, -10);
$this->dispatcher
->addListener('pre.foo', $listener2);
$this
->assertSame(-10, $this->dispatcher
->getListenerPriority('pre.foo', $listener1));
$this
->assertSame(0, $this->dispatcher
->getListenerPriority('pre.foo', $listener2));
$this
->assertNull($this->dispatcher
->getListenerPriority('pre.bar', $listener2));
$this
->assertNull($this->dispatcher
->getListenerPriority('pre.foo', function () {
}));
}
public function testDispatch() {
$this->dispatcher
->addListener('pre.foo', [
$this->listener,
'preFoo',
]);
$this->dispatcher
->addListener('post.foo', [
$this->listener,
'postFoo',
]);
$this->dispatcher
->dispatch(new Event(), self::PREFOO);
$this
->assertTrue($this->listener->preFooInvoked);
$this
->assertFalse($this->listener->postFooInvoked);
$this
->assertInstanceOf(Event::class, $this->dispatcher
->dispatch(new Event(), 'noevent'));
$this
->assertInstanceOf(Event::class, $this->dispatcher
->dispatch(new Event(), self::PREFOO));
$event = new Event();
$return = $this->dispatcher
->dispatch($event, self::PREFOO);
$this
->assertSame($event, $return);
}
public function testDispatchForClosure() {
$invoked = 0;
$listener = function () use (&$invoked) {
++$invoked;
};
$this->dispatcher
->addListener('pre.foo', $listener);
$this->dispatcher
->addListener('post.foo', $listener);
$this->dispatcher
->dispatch(new Event(), self::PREFOO);
$this
->assertEquals(1, $invoked);
}
public function testStopEventPropagation() {
$otherListener = new TestEventListener();
$this->dispatcher
->addListener('post.foo', [
$this->listener,
'postFoo',
], 10);
$this->dispatcher
->addListener('post.foo', [
$otherListener,
'postFoo',
]);
$this->dispatcher
->dispatch(new Event(), self::POSTFOO);
$this
->assertTrue($this->listener->postFooInvoked);
$this
->assertFalse($otherListener->postFooInvoked);
}
public function testDispatchByPriority() {
$invoked = [];
$listener1 = function () use (&$invoked) {
$invoked[] = '1';
};
$listener2 = function () use (&$invoked) {
$invoked[] = '2';
};
$listener3 = function () use (&$invoked) {
$invoked[] = '3';
};
$this->dispatcher
->addListener('pre.foo', $listener1, -10);
$this->dispatcher
->addListener('pre.foo', $listener2);
$this->dispatcher
->addListener('pre.foo', $listener3, 10);
$this->dispatcher
->dispatch(new Event(), self::PREFOO);
$this
->assertEquals([
'3',
'2',
'1',
], $invoked);
}
public function testRemoveListener() {
$this->dispatcher
->addListener('pre.bar', $this->listener);
$this
->assertTrue($this->dispatcher
->hasListeners(self::PREBAR));
$this->dispatcher
->removeListener('pre.bar', $this->listener);
$this
->assertFalse($this->dispatcher
->hasListeners(self::PREBAR));
$this->dispatcher
->removeListener('notExists', $this->listener);
}
public function testAddSubscriber() {
$eventSubscriber = new TestEventSubscriber();
$this->dispatcher
->addSubscriber($eventSubscriber);
$this
->assertTrue($this->dispatcher
->hasListeners(self::PREFOO));
$this
->assertTrue($this->dispatcher
->hasListeners(self::POSTFOO));
}
public function testAddSubscriberWithPriorities() {
$eventSubscriber = new TestEventSubscriber();
$this->dispatcher
->addSubscriber($eventSubscriber);
$eventSubscriber = new TestEventSubscriberWithPriorities();
$this->dispatcher
->addSubscriber($eventSubscriber);
$listeners = $this->dispatcher
->getListeners('pre.foo');
$this
->assertTrue($this->dispatcher
->hasListeners(self::PREFOO));
$this
->assertCount(2, $listeners);
$this
->assertInstanceOf(TestEventSubscriberWithPriorities::class, $listeners[0][0]);
}
public function testAddSubscriberWithMultipleListeners() {
$eventSubscriber = new TestEventSubscriberWithMultipleListeners();
$this->dispatcher
->addSubscriber($eventSubscriber);
$listeners = $this->dispatcher
->getListeners('pre.foo');
$this
->assertTrue($this->dispatcher
->hasListeners(self::PREFOO));
$this
->assertCount(2, $listeners);
$this
->assertEquals('preFoo2', $listeners[0][1]);
}
public function testRemoveSubscriber() {
$eventSubscriber = new TestEventSubscriber();
$this->dispatcher
->addSubscriber($eventSubscriber);
$this
->assertTrue($this->dispatcher
->hasListeners(self::PREFOO));
$this
->assertTrue($this->dispatcher
->hasListeners(self::POSTFOO));
$this->dispatcher
->removeSubscriber($eventSubscriber);
$this
->assertFalse($this->dispatcher
->hasListeners(self::PREFOO));
$this
->assertFalse($this->dispatcher
->hasListeners(self::POSTFOO));
}
public function testRemoveSubscriberWithPriorities() {
$eventSubscriber = new TestEventSubscriberWithPriorities();
$this->dispatcher
->addSubscriber($eventSubscriber);
$this
->assertTrue($this->dispatcher
->hasListeners(self::PREFOO));
$this->dispatcher
->removeSubscriber($eventSubscriber);
$this
->assertFalse($this->dispatcher
->hasListeners(self::PREFOO));
}
public function testRemoveSubscriberWithMultipleListeners() {
$eventSubscriber = new TestEventSubscriberWithMultipleListeners();
$this->dispatcher
->addSubscriber($eventSubscriber);
$this
->assertTrue($this->dispatcher
->hasListeners(self::PREFOO));
$this
->assertCount(2, $this->dispatcher
->getListeners(self::PREFOO));
$this->dispatcher
->removeSubscriber($eventSubscriber);
$this
->assertFalse($this->dispatcher
->hasListeners(self::PREFOO));
}
public function testEventReceivesTheDispatcherInstanceAsArgument() {
$listener = new TestWithDispatcher();
$this->dispatcher
->addListener('test', [
$listener,
'foo',
]);
$this
->assertNull($listener->name);
$this
->assertNull($listener->dispatcher);
$this->dispatcher
->dispatch(new Event(), 'test');
$this
->assertEquals('test', $listener->name);
$this
->assertSame($this->dispatcher, $listener->dispatcher);
}
public function testWorkaroundForPhpBug62976() {
$dispatcher = $this
->createEventDispatcher();
$dispatcher
->addListener('bug.62976', new CallableClass());
$dispatcher
->removeListener('bug.62976', function () {
});
$this
->assertTrue($dispatcher
->hasListeners('bug.62976'));
}
public function testHasListenersWhenAddedCallbackListenerIsRemoved() {
$listener = function () {
};
$this->dispatcher
->addListener('foo', $listener);
$this->dispatcher
->removeListener('foo', $listener);
$this
->assertFalse($this->dispatcher
->hasListeners());
}
public function testGetListenersWhenAddedCallbackListenerIsRemoved() {
$listener = function () {
};
$this->dispatcher
->addListener('foo', $listener);
$this->dispatcher
->removeListener('foo', $listener);
$this
->assertSame([], $this->dispatcher
->getListeners());
}
public function testHasListenersWithoutEventsReturnsFalseAfterHasListenersWithEventHasBeenCalled() {
$this
->assertFalse($this->dispatcher
->hasListeners('foo'));
$this
->assertFalse($this->dispatcher
->hasListeners());
}
public function testHasListenersIsLazy() {
$called = 0;
$listener = [
function () use (&$called) {
++$called;
},
'onFoo',
];
$this->dispatcher
->addListener('foo', $listener);
$this
->assertTrue($this->dispatcher
->hasListeners());
$this
->assertTrue($this->dispatcher
->hasListeners('foo'));
$this
->assertSame(0, $called);
}
public function testDispatchLazyListener() {
$called = 0;
$factory = function () use (&$called) {
++$called;
return new TestWithDispatcher();
};
$this->dispatcher
->addListener('foo', [
$factory,
'foo',
]);
$this
->assertSame(0, $called);
$this->dispatcher
->dispatch(new Event(), 'foo');
$this->dispatcher
->dispatch(new Event(), 'foo');
$this
->assertSame(1, $called);
}
public function testRemoveFindsLazyListeners() {
$test = new TestWithDispatcher();
$factory = function () use ($test) {
return $test;
};
$this->dispatcher
->addListener('foo', [
$factory,
'foo',
]);
$this
->assertTrue($this->dispatcher
->hasListeners('foo'));
$this->dispatcher
->removeListener('foo', [
$test,
'foo',
]);
$this
->assertFalse($this->dispatcher
->hasListeners('foo'));
$this->dispatcher
->addListener('foo', [
$test,
'foo',
]);
$this
->assertTrue($this->dispatcher
->hasListeners('foo'));
$this->dispatcher
->removeListener('foo', [
$factory,
'foo',
]);
$this
->assertFalse($this->dispatcher
->hasListeners('foo'));
}
public function testPriorityFindsLazyListeners() {
$test = new TestWithDispatcher();
$factory = function () use ($test) {
return $test;
};
$this->dispatcher
->addListener('foo', [
$factory,
'foo',
], 3);
$this
->assertSame(3, $this->dispatcher
->getListenerPriority('foo', [
$test,
'foo',
]));
$this->dispatcher
->removeListener('foo', [
$factory,
'foo',
]);
$this->dispatcher
->addListener('foo', [
$test,
'foo',
], 5);
$this
->assertSame(5, $this->dispatcher
->getListenerPriority('foo', [
$factory,
'foo',
]));
}
public function testGetLazyListeners() {
$test = new TestWithDispatcher();
$factory = function () use ($test) {
return $test;
};
$this->dispatcher
->addListener('foo', [
$factory,
'foo',
], 3);
$this
->assertSame([
[
$test,
'foo',
],
], $this->dispatcher
->getListeners('foo'));
$this->dispatcher
->removeListener('foo', [
$test,
'foo',
]);
$this->dispatcher
->addListener('bar', [
$factory,
'foo',
], 3);
$this
->assertSame([
'bar' => [
[
$test,
'foo',
],
],
], $this->dispatcher
->getListeners());
}
}
class CallableClass {
public function __invoke() {
}
}
class TestEventListener {
public $preFooInvoked = FALSE;
public $postFooInvoked = FALSE;
public function preFoo(Event $e) {
$this->preFooInvoked = TRUE;
}
public function postFoo(Event $e) {
$this->postFooInvoked = TRUE;
$e
->stopPropagation();
}
}
class TestWithDispatcher {
public $name;
public $dispatcher;
public function foo(Event $e, $name, $dispatcher) {
$this->name = $name;
$this->dispatcher = $dispatcher;
}
}
class TestEventSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return [
'pre.foo' => 'preFoo',
'post.foo' => 'postFoo',
];
}
}
class TestEventSubscriberWithPriorities implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return [
'pre.foo' => [
'preFoo',
10,
],
'post.foo' => [
'postFoo',
],
];
}
}
class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return [
'pre.foo' => [
[
'preFoo1',
],
[
'preFoo2',
10,
],
],
];
}
}
class SymfonyInheritedEvent extends SymfonyEvent {
}