Chris@0: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace Symfony\Component\EventDispatcher\Tests; Chris@0: Chris@0: use PHPUnit\Framework\TestCase; Chris@0: use Symfony\Component\EventDispatcher\Event; Chris@0: use Symfony\Component\EventDispatcher\EventDispatcher; Chris@0: use Symfony\Component\EventDispatcher\EventSubscriberInterface; Chris@0: Chris@0: abstract class AbstractEventDispatcherTest extends TestCase Chris@0: { Chris@0: /* Some pseudo events */ Chris@0: const preFoo = 'pre.foo'; Chris@0: const postFoo = 'post.foo'; Chris@0: const preBar = 'pre.bar'; Chris@0: const postBar = 'post.bar'; Chris@0: Chris@0: /** Chris@0: * @var EventDispatcher Chris@0: */ Chris@0: private $dispatcher; Chris@0: Chris@0: private $listener; Chris@0: Chris@0: protected function setUp() Chris@0: { Chris@0: $this->dispatcher = $this->createEventDispatcher(); Chris@0: $this->listener = new TestEventListener(); Chris@0: } Chris@0: Chris@0: protected function tearDown() Chris@0: { Chris@0: $this->dispatcher = null; Chris@0: $this->listener = null; Chris@0: } Chris@0: Chris@0: abstract protected function createEventDispatcher(); Chris@0: Chris@0: public function testInitialState() Chris@0: { Chris@17: $this->assertEquals([], $this->dispatcher->getListeners()); Chris@0: $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); Chris@0: $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); Chris@0: } Chris@0: Chris@0: public function testAddListener() Chris@0: { Chris@17: $this->dispatcher->addListener('pre.foo', [$this->listener, 'preFoo']); Chris@17: $this->dispatcher->addListener('post.foo', [$this->listener, 'postFoo']); Chris@12: $this->assertTrue($this->dispatcher->hasListeners()); Chris@0: $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); Chris@0: $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); Chris@0: $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo)); Chris@0: $this->assertCount(1, $this->dispatcher->getListeners(self::postFoo)); Chris@0: $this->assertCount(2, $this->dispatcher->getListeners()); Chris@0: } Chris@0: Chris@0: public function testGetListenersSortsByPriority() Chris@0: { Chris@0: $listener1 = new TestEventListener(); Chris@0: $listener2 = new TestEventListener(); Chris@0: $listener3 = new TestEventListener(); Chris@0: $listener1->name = '1'; Chris@0: $listener2->name = '2'; Chris@0: $listener3->name = '3'; Chris@0: Chris@17: $this->dispatcher->addListener('pre.foo', [$listener1, 'preFoo'], -10); Chris@17: $this->dispatcher->addListener('pre.foo', [$listener2, 'preFoo'], 10); Chris@17: $this->dispatcher->addListener('pre.foo', [$listener3, 'preFoo']); Chris@0: Chris@17: $expected = [ Chris@17: [$listener2, 'preFoo'], Chris@17: [$listener3, 'preFoo'], Chris@17: [$listener1, 'preFoo'], Chris@17: ]; Chris@0: Chris@0: $this->assertSame($expected, $this->dispatcher->getListeners('pre.foo')); Chris@0: } Chris@0: Chris@0: public function testGetAllListenersSortsByPriority() Chris@0: { Chris@0: $listener1 = new TestEventListener(); Chris@0: $listener2 = new TestEventListener(); Chris@0: $listener3 = new TestEventListener(); Chris@0: $listener4 = new TestEventListener(); Chris@0: $listener5 = new TestEventListener(); Chris@0: $listener6 = new TestEventListener(); Chris@0: Chris@0: $this->dispatcher->addListener('pre.foo', $listener1, -10); Chris@0: $this->dispatcher->addListener('pre.foo', $listener2); Chris@0: $this->dispatcher->addListener('pre.foo', $listener3, 10); Chris@0: $this->dispatcher->addListener('post.foo', $listener4, -10); Chris@0: $this->dispatcher->addListener('post.foo', $listener5); Chris@0: $this->dispatcher->addListener('post.foo', $listener6, 10); Chris@0: Chris@17: $expected = [ Chris@17: 'pre.foo' => [$listener3, $listener2, $listener1], Chris@17: 'post.foo' => [$listener6, $listener5, $listener4], Chris@17: ]; Chris@0: Chris@0: $this->assertSame($expected, $this->dispatcher->getListeners()); Chris@0: } Chris@0: Chris@0: public function testGetListenerPriority() Chris@0: { Chris@0: $listener1 = new TestEventListener(); Chris@0: $listener2 = new TestEventListener(); Chris@0: Chris@0: $this->dispatcher->addListener('pre.foo', $listener1, -10); Chris@0: $this->dispatcher->addListener('pre.foo', $listener2); Chris@0: Chris@0: $this->assertSame(-10, $this->dispatcher->getListenerPriority('pre.foo', $listener1)); Chris@0: $this->assertSame(0, $this->dispatcher->getListenerPriority('pre.foo', $listener2)); Chris@0: $this->assertNull($this->dispatcher->getListenerPriority('pre.bar', $listener2)); Chris@0: $this->assertNull($this->dispatcher->getListenerPriority('pre.foo', function () {})); Chris@0: } Chris@0: Chris@0: public function testDispatch() Chris@0: { Chris@17: $this->dispatcher->addListener('pre.foo', [$this->listener, 'preFoo']); Chris@17: $this->dispatcher->addListener('post.foo', [$this->listener, 'postFoo']); Chris@0: $this->dispatcher->dispatch(self::preFoo); Chris@0: $this->assertTrue($this->listener->preFooInvoked); Chris@0: $this->assertFalse($this->listener->postFooInvoked); Chris@0: $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch('noevent')); Chris@0: $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo)); Chris@0: $event = new Event(); Chris@0: $return = $this->dispatcher->dispatch(self::preFoo, $event); Chris@0: $this->assertSame($event, $return); Chris@0: } Chris@0: Chris@0: public function testDispatchForClosure() Chris@0: { Chris@0: $invoked = 0; Chris@0: $listener = function () use (&$invoked) { Chris@0: ++$invoked; Chris@0: }; Chris@0: $this->dispatcher->addListener('pre.foo', $listener); Chris@0: $this->dispatcher->addListener('post.foo', $listener); Chris@0: $this->dispatcher->dispatch(self::preFoo); Chris@0: $this->assertEquals(1, $invoked); Chris@0: } Chris@0: Chris@0: public function testStopEventPropagation() Chris@0: { Chris@0: $otherListener = new TestEventListener(); Chris@0: Chris@0: // postFoo() stops the propagation, so only one listener should Chris@0: // be executed Chris@0: // Manually set priority to enforce $this->listener to be called first Chris@17: $this->dispatcher->addListener('post.foo', [$this->listener, 'postFoo'], 10); Chris@17: $this->dispatcher->addListener('post.foo', [$otherListener, 'postFoo']); Chris@0: $this->dispatcher->dispatch(self::postFoo); Chris@0: $this->assertTrue($this->listener->postFooInvoked); Chris@0: $this->assertFalse($otherListener->postFooInvoked); Chris@0: } Chris@0: Chris@0: public function testDispatchByPriority() Chris@0: { Chris@17: $invoked = []; Chris@0: $listener1 = function () use (&$invoked) { Chris@0: $invoked[] = '1'; Chris@0: }; Chris@0: $listener2 = function () use (&$invoked) { Chris@0: $invoked[] = '2'; Chris@0: }; Chris@0: $listener3 = function () use (&$invoked) { Chris@0: $invoked[] = '3'; Chris@0: }; Chris@0: $this->dispatcher->addListener('pre.foo', $listener1, -10); Chris@0: $this->dispatcher->addListener('pre.foo', $listener2); Chris@0: $this->dispatcher->addListener('pre.foo', $listener3, 10); Chris@0: $this->dispatcher->dispatch(self::preFoo); Chris@17: $this->assertEquals(['3', '2', '1'], $invoked); Chris@0: } Chris@0: Chris@0: public function testRemoveListener() Chris@0: { Chris@0: $this->dispatcher->addListener('pre.bar', $this->listener); Chris@0: $this->assertTrue($this->dispatcher->hasListeners(self::preBar)); Chris@0: $this->dispatcher->removeListener('pre.bar', $this->listener); Chris@0: $this->assertFalse($this->dispatcher->hasListeners(self::preBar)); Chris@0: $this->dispatcher->removeListener('notExists', $this->listener); Chris@0: } Chris@0: Chris@0: public function testAddSubscriber() Chris@0: { Chris@0: $eventSubscriber = new TestEventSubscriber(); Chris@0: $this->dispatcher->addSubscriber($eventSubscriber); Chris@0: $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); Chris@0: $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); Chris@0: } Chris@0: Chris@0: public function testAddSubscriberWithPriorities() Chris@0: { Chris@0: $eventSubscriber = new TestEventSubscriber(); Chris@0: $this->dispatcher->addSubscriber($eventSubscriber); Chris@0: Chris@0: $eventSubscriber = new TestEventSubscriberWithPriorities(); Chris@0: $this->dispatcher->addSubscriber($eventSubscriber); Chris@0: Chris@0: $listeners = $this->dispatcher->getListeners('pre.foo'); Chris@0: $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); Chris@0: $this->assertCount(2, $listeners); Chris@0: $this->assertInstanceOf('Symfony\Component\EventDispatcher\Tests\TestEventSubscriberWithPriorities', $listeners[0][0]); Chris@0: } Chris@0: Chris@0: public function testAddSubscriberWithMultipleListeners() Chris@0: { Chris@0: $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); Chris@0: $this->dispatcher->addSubscriber($eventSubscriber); Chris@0: Chris@0: $listeners = $this->dispatcher->getListeners('pre.foo'); Chris@0: $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); Chris@0: $this->assertCount(2, $listeners); Chris@0: $this->assertEquals('preFoo2', $listeners[0][1]); Chris@0: } Chris@0: Chris@0: public function testRemoveSubscriber() Chris@0: { Chris@0: $eventSubscriber = new TestEventSubscriber(); Chris@0: $this->dispatcher->addSubscriber($eventSubscriber); Chris@0: $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); Chris@0: $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); Chris@0: $this->dispatcher->removeSubscriber($eventSubscriber); Chris@0: $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); Chris@0: $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); Chris@0: } Chris@0: Chris@0: public function testRemoveSubscriberWithPriorities() Chris@0: { Chris@0: $eventSubscriber = new TestEventSubscriberWithPriorities(); Chris@0: $this->dispatcher->addSubscriber($eventSubscriber); Chris@0: $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); Chris@0: $this->dispatcher->removeSubscriber($eventSubscriber); Chris@0: $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); Chris@0: } Chris@0: Chris@0: public function testRemoveSubscriberWithMultipleListeners() Chris@0: { Chris@0: $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); Chris@0: $this->dispatcher->addSubscriber($eventSubscriber); Chris@0: $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); Chris@0: $this->assertCount(2, $this->dispatcher->getListeners(self::preFoo)); Chris@0: $this->dispatcher->removeSubscriber($eventSubscriber); Chris@0: $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); Chris@0: } Chris@0: Chris@0: public function testEventReceivesTheDispatcherInstanceAsArgument() Chris@0: { Chris@0: $listener = new TestWithDispatcher(); Chris@17: $this->dispatcher->addListener('test', [$listener, 'foo']); Chris@0: $this->assertNull($listener->name); Chris@0: $this->assertNull($listener->dispatcher); Chris@0: $this->dispatcher->dispatch('test'); Chris@0: $this->assertEquals('test', $listener->name); Chris@0: $this->assertSame($this->dispatcher, $listener->dispatcher); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @see https://bugs.php.net/bug.php?id=62976 Chris@0: * Chris@0: * This bug affects: Chris@0: * - The PHP 5.3 branch for versions < 5.3.18 Chris@0: * - The PHP 5.4 branch for versions < 5.4.8 Chris@0: * - The PHP 5.5 branch is not affected Chris@0: */ Chris@0: public function testWorkaroundForPhpBug62976() Chris@0: { Chris@0: $dispatcher = $this->createEventDispatcher(); Chris@0: $dispatcher->addListener('bug.62976', new CallableClass()); Chris@0: $dispatcher->removeListener('bug.62976', function () {}); Chris@0: $this->assertTrue($dispatcher->hasListeners('bug.62976')); Chris@0: } Chris@0: Chris@0: public function testHasListenersWhenAddedCallbackListenerIsRemoved() Chris@0: { Chris@0: $listener = function () {}; Chris@0: $this->dispatcher->addListener('foo', $listener); Chris@0: $this->dispatcher->removeListener('foo', $listener); Chris@0: $this->assertFalse($this->dispatcher->hasListeners()); Chris@0: } Chris@0: Chris@0: public function testGetListenersWhenAddedCallbackListenerIsRemoved() Chris@0: { Chris@0: $listener = function () {}; Chris@0: $this->dispatcher->addListener('foo', $listener); Chris@0: $this->dispatcher->removeListener('foo', $listener); Chris@17: $this->assertSame([], $this->dispatcher->getListeners()); Chris@0: } Chris@0: Chris@0: public function testHasListenersWithoutEventsReturnsFalseAfterHasListenersWithEventHasBeenCalled() Chris@0: { Chris@0: $this->assertFalse($this->dispatcher->hasListeners('foo')); Chris@0: $this->assertFalse($this->dispatcher->hasListeners()); Chris@0: } Chris@14: Chris@14: public function testHasListenersIsLazy() Chris@14: { Chris@14: $called = 0; Chris@17: $listener = [function () use (&$called) { ++$called; }, 'onFoo']; Chris@14: $this->dispatcher->addListener('foo', $listener); Chris@14: $this->assertTrue($this->dispatcher->hasListeners()); Chris@14: $this->assertTrue($this->dispatcher->hasListeners('foo')); Chris@14: $this->assertSame(0, $called); Chris@14: } Chris@14: Chris@14: public function testDispatchLazyListener() Chris@14: { Chris@14: $called = 0; Chris@14: $factory = function () use (&$called) { Chris@14: ++$called; Chris@14: Chris@14: return new TestWithDispatcher(); Chris@14: }; Chris@17: $this->dispatcher->addListener('foo', [$factory, 'foo']); Chris@14: $this->assertSame(0, $called); Chris@14: $this->dispatcher->dispatch('foo', new Event()); Chris@14: $this->dispatcher->dispatch('foo', new Event()); Chris@14: $this->assertSame(1, $called); Chris@14: } Chris@14: Chris@14: public function testRemoveFindsLazyListeners() Chris@14: { Chris@14: $test = new TestWithDispatcher(); Chris@14: $factory = function () use ($test) { return $test; }; Chris@14: Chris@17: $this->dispatcher->addListener('foo', [$factory, 'foo']); Chris@14: $this->assertTrue($this->dispatcher->hasListeners('foo')); Chris@17: $this->dispatcher->removeListener('foo', [$test, 'foo']); Chris@14: $this->assertFalse($this->dispatcher->hasListeners('foo')); Chris@14: Chris@17: $this->dispatcher->addListener('foo', [$test, 'foo']); Chris@14: $this->assertTrue($this->dispatcher->hasListeners('foo')); Chris@17: $this->dispatcher->removeListener('foo', [$factory, 'foo']); Chris@14: $this->assertFalse($this->dispatcher->hasListeners('foo')); Chris@14: } Chris@14: Chris@14: public function testPriorityFindsLazyListeners() Chris@14: { Chris@14: $test = new TestWithDispatcher(); Chris@14: $factory = function () use ($test) { return $test; }; Chris@14: Chris@17: $this->dispatcher->addListener('foo', [$factory, 'foo'], 3); Chris@17: $this->assertSame(3, $this->dispatcher->getListenerPriority('foo', [$test, 'foo'])); Chris@17: $this->dispatcher->removeListener('foo', [$factory, 'foo']); Chris@14: Chris@17: $this->dispatcher->addListener('foo', [$test, 'foo'], 5); Chris@17: $this->assertSame(5, $this->dispatcher->getListenerPriority('foo', [$factory, 'foo'])); Chris@14: } Chris@14: Chris@14: public function testGetLazyListeners() Chris@14: { Chris@14: $test = new TestWithDispatcher(); Chris@14: $factory = function () use ($test) { return $test; }; Chris@14: Chris@17: $this->dispatcher->addListener('foo', [$factory, 'foo'], 3); Chris@17: $this->assertSame([[$test, 'foo']], $this->dispatcher->getListeners('foo')); Chris@14: Chris@17: $this->dispatcher->removeListener('foo', [$test, 'foo']); Chris@17: $this->dispatcher->addListener('bar', [$factory, 'foo'], 3); Chris@17: $this->assertSame(['bar' => [[$test, 'foo']]], $this->dispatcher->getListeners()); Chris@14: } Chris@0: } Chris@0: Chris@0: class CallableClass Chris@0: { Chris@0: public function __invoke() Chris@0: { Chris@0: } Chris@0: } Chris@0: Chris@0: class TestEventListener Chris@0: { Chris@0: public $preFooInvoked = false; Chris@0: public $postFooInvoked = false; Chris@0: Chris@0: /* Listener methods */ Chris@0: Chris@0: public function preFoo(Event $e) Chris@0: { Chris@0: $this->preFooInvoked = true; Chris@0: } Chris@0: Chris@0: public function postFoo(Event $e) Chris@0: { Chris@0: $this->postFooInvoked = true; Chris@0: Chris@0: $e->stopPropagation(); Chris@0: } Chris@0: } Chris@0: Chris@0: class TestWithDispatcher Chris@0: { Chris@0: public $name; Chris@0: public $dispatcher; Chris@0: Chris@0: public function foo(Event $e, $name, $dispatcher) Chris@0: { Chris@0: $this->name = $name; Chris@0: $this->dispatcher = $dispatcher; Chris@0: } Chris@0: } Chris@0: Chris@0: class TestEventSubscriber implements EventSubscriberInterface Chris@0: { Chris@0: public static function getSubscribedEvents() Chris@0: { Chris@17: return ['pre.foo' => 'preFoo', 'post.foo' => 'postFoo']; Chris@0: } Chris@0: } Chris@0: Chris@0: class TestEventSubscriberWithPriorities implements EventSubscriberInterface Chris@0: { Chris@0: public static function getSubscribedEvents() Chris@0: { Chris@17: return [ Chris@17: 'pre.foo' => ['preFoo', 10], Chris@17: 'post.foo' => ['postFoo'], Chris@17: ]; Chris@0: } Chris@0: } Chris@0: Chris@0: class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface Chris@0: { Chris@0: public static function getSubscribedEvents() Chris@0: { Chris@17: return ['pre.foo' => [ Chris@17: ['preFoo1'], Chris@17: ['preFoo2', 10], Chris@17: ]]; Chris@0: } Chris@0: }