1<?php 2 3/* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Symfony\Component\EventDispatcher\Tests; 13 14use Symfony\Component\EventDispatcher\Event; 15use Symfony\Component\EventDispatcher\EventDispatcher; 16use Symfony\Component\EventDispatcher\EventSubscriberInterface; 17 18abstract class AbstractEventDispatcherTest extends \PHPUnit_Framework_TestCase 19{ 20 /* Some pseudo events */ 21 const preFoo = 'pre.foo'; 22 const postFoo = 'post.foo'; 23 const preBar = 'pre.bar'; 24 const postBar = 'post.bar'; 25 26 /** 27 * @var EventDispatcher 28 */ 29 private $dispatcher; 30 31 private $listener; 32 33 protected function setUp() 34 { 35 $this->dispatcher = $this->createEventDispatcher(); 36 $this->listener = new TestEventListener(); 37 } 38 39 protected function tearDown() 40 { 41 $this->dispatcher = null; 42 $this->listener = null; 43 } 44 45 abstract protected function createEventDispatcher(); 46 47 public function testInitialState() 48 { 49 $this->assertEquals(array(), $this->dispatcher->getListeners()); 50 $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); 51 $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); 52 } 53 54 public function testAddListener() 55 { 56 $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); 57 $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); 58 $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); 59 $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); 60 $this->assertCount(1, $this->dispatcher->getListeners(self::preFoo)); 61 $this->assertCount(1, $this->dispatcher->getListeners(self::postFoo)); 62 $this->assertCount(2, $this->dispatcher->getListeners()); 63 } 64 65 public function testGetListenersSortsByPriority() 66 { 67 $listener1 = new TestEventListener(); 68 $listener2 = new TestEventListener(); 69 $listener3 = new TestEventListener(); 70 $listener1->name = '1'; 71 $listener2->name = '2'; 72 $listener3->name = '3'; 73 74 $this->dispatcher->addListener('pre.foo', array($listener1, 'preFoo'), -10); 75 $this->dispatcher->addListener('pre.foo', array($listener2, 'preFoo'), 10); 76 $this->dispatcher->addListener('pre.foo', array($listener3, 'preFoo')); 77 78 $expected = array( 79 array($listener2, 'preFoo'), 80 array($listener3, 'preFoo'), 81 array($listener1, 'preFoo'), 82 ); 83 84 $this->assertSame($expected, $this->dispatcher->getListeners('pre.foo')); 85 } 86 87 public function testGetAllListenersSortsByPriority() 88 { 89 $listener1 = new TestEventListener(); 90 $listener2 = new TestEventListener(); 91 $listener3 = new TestEventListener(); 92 $listener4 = new TestEventListener(); 93 $listener5 = new TestEventListener(); 94 $listener6 = new TestEventListener(); 95 96 $this->dispatcher->addListener('pre.foo', $listener1, -10); 97 $this->dispatcher->addListener('pre.foo', $listener2); 98 $this->dispatcher->addListener('pre.foo', $listener3, 10); 99 $this->dispatcher->addListener('post.foo', $listener4, -10); 100 $this->dispatcher->addListener('post.foo', $listener5); 101 $this->dispatcher->addListener('post.foo', $listener6, 10); 102 103 $expected = array( 104 'pre.foo' => array($listener3, $listener2, $listener1), 105 'post.foo' => array($listener6, $listener5, $listener4), 106 ); 107 108 $this->assertSame($expected, $this->dispatcher->getListeners()); 109 } 110 111 public function testGetListenerPriority() 112 { 113 $listener1 = new TestEventListener(); 114 $listener2 = new TestEventListener(); 115 116 $this->dispatcher->addListener('pre.foo', $listener1, -10); 117 $this->dispatcher->addListener('pre.foo', $listener2); 118 119 $this->assertSame(-10, $this->dispatcher->getListenerPriority('pre.foo', $listener1)); 120 $this->assertSame(0, $this->dispatcher->getListenerPriority('pre.foo', $listener2)); 121 $this->assertNull($this->dispatcher->getListenerPriority('pre.bar', $listener2)); 122 $this->assertNull($this->dispatcher->getListenerPriority('pre.foo', function () {})); 123 } 124 125 public function testDispatch() 126 { 127 $this->dispatcher->addListener('pre.foo', array($this->listener, 'preFoo')); 128 $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo')); 129 $this->dispatcher->dispatch(self::preFoo); 130 $this->assertTrue($this->listener->preFooInvoked); 131 $this->assertFalse($this->listener->postFooInvoked); 132 $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch('noevent')); 133 $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo)); 134 $event = new Event(); 135 $return = $this->dispatcher->dispatch(self::preFoo, $event); 136 $this->assertSame($event, $return); 137 } 138 139 public function testDispatchForClosure() 140 { 141 $invoked = 0; 142 $listener = function () use (&$invoked) { 143 ++$invoked; 144 }; 145 $this->dispatcher->addListener('pre.foo', $listener); 146 $this->dispatcher->addListener('post.foo', $listener); 147 $this->dispatcher->dispatch(self::preFoo); 148 $this->assertEquals(1, $invoked); 149 } 150 151 public function testStopEventPropagation() 152 { 153 $otherListener = new TestEventListener(); 154 155 // postFoo() stops the propagation, so only one listener should 156 // be executed 157 // Manually set priority to enforce $this->listener to be called first 158 $this->dispatcher->addListener('post.foo', array($this->listener, 'postFoo'), 10); 159 $this->dispatcher->addListener('post.foo', array($otherListener, 'preFoo')); 160 $this->dispatcher->dispatch(self::postFoo); 161 $this->assertTrue($this->listener->postFooInvoked); 162 $this->assertFalse($otherListener->postFooInvoked); 163 } 164 165 public function testDispatchByPriority() 166 { 167 $invoked = array(); 168 $listener1 = function () use (&$invoked) { 169 $invoked[] = '1'; 170 }; 171 $listener2 = function () use (&$invoked) { 172 $invoked[] = '2'; 173 }; 174 $listener3 = function () use (&$invoked) { 175 $invoked[] = '3'; 176 }; 177 $this->dispatcher->addListener('pre.foo', $listener1, -10); 178 $this->dispatcher->addListener('pre.foo', $listener2); 179 $this->dispatcher->addListener('pre.foo', $listener3, 10); 180 $this->dispatcher->dispatch(self::preFoo); 181 $this->assertEquals(array('3', '2', '1'), $invoked); 182 } 183 184 public function testRemoveListener() 185 { 186 $this->dispatcher->addListener('pre.bar', $this->listener); 187 $this->assertTrue($this->dispatcher->hasListeners(self::preBar)); 188 $this->dispatcher->removeListener('pre.bar', $this->listener); 189 $this->assertFalse($this->dispatcher->hasListeners(self::preBar)); 190 $this->dispatcher->removeListener('notExists', $this->listener); 191 } 192 193 public function testAddSubscriber() 194 { 195 $eventSubscriber = new TestEventSubscriber(); 196 $this->dispatcher->addSubscriber($eventSubscriber); 197 $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); 198 $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); 199 } 200 201 public function testAddSubscriberWithPriorities() 202 { 203 $eventSubscriber = new TestEventSubscriber(); 204 $this->dispatcher->addSubscriber($eventSubscriber); 205 206 $eventSubscriber = new TestEventSubscriberWithPriorities(); 207 $this->dispatcher->addSubscriber($eventSubscriber); 208 209 $listeners = $this->dispatcher->getListeners('pre.foo'); 210 $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); 211 $this->assertCount(2, $listeners); 212 $this->assertInstanceOf('Symfony\Component\EventDispatcher\Tests\TestEventSubscriberWithPriorities', $listeners[0][0]); 213 } 214 215 public function testAddSubscriberWithMultipleListeners() 216 { 217 $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); 218 $this->dispatcher->addSubscriber($eventSubscriber); 219 220 $listeners = $this->dispatcher->getListeners('pre.foo'); 221 $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); 222 $this->assertCount(2, $listeners); 223 $this->assertEquals('preFoo2', $listeners[0][1]); 224 } 225 226 public function testRemoveSubscriber() 227 { 228 $eventSubscriber = new TestEventSubscriber(); 229 $this->dispatcher->addSubscriber($eventSubscriber); 230 $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); 231 $this->assertTrue($this->dispatcher->hasListeners(self::postFoo)); 232 $this->dispatcher->removeSubscriber($eventSubscriber); 233 $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); 234 $this->assertFalse($this->dispatcher->hasListeners(self::postFoo)); 235 } 236 237 public function testRemoveSubscriberWithPriorities() 238 { 239 $eventSubscriber = new TestEventSubscriberWithPriorities(); 240 $this->dispatcher->addSubscriber($eventSubscriber); 241 $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); 242 $this->dispatcher->removeSubscriber($eventSubscriber); 243 $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); 244 } 245 246 public function testRemoveSubscriberWithMultipleListeners() 247 { 248 $eventSubscriber = new TestEventSubscriberWithMultipleListeners(); 249 $this->dispatcher->addSubscriber($eventSubscriber); 250 $this->assertTrue($this->dispatcher->hasListeners(self::preFoo)); 251 $this->assertCount(2, $this->dispatcher->getListeners(self::preFoo)); 252 $this->dispatcher->removeSubscriber($eventSubscriber); 253 $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); 254 } 255 256 public function testEventReceivesTheDispatcherInstanceAsArgument() 257 { 258 $listener = new TestWithDispatcher(); 259 $this->dispatcher->addListener('test', array($listener, 'foo')); 260 $this->assertNull($listener->name); 261 $this->assertNull($listener->dispatcher); 262 $this->dispatcher->dispatch('test'); 263 $this->assertEquals('test', $listener->name); 264 $this->assertSame($this->dispatcher, $listener->dispatcher); 265 } 266 267 /** 268 * @see https://bugs.php.net/bug.php?id=62976 269 * 270 * This bug affects: 271 * - The PHP 5.3 branch for versions < 5.3.18 272 * - The PHP 5.4 branch for versions < 5.4.8 273 * - The PHP 5.5 branch is not affected 274 */ 275 public function testWorkaroundForPhpBug62976() 276 { 277 $dispatcher = $this->createEventDispatcher(); 278 $dispatcher->addListener('bug.62976', new CallableClass()); 279 $dispatcher->removeListener('bug.62976', function () {}); 280 $this->assertTrue($dispatcher->hasListeners('bug.62976')); 281 } 282 283 public function testHasListenersWhenAddedCallbackListenerIsRemoved() 284 { 285 $listener = function () {}; 286 $this->dispatcher->addListener('foo', $listener); 287 $this->dispatcher->removeListener('foo', $listener); 288 $this->assertFalse($this->dispatcher->hasListeners()); 289 } 290 291 public function testGetListenersWhenAddedCallbackListenerIsRemoved() 292 { 293 $listener = function () {}; 294 $this->dispatcher->addListener('foo', $listener); 295 $this->dispatcher->removeListener('foo', $listener); 296 $this->assertSame(array(), $this->dispatcher->getListeners()); 297 } 298 299 public function testHasListenersWithoutEventsReturnsFalseAfterHasListenersWithEventHasBeenCalled() 300 { 301 $this->assertFalse($this->dispatcher->hasListeners('foo')); 302 $this->assertFalse($this->dispatcher->hasListeners()); 303 } 304} 305 306class CallableClass 307{ 308 public function __invoke() 309 { 310 } 311} 312 313class TestEventListener 314{ 315 public $preFooInvoked = false; 316 public $postFooInvoked = false; 317 318 /* Listener methods */ 319 320 public function preFoo(Event $e) 321 { 322 $this->preFooInvoked = true; 323 } 324 325 public function postFoo(Event $e) 326 { 327 $this->postFooInvoked = true; 328 329 $e->stopPropagation(); 330 } 331} 332 333class TestWithDispatcher 334{ 335 public $name; 336 public $dispatcher; 337 338 public function foo(Event $e, $name, $dispatcher) 339 { 340 $this->name = $name; 341 $this->dispatcher = $dispatcher; 342 } 343} 344 345class TestEventSubscriber implements EventSubscriberInterface 346{ 347 public static function getSubscribedEvents() 348 { 349 return array('pre.foo' => 'preFoo', 'post.foo' => 'postFoo'); 350 } 351} 352 353class TestEventSubscriberWithPriorities implements EventSubscriberInterface 354{ 355 public static function getSubscribedEvents() 356 { 357 return array( 358 'pre.foo' => array('preFoo', 10), 359 'post.foo' => array('postFoo'), 360 ); 361 } 362} 363 364class TestEventSubscriberWithMultipleListeners implements EventSubscriberInterface 365{ 366 public static function getSubscribedEvents() 367 { 368 return array('pre.foo' => array( 369 array('preFoo1'), 370 array('preFoo2', 10), 371 )); 372 } 373} 374