1<?php 2 3declare(strict_types=1); 4 5namespace JMS\Serializer\Tests\Serializer\EventDispatcher; 6 7use Doctrine\Common\Persistence\Proxy; 8use JMS\Serializer\Context; 9use JMS\Serializer\EventDispatcher\Event; 10use JMS\Serializer\EventDispatcher\EventDispatcher; 11use JMS\Serializer\EventDispatcher\EventDispatcherInterface; 12use JMS\Serializer\EventDispatcher\EventSubscriberInterface; 13use JMS\Serializer\EventDispatcher\ObjectEvent; 14use JMS\Serializer\Tests\Fixtures\SimpleObject; 15use JMS\Serializer\Tests\Fixtures\SimpleObjectProxy; 16use PHPUnit\Framework\Assert; 17use PHPUnit\Framework\TestCase; 18 19class EventDispatcherTest extends TestCase 20{ 21 /** 22 * @var EventDispatcher 23 */ 24 protected $dispatcher; 25 protected $event; 26 protected $context; 27 28 public function testHasListeners() 29 { 30 self::assertFalse($this->dispatcher->hasListeners('foo', 'Foo', 'json')); 31 $this->dispatcher->addListener('foo', static function () { 32 }); 33 self::assertTrue($this->dispatcher->hasListeners('foo', 'Foo', 'json')); 34 35 self::assertFalse($this->dispatcher->hasListeners('bar', 'Bar', 'json')); 36 $this->dispatcher->addListener('bar', static function () { 37 }, 'Foo'); 38 self::assertFalse($this->dispatcher->hasListeners('bar', 'Bar', 'json')); 39 $this->dispatcher->addListener('bar', static function () { 40 }, 'Bar', 'xml'); 41 self::assertFalse($this->dispatcher->hasListeners('bar', 'Bar', 'json')); 42 $this->dispatcher->addListener('bar', static function () { 43 }, null, 'json'); 44 self::assertTrue($this->dispatcher->hasListeners('bar', 'Baz', 'json')); 45 self::assertTrue($this->dispatcher->hasListeners('bar', 'Bar', 'json')); 46 47 self::assertFalse($this->dispatcher->hasListeners('baz', 'Bar', 'xml')); 48 $this->dispatcher->addListener('baz', static function () { 49 }, 'Bar'); 50 self::assertTrue($this->dispatcher->hasListeners('baz', 'Bar', 'xml')); 51 //self::assertTrue($this->dispatcher->hasListeners('baz', 'bAr', 'xml')); 52 } 53 54 public function testDispatch() 55 { 56 $a = new MockListener(); 57 $this->dispatcher->addListener('foo', [$a, 'Foo']); 58 $this->dispatch('bar'); 59 $a->_verify('Listener is not called for other event.'); 60 61 $b = new MockListener(); 62 $this->dispatcher->addListener('pre', [$b, 'bar'], 'Bar'); 63 $this->dispatcher->addListener('pre', [$b, 'foo'], 'Foo'); 64 $this->dispatcher->addListener('pre', [$b, 'all']); 65 66 $b->bar($this->event, 'pre', 'Bar', 'json', $this->dispatcher); 67 $b->all($this->event, 'pre', 'Bar', 'json', $this->dispatcher); 68 $b->foo($this->event, 'pre', 'Foo', 'json', $this->dispatcher); 69 $b->all($this->event, 'pre', 'Foo', 'json', $this->dispatcher); 70 71 $b->_replay(); 72 $this->dispatch('pre', 'Bar'); 73 $this->dispatch('pre', 'Foo'); 74 $b->_verify(); 75 } 76 77 public function testDispatchWithInstanceFilteringBothListenersInvoked() 78 { 79 $a = new MockListener(); 80 81 $this->dispatcher->addListener('pre', [$a, 'onlyProxy'], 'Bar', 'json', Proxy::class); 82 $this->dispatcher->addListener('pre', [$a, 'all'], 'Bar', 'json'); 83 84 $object = new SimpleObjectProxy('a', 'b'); 85 $event = new ObjectEvent($this->context, $object, ['name' => 'foo', 'params' => []]); 86 87 // expected 88 $a->onlyProxy($event, 'pre', 'Bar', 'json', $this->dispatcher); 89 $a->all($event, 'pre', 'Bar', 'json', $this->dispatcher); 90 91 $a->_replay(); 92 $this->dispatch('pre', 'Bar', 'json', $event); 93 $a->_verify(); 94 } 95 96 public function testDispatchWithInstanceFilteringOnlyGenericListenerInvoked() 97 { 98 $a = new MockListener(); 99 100 $this->dispatcher->addListener('pre', [$a, 'onlyProxy'], 'Bar', 'json', Proxy::class); 101 $this->dispatcher->addListener('pre', [$a, 'all'], 'Bar', 'json'); 102 103 $object = new SimpleObject('a', 'b'); 104 $event = new ObjectEvent($this->context, $object, ['name' => 'foo', 'params' => []]); 105 106 // expected 107 $a->all($event, 'pre', 'Bar', 'json', $this->dispatcher); 108 109 $a->_replay(); 110 $this->dispatch('pre', 'Bar', 'json', $event); 111 $a->_verify(); 112 } 113 114 public function testListenerCanStopPropagation() 115 { 116 $listener1 = false; 117 $listener2 = false; 118 119 $this->dispatcher->addListener('pre', static function (Event $event) use (&$listener1) { 120 $event->stopPropagation(); 121 $listener1 = true; 122 }); 123 124 $this->dispatcher->addListener('pre', static function () use (&$listener2) { 125 $listener2 = true; 126 }); 127 128 $this->dispatch('pre'); 129 130 self::assertTrue($listener1); 131 self::assertFalse($listener2); 132 } 133 134 public function testListenerCanDispatchEvent() 135 { 136 $listener1 = false; 137 $listener2 = false; 138 $listener3 = false; 139 140 $this->dispatcher->addListener('pre', static function (Event $event, $eventName, $loweredClass, $format, EventDispatcherInterface $dispatcher) use (&$listener1) { 141 $listener1 = true; 142 143 $event = new Event($event->getContext(), $event->getType()); 144 145 self::assertSame('pre', $eventName); 146 self::assertSame('json', $format); 147 self::assertSame('Foo', $loweredClass); 148 149 $dispatcher->dispatch('post', 'Blah', 'xml', $event); 150 }); 151 152 $this->dispatcher->addListener('pre', static function () use (&$listener2) { 153 $listener2 = true; 154 }); 155 156 $this->dispatcher->addListener('post', static function (Event $event, $eventName, $loweredClass, $format, EventDispatcherInterface $dispatcher) use (&$listener3) { 157 $listener3 = true; 158 159 self::assertSame('post', $eventName); 160 self::assertSame('xml', $format); 161 self::assertSame('Blah', $loweredClass); 162 }); 163 164 $this->dispatch('pre'); 165 166 self::assertTrue($listener1); 167 self::assertTrue($listener2); 168 self::assertTrue($listener3); 169 } 170 171 public function testAddSubscriber() 172 { 173 $subscriber = new MockSubscriber(); 174 MockSubscriber::$events = [ 175 ['event' => 'foo.bar_baz', 'format' => 'foo'], 176 ['event' => 'bar', 'method' => 'bar', 'class' => 'foo'], 177 ]; 178 179 $this->dispatcher->addSubscriber($subscriber); 180 self::assertAttributeEquals([ 181 'foo.bar_baz' => [ 182 [[$subscriber, 'onfoobarbaz'], null, 'foo', null], 183 ], 184 'bar' => [ 185 [[$subscriber, 'bar'], 'foo', null, null], 186 ], 187 ], 'listeners', $this->dispatcher); 188 } 189 190 protected function setUp() 191 { 192 $this->context = $this->getMockBuilder(Context::class)->getMock(); 193 194 $this->dispatcher = $this->createEventDispatcher(); 195 $this->event = new ObjectEvent($this->context, new \stdClass(), ['name' => 'foo', 'params' => []]); 196 } 197 198 protected function createEventDispatcher() 199 { 200 return new EventDispatcher(); 201 } 202 203 protected function dispatch($eventName, $class = 'Foo', $format = 'json', ?Event $event = null) 204 { 205 $this->dispatcher->dispatch($eventName, $class, $format, $event ?: $this->event); 206 } 207} 208 209class MockSubscriber implements EventSubscriberInterface 210{ 211 public static $events = []; 212 213 public static function getSubscribedEvents() 214 { 215 return self::$events; 216 } 217} 218 219class MockListener 220{ 221 private $expected = []; 222 private $actual = []; 223 private $wasReplayed = false; 224 225 public function __call($method, array $args = []) 226 { 227 if (!$this->wasReplayed) { 228 $this->expected[] = [$method, $args]; 229 230 return; 231 } 232 233 $this->actual[] = [$method, $args]; 234 } 235 236 public function _replay() 237 { 238 $this->wasReplayed = true; 239 } 240 241 public function _verify($message = '') 242 { 243 Assert::assertSame($this->expected, $this->actual, $message); 244 } 245} 246