Skip to content

Commit

Permalink
Refactoring subscriber attaching and making it possible to define mul…
Browse files Browse the repository at this point in the history
…tiple functions per event
  • Loading branch information
lorenzo committed Dec 25, 2011
1 parent 07d358d commit 0a49bd9
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 22 deletions.
76 changes: 61 additions & 15 deletions lib/Cake/Event/CakeEventManager.php
Expand Up @@ -83,33 +83,27 @@ public static function instance($manager = null) {
/**
* Adds a new listener to an event. Listeners
*
* @param callback|CakeEventListener $callable PHP valid callback type or instance of CakeListener to be called
* when the event named with $eventKey is triggered.
* @param callback|CakeEventListener $callable PHP valid callback type or instance of CakeEventListener to be called
* when the event named with $eventKey is triggered. If a CakeEventListener instances is passed, then the `implementedEvents`
* method will be called on the object to register the declared events individually as methods to be managed by this class.
* It is possible to define multiple event handlers per event name.
*
* @param mixed $eventKey The event unique identifier name to with the callback will be associated. If $callable
* is an instance of CakeEventListener this argument will be ignored
*
* @param array $options used to set the `priority` and `passParams` flags to the listener.
* Priorities are handled like queues, and multiple attachments into the same priority queue will be treated in
* the order of insertion. `passParams` means that the event data property will be converted to function arguments
* when the listener is called. If $called is an instance of CakeEventListener, this parameter will be ignored
*
* @return void
*/
public function attach($callable, $eventKey = null, $options = array()) {
if (!$eventKey && !($callable instanceof CakeEventListener)) {
throw new InvalidArgumentException(__d('cake_dev', 'The eventKey variable is required'));
}
if ($callable instanceof CakeEventListener) {
foreach ($callable->implementedEvents() as $eventKey => $function) {
$options = array();
$method = null;
if (is_array($function)) {
$method = array($callable, $function['callable']);
unset($function['callable']);
$options = $function;
} else {
$method = array($callable, $function);
}
$this->attach($method, $eventKey, $options);
}
$this->_attachSubscriber($callable);
return;
}
$options = $options + array('priority' => self::$defaultPriority, 'passParams' => false);
Expand All @@ -119,10 +113,55 @@ public function attach($callable, $eventKey = null, $options = array()) {
);
}

/**
* Auxiliary function to attach all implemented callbacks of a CakeEventListener class instance
* as individual methods on this manager
*
* @param CakeEventListener $subscriber
* @return void
*/
protected function _attachSubscriber(CakeEventListener $subscriber) {
foreach ($subscriber->implementedEvents() as $eventKey => $function) {
$options = array();
$method = $function;
if (is_array($function) && isset($function['callable'])) {
list($method, $options) = $this->_extractCallable($function, $subscriber);
} else if (is_array($function) && is_numeric(key($function))) {
foreach ($function as $f) {
list($method, $options) = $this->_extractCallable($f, $subscriber);
$this->attach($method, $eventKey, $options);
}
continue;
}
if (is_string($method)) {
$method = array($subscriber, $function);
}
$this->attach($method, $eventKey, $options);
}
}

/**
* Auxiliary function to extract and return a PHP callback type out of the callable definition
* from the return value of the `implementedEvents` method on a CakeEventListener
*
* @param array $function the array taken from a handler definition for a event
* @param CakeEventListener $object The handler object
* @return callback
*/
protected function _extractCallable($function, $object) {
$method = $function['callable'];
$options = $function;
unset($options['callable']);
if (is_string($method)) {
$method = array($object, $method);
}
return array($method, $options);
}

/**
* Removes a listener from the active listeners.
*
* @param callback|CakeListener $callable any valid PHP callback type or an instance of CakeListener
* @param callback|CakeEventListener $callable any valid PHP callback type or an instance of CakeEventListener
* @return void
*/
public function detach($callable, $eventKey = null) {
Expand Down Expand Up @@ -164,6 +203,13 @@ protected function _detachSubscriber(CakeEventListener $subscriber, $eventKey =
}
foreach ($events as $key => $function) {
if (is_array($function)) {
if (is_numeric(key($function))) {
foreach ($function as $handler) {
$handler = isset($handler['callable']) ? $handler['callable'] : $handler;
$this->detach(array($subscriber, $handler), $key);
}
continue;
}
$function = $function['callable'];
}
$this->detach(array($subscriber, $function), $key);
Expand Down
26 changes: 19 additions & 7 deletions lib/Cake/Test/Case/Event/CakeEventManagerTest.php
Expand Up @@ -12,11 +12,11 @@
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright 2005-2011, Cake Software Foundation, Inc.
* @link http://cakephp.org CakePHP Project
* @package Cake.Test.Case.Event
* @since CakePHP v 2.1
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
* @copyright Copyright 2005-2011, Cake Software Foundation, Inc.
* @link http://cakephp.org CakePHP Project
* @package Cake.Test.Case.Event
* @since CakePHP v 2.1
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/

App::uses('CakeEvent', 'Event');
Expand Down Expand Up @@ -71,7 +71,11 @@ class CustomTestEventListerner extends CakeEventTestListener implements CakeEven
public function implementedEvents() {
return array(
'fake.event' => 'listenerFunction',
'another.event' => array('callable' => 'secondListenerFunction', 'passParams' => true)
'another.event' => array('callable' => 'secondListenerFunction', 'passParams' => true),
'multiple.handlers' => array(
array('callable' => 'listenerFunction'),
array('callable' => 'thirdListenerFunction')
)
);
}

Expand Down Expand Up @@ -257,9 +261,17 @@ public function testAttachSubscriber() {
$expected = array('listenerFunction');
$this->assertEquals($expected, $listener->callStack);

$listener->expects($this->once())->method('secondListenerFunction')->with('data');
$listener->expects($this->at(0))->method('secondListenerFunction')->with('data');
$event = new CakeEvent('another.event', $this, array('some' => 'data'));
$manager->dispatch($event);

$manager = new CakeEventManager;
$listener = $this->getMock('CustomTestEventListerner', array('listenerFunction', 'thirdListenerFunction'));
$manager->attach($listener);
$event = new CakeEvent('multiple.handlers');
$listener->expects($this->once())->method('listenerFunction')->with($event);
$listener->expects($this->once())->method('thirdListenerFunction')->with($event);
$manager->dispatch($event);
}

/**
Expand Down

0 comments on commit 0a49bd9

Please sign in to comment.