Merge branch 'ticket-4059' into 2.5

Merge the changes from pull request #1638 into 2.5.

This set of changes combines the listeners from the global and local
event managers into one ordered set of listeners. This makes the
cognitive load lower around events, as there are no longer two separate
priority queues for the global and local managers.

Closes #2105
This commit is contained in:
mark_story 2013-11-10 22:35:23 -05:00
commit fc170770d5
2 changed files with 126 additions and 30 deletions

View file

@ -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];
}
}

View file

@ -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());
}
}