diff --git a/lib/Cake/Event/CakeEventManager.php b/lib/Cake/Event/CakeEventManager.php index 799e0b733..c88c6b977 100644 --- a/lib/Cake/Event/CakeEventManager.php +++ b/lib/Cake/Event/CakeEventManager.php @@ -230,15 +230,12 @@ class CakeEventManager { $event = new CakeEvent($event); } - if (!$this->_isGlobal) { - self::instance()->dispatch($event); - } - - if (empty($this->_listeners[$event->name()])) { + $listeners = $this->listeners($event->name()); + if (empty($listeners)) { return; } - foreach ($this->listeners($event->name()) as $listener) { + foreach ($listeners as $listener) { if ($event->isStopped()) { break; } @@ -264,15 +261,42 @@ class CakeEventManager { * @return array */ public function listeners($eventKey) { - if (empty($this->_listeners[$eventKey])) { + $globalListeners = array(); + if (!$this->_isGlobal) { + $globalListeners = self::instance()->prioritisedListeners($eventKey); + } + + if (empty($this->_listeners[$eventKey]) && empty($globalListeners)) { return array(); } - ksort($this->_listeners[$eventKey]); + + $listeners = $this->_listeners[$eventKey]; + foreach ($globalListeners as $priority => $priorityQ) { + if (!empty($listeners[$priority])) { + $listeners[$priority] = array_merge($priorityQ, $listeners[$priority]); + unset($globalListeners[$priority]); + } + } + $listeners = $listeners + $globalListeners; + + ksort($listeners); $result = array(); - foreach ($this->_listeners[$eventKey] as $priorityQ) { + foreach ($listeners as $priorityQ) { $result = array_merge($result, $priorityQ); } return $result; } +/** + * Returns the listeners for the specified event key indexed by priority + * + * @param string $eventKey + * @return array + */ + public function prioritisedListeners($eventKey) { + if (empty($this->_listeners[$eventKey])) { + return array(); + } + return $this->_listeners[$eventKey]; + } } diff --git a/lib/Cake/Test/Case/Event/CakeEventManagerTest.php b/lib/Cake/Test/Case/Event/CakeEventManagerTest.php index 79461305e..7df403da4 100644 --- a/lib/Cake/Test/Case/Event/CakeEventManagerTest.php +++ b/lib/Cake/Test/Case/Event/CakeEventManagerTest.php @@ -104,7 +104,7 @@ class CakeEventManagerTest extends CakeTestCase { * @return void */ public function testAttachListeners() { - $manager = new CakeEventManager; + $manager = new CakeEventManager(); $manager->attach('fakeFunction', 'fake.event'); $expected = array( array('callable' => 'fakeFunction', 'passParams' => false) @@ -136,7 +136,7 @@ class CakeEventManagerTest extends CakeTestCase { * @return void */ public function testAttachMultipleEventKeys() { - $manager = new CakeEventManager; + $manager = new CakeEventManager(); $manager->attach('fakeFunction', 'fake.event'); $manager->attach('fakeFunction2', 'another.event'); $manager->attach('fakeFunction3', 'another.event', array('priority' => 1, 'passParams' => true)); @@ -158,7 +158,7 @@ class CakeEventManagerTest extends CakeTestCase { * @return void */ public function testDetach() { - $manager = new CakeEventManager; + $manager = new CakeEventManager(); $manager->attach(array('AClass', 'aMethod'), 'fake.event'); $manager->attach(array('AClass', 'anotherMethod'), 'another.event'); $manager->attach('fakeFunction', 'another.event', array('priority' => 1)); @@ -182,7 +182,7 @@ class CakeEventManagerTest extends CakeTestCase { * @return void */ public function testDetachFromAll() { - $manager = new CakeEventManager; + $manager = new CakeEventManager(); $manager->attach(array('AClass', 'aMethod'), 'fake.event'); $manager->attach(array('AClass', 'aMethod'), 'another.event'); $manager->attach('fakeFunction', 'another.event', array('priority' => 1)); @@ -201,7 +201,7 @@ class CakeEventManagerTest extends CakeTestCase { * @return void */ public function testDispatch() { - $manager = new CakeEventManager; + $manager = new CakeEventManager(); $listener = $this->getMock('CakeEventTestListener'); $anotherListener = $this->getMock('CakeEventTestListener'); $manager->attach(array($listener, 'listenerFunction'), 'fake.event'); @@ -219,8 +219,8 @@ class CakeEventManagerTest extends CakeTestCase { * @return void */ public function testDispatchWithKeyName() { - $manager = new CakeEventManager; - $listener = new CakeEventTestListener; + $manager = new CakeEventManager(); + $listener = new CakeEventTestListener(); $manager->attach(array($listener, 'listenerFunction'), 'fake.event'); $event = 'fake.event'; $manager->dispatch($event); @@ -239,7 +239,7 @@ class CakeEventManagerTest extends CakeTestCase { version_compare(PHPUnit_Runner_Version::id(), '3.7', '<'), 'These tests fail in PHPUnit 3.6' ); - $manager = new CakeEventManager; + $manager = new CakeEventManager(); $listener = $this->getMock('CakeEventTestListener'); $anotherListener = $this->getMock('CakeEventTestListener'); $manager->attach(array($listener, 'listenerFunction'), 'fake.event'); @@ -267,7 +267,7 @@ class CakeEventManagerTest extends CakeTestCase { 'These tests fail in PHPUnit 3.6' ); - $manager = new CakeEventManager; + $manager = new CakeEventManager(); $listener = $this->getMock('CakeEventTestListener'); $anotherListener = $this->getMock('CakeEventTestListener'); $manager->attach(array($listener, 'listenerFunction'), 'fake.event'); @@ -289,8 +289,8 @@ class CakeEventManagerTest extends CakeTestCase { * @return void */ public function testDispatchPrioritized() { - $manager = new CakeEventManager; - $listener = new CakeEventTestListener; + $manager = new CakeEventManager(); + $listener = new CakeEventTestListener(); $manager->attach(array($listener, 'listenerFunction'), 'fake.event'); $manager->attach(array($listener, 'secondListenerFunction'), 'fake.event', array('priority' => 5)); $event = new CakeEvent('fake.event'); @@ -306,7 +306,7 @@ class CakeEventManagerTest extends CakeTestCase { * @return void */ public function testDispatchPassingParams() { - $manager = new CakeEventManager; + $manager = new CakeEventManager(); $listener = $this->getMock('CakeEventTestListener'); $anotherListener = $this->getMock('CakeEventTestListener'); $manager->attach(array($listener, 'listenerFunction'), 'fake.event'); @@ -324,7 +324,7 @@ class CakeEventManagerTest extends CakeTestCase { * @return void */ public function testAttachSubscriber() { - $manager = new CakeEventManager; + $manager = new CakeEventManager(); $listener = $this->getMock('CustomTestEventListener', array('secondListenerFunction')); $manager->attach($listener); $event = new CakeEvent('fake.event'); @@ -338,7 +338,7 @@ class CakeEventManagerTest extends CakeTestCase { $event = new CakeEvent('another.event', $this, array('some' => 'data')); $manager->dispatch($event); - $manager = new CakeEventManager; + $manager = new CakeEventManager(); $listener = $this->getMock('CustomTestEventListener', array('listenerFunction', 'thirdListenerFunction')); $manager->attach($listener); $event = new CakeEvent('multiple.handlers'); @@ -353,7 +353,7 @@ class CakeEventManagerTest extends CakeTestCase { * @return void */ public function testDetachSubscriber() { - $manager = new CakeEventManager; + $manager = new CakeEventManager(); $listener = $this->getMock('CustomTestEventListener', array('secondListenerFunction')); $manager->attach($listener); $expected = array( @@ -376,7 +376,7 @@ class CakeEventManagerTest extends CakeTestCase { */ public function testGlobalDispatcherGetter() { $this->assertInstanceOf('CakeEventManager', CakeEventManager::instance()); - $manager = new CakeEventManager; + $manager = new CakeEventManager(); CakeEventManager::instance($manager); $this->assertSame($manager, CakeEventManager::instance()); @@ -388,13 +388,14 @@ class CakeEventManagerTest extends CakeTestCase { * @return void */ public function testDispatchWithGlobal() { - $generalManager = $this->getMock('CakeEventManager', array('dispatch')); - $manager = new CakeEventManager; + $generalManager = $this->getMock('CakeEventManager', array('prioritisedListeners')); + $manager = new CakeEventManager(); $event = new CakeEvent('fake.event'); CakeEventManager::instance($generalManager); - $generalManager->expects($this->once())->method('dispatch')->with($event); + $generalManager->expects($this->once())->method('prioritisedListeners')->with('fake.event'); $manager->dispatch($event); + CakeEventManager::instance(new CakeEventManager()); } /** @@ -403,8 +404,16 @@ class CakeEventManagerTest extends CakeTestCase { * @return void */ public function testStopPropagation() { - $manager = new CakeEventManager; - $listener = new CakeEventTestListener; + $generalManager = $this->getMock('CakeEventManager'); + $manager = new CakeEventManager(); + $listener = new CakeEventTestListener(); + + CakeEventManager::instance($generalManager); + $generalManager->expects($this->any()) + ->method('prioritisedListeners') + ->with('fake.event') + ->will($this->returnValue(array())); + $manager->attach(array($listener, 'listenerFunction'), 'fake.event'); $manager->attach(array($listener, 'stopListener'), 'fake.event', array('priority' => 8)); $manager->attach(array($listener, 'secondListenerFunction'), 'fake.event', array('priority' => 5)); @@ -413,5 +422,68 @@ class CakeEventManagerTest extends CakeTestCase { $expected = array('secondListenerFunction'); $this->assertEquals($expected, $listener->callStack); + CakeEventManager::instance(new CakeEventManager()); } + +/** + * Tests event dispatching using priorities + * + * @return void + */ + public function testDispatchPrioritizedWithGlobal() { + $generalManager = $this->getMock('CakeEventManager'); + $manager = new CakeEventManager(); + $listener = new CustomTestEventListener(); + $event = new CakeEvent('fake.event'); + + CakeEventManager::instance($generalManager); + $generalManager->expects($this->any()) + ->method('prioritisedListeners') + ->with('fake.event') + ->will($this->returnValue( + array(11 => array( + array('callable' => array($listener, 'secondListenerFunction'), 'passParams' => false) + )) + )); + + $manager->attach(array($listener, 'listenerFunction'), 'fake.event'); + $manager->attach(array($listener, 'thirdListenerFunction'), 'fake.event', array('priority' => 15)); + + $manager->dispatch($event); + + $expected = array('listenerFunction', 'secondListenerFunction', 'thirdListenerFunction'); + $this->assertEquals($expected, $listener->callStack); + CakeEventManager::instance(new CakeEventManager()); + } + +/** + * Tests event dispatching using priorities + * + * @return void + */ + public function testDispatchGlobalBeforeLocal() { + $generalManager = $this->getMock('CakeEventManager'); + $manager = new CakeEventManager(); + $listener = new CustomTestEventListener(); + $event = new CakeEvent('fake.event'); + + CakeEventManager::instance($generalManager); + $generalManager->expects($this->any()) + ->method('prioritisedListeners') + ->with('fake.event') + ->will($this->returnValue( + array(10 => array( + array('callable' => array($listener, 'listenerFunction'), 'passParams' => false) + )) + )); + + $manager->attach(array($listener, 'secondListenerFunction'), 'fake.event'); + + $manager->dispatch($event); + + $expected = array('listenerFunction', 'secondListenerFunction'); + $this->assertEquals($expected, $listener->callStack); + CakeEventManager::instance(new CakeEventManager()); + } + }