File indexing completed on 2025-02-09 07:19:47
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_EventManager 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 */ 0020 0021 // require_once 'Zend/EventManager/Event.php'; 0022 // require_once 'Zend/EventManager/EventCollection.php'; 0023 // require_once 'Zend/EventManager/ResponseCollection.php'; 0024 // require_once 'Zend/EventManager/SharedEventCollectionAware.php'; 0025 // require_once 'Zend/EventManager/StaticEventManager.php'; 0026 // require_once 'Zend/Stdlib/CallbackHandler.php'; 0027 // require_once 'Zend/Stdlib/PriorityQueue.php'; 0028 0029 /** 0030 * Event manager: notification system 0031 * 0032 * Use the EventManager when you want to create a per-instance notification 0033 * system for your objects. 0034 * 0035 * @category Zend 0036 * @package Zend_EventManager 0037 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0038 * @license http://framework.zend.com/license/new-bsd New BSD License 0039 */ 0040 class Zend_EventManager_EventManager implements Zend_EventManager_EventCollection, Zend_EventManager_SharedEventCollectionAware 0041 { 0042 /** 0043 * Subscribed events and their listeners 0044 * @var array Array of Zend_Stdlib_PriorityQueue objects 0045 */ 0046 protected $events = array(); 0047 0048 /** 0049 * @var string Class representing the event being emitted 0050 */ 0051 protected $eventClass = 'Zend_EventManager_Event'; 0052 0053 /** 0054 * Identifiers, used to pull static signals from StaticEventManager 0055 * @var array 0056 */ 0057 protected $identifiers = array(); 0058 0059 /** 0060 * Static collections 0061 * @var false|null|Zend_EventManager_StaticEventCollection 0062 */ 0063 protected $sharedCollections = null; 0064 0065 /** 0066 * Constructor 0067 * 0068 * Allows optionally specifying identifier(s) to use to pull signals from a 0069 * StaticEventManager. 0070 * 0071 * @param null|string|int|array|Traversable $identifiers 0072 * @return void 0073 */ 0074 public function __construct($identifiers = null) 0075 { 0076 $this->setIdentifiers($identifiers); 0077 } 0078 0079 /** 0080 * Set the event class to utilize 0081 * 0082 * @param string $class 0083 * @return Zend_EventManager_EventManager 0084 */ 0085 public function setEventClass($class) 0086 { 0087 $this->eventClass = $class; 0088 return $this; 0089 } 0090 0091 /** 0092 * Set static collections container 0093 * 0094 * @param Zend_EventManager_SharedEventCollection $collections 0095 * @return $this 0096 */ 0097 public function setSharedCollections(Zend_EventManager_SharedEventCollection $collections) 0098 { 0099 $this->sharedCollections = $collections; 0100 return $this; 0101 } 0102 0103 /** 0104 * Remove any shared collections 0105 * 0106 * Sets {@link $sharedCollections} to boolean false to disable ability 0107 * to lazy-load static event manager instance. 0108 * 0109 * @return void 0110 */ 0111 public function unsetSharedCollections() 0112 { 0113 $this->sharedCollections = false; 0114 } 0115 0116 /** 0117 * Get static collections container 0118 * 0119 * @return false|Zend_EventManager_SharedEventCollection 0120 */ 0121 public function getSharedCollections() 0122 { 0123 if (null === $this->sharedCollections) { 0124 $this->setSharedCollections(Zend_EventManager_StaticEventManager::getInstance()); 0125 } 0126 return $this->sharedCollections; 0127 } 0128 0129 /** 0130 * Get the identifier(s) for this Zend_EventManager_EventManager 0131 * 0132 * @return array 0133 */ 0134 public function getIdentifiers() 0135 { 0136 return $this->identifiers; 0137 } 0138 0139 /** 0140 * Set the identifiers (overrides any currently set identifiers) 0141 * 0142 * @param string|int|array|Traversable $identifiers 0143 * @return Zend_EventManager_EventManager 0144 */ 0145 public function setIdentifiers($identifiers) 0146 { 0147 if (is_array($identifiers) || $identifiers instanceof Traversable) { 0148 $this->identifiers = array_unique((array) $identifiers); 0149 } elseif ($identifiers !== null) { 0150 $this->identifiers = array($identifiers); 0151 } 0152 return $this; 0153 } 0154 0155 /** 0156 * Add some identifier(s) (appends to any currently set identifiers) 0157 * 0158 * @param string|int|array|Traversable $identifiers 0159 * @return Zend_EventManager_EventManager 0160 */ 0161 public function addIdentifiers($identifiers) 0162 { 0163 if (is_array($identifiers) || $identifiers instanceof Traversable) { 0164 $this->identifiers = array_unique($this->identifiers + (array) $identifiers); 0165 } elseif ($identifiers !== null) { 0166 $this->identifiers = array_unique(array_merge($this->identifiers, array($identifiers))); 0167 } 0168 return $this; 0169 } 0170 0171 /** 0172 * Trigger all listeners for a given event 0173 * 0174 * Can emulate triggerUntil() if the last argument provided is a callback. 0175 * 0176 * @param string $event 0177 * @param string|object $target Object calling emit, or symbol describing target (such as static method name) 0178 * @param array|ArrayAccess $argv Array of arguments; typically, should be associative 0179 * @param null|callback $callback 0180 * @return Zend_EventManager_ResponseCollection All listener return values 0181 */ 0182 public function trigger($event, $target = null, $argv = array(), $callback = null) 0183 { 0184 if ($event instanceof Zend_EventManager_EventDescription) { 0185 $e = $event; 0186 $event = $e->getName(); 0187 $callback = $target; 0188 } elseif ($target instanceof Zend_EventManager_EventDescription) { 0189 $e = $target; 0190 $e->setName($event); 0191 $callback = $argv; 0192 } elseif ($argv instanceof Zend_EventManager_EventDescription) { 0193 $e = $argv; 0194 $e->setName($event); 0195 $e->setTarget($target); 0196 } else { 0197 $e = new $this->eventClass(); 0198 $e->setName($event); 0199 $e->setTarget($target); 0200 $e->setParams($argv); 0201 } 0202 0203 if ($callback && !is_callable($callback)) { 0204 // require_once 'Zend/Stdlib/Exception/InvalidCallbackException.php'; 0205 throw new Zend_Stdlib_Exception_InvalidCallbackException('Invalid callback provided'); 0206 } 0207 0208 return $this->triggerListeners($event, $e, $callback); 0209 } 0210 0211 /** 0212 * Trigger listeners until return value of one causes a callback to 0213 * evaluate to true 0214 * 0215 * Triggers listeners until the provided callback evaluates the return 0216 * value of one as true, or until all listeners have been executed. 0217 * 0218 * @param string $event 0219 * @param string|object $target Object calling emit, or symbol describing target (such as static method name) 0220 * @param array|ArrayAccess $argv Array of arguments; typically, should be associative 0221 * @param Callable $callback 0222 * @throws Zend_Stdlib_Exception_InvalidCallbackException if invalid callback provided 0223 */ 0224 public function triggerUntil($event, $target, $argv = null, $callback = null) 0225 { 0226 if ($event instanceof Zend_EventManager_EventDescription) { 0227 $e = $event; 0228 $event = $e->getName(); 0229 $callback = $target; 0230 } elseif ($target instanceof Zend_EventManager_EventDescription) { 0231 $e = $target; 0232 $e->setName($event); 0233 $callback = $argv; 0234 } elseif ($argv instanceof Zend_EventManager_EventDescription) { 0235 $e = $argv; 0236 $e->setName($event); 0237 $e->setTarget($target); 0238 } else { 0239 $e = new $this->eventClass(); 0240 $e->setName($event); 0241 $e->setTarget($target); 0242 $e->setParams($argv); 0243 } 0244 0245 if (!is_callable($callback)) { 0246 // require_once 'Zend/Stdlib/Exception/InvalidCallbackException.php'; 0247 throw new Zend_Stdlib_Exception_InvalidCallbackException('Invalid callback provided'); 0248 } 0249 0250 return $this->triggerListeners($event, $e, $callback); 0251 } 0252 0253 /** 0254 * Attach a listener to an event 0255 * 0256 * The first argument is the event, and the next argument describes a 0257 * callback that will respond to that event. A CallbackHandler instance 0258 * describing the event listener combination will be returned. 0259 * 0260 * The last argument indicates a priority at which the event should be 0261 * executed. By default, this value is 1; however, you may set it for any 0262 * integer value. Higher values have higher priority (i.e., execute first). 0263 * 0264 * You can specify "*" for the event name. In such cases, the listener will 0265 * be triggered for every event. 0266 * 0267 * @param string|array|Zend_EventManager_ListenerAggregate $event An event or array of event names. If a ListenerAggregate, proxies to {@link attachAggregate()}. 0268 * @param callback|int $callback If string $event provided, expects PHP callback; for a ListenerAggregate $event, this will be the priority 0269 * @param int $priority If provided, the priority at which to register the callback 0270 * @return Zend_Stdlib_CallbackHandler|mixed CallbackHandler if attaching callback (to allow later unsubscribe); mixed if attaching aggregate 0271 */ 0272 public function attach($event, $callback = null, $priority = 1) 0273 { 0274 // Proxy ListenerAggregate arguments to attachAggregate() 0275 if ($event instanceof Zend_EventManager_ListenerAggregate) { 0276 return $this->attachAggregate($event, $callback); 0277 } 0278 0279 // Null callback is invalid 0280 if (null === $callback) { 0281 // require_once 'Zend/EventManager/Exception/InvalidArgumentException.php'; 0282 throw new Zend_EventManager_Exception_InvalidArgumentException(sprintf( 0283 '%s: expects a callback; none provided', 0284 __METHOD__ 0285 )); 0286 } 0287 0288 // Array of events should be registered individually, and return an array of all listeners 0289 if (is_array($event)) { 0290 $listeners = array(); 0291 foreach ($event as $name) { 0292 $listeners[] = $this->attach($name, $callback, $priority); 0293 } 0294 return $listeners; 0295 } 0296 0297 // If we don't have a priority queue for the event yet, create one 0298 if (empty($this->events[$event])) { 0299 $this->events[$event] = new Zend_Stdlib_PriorityQueue(); 0300 } 0301 0302 // Create a callback handler, setting the event and priority in its metadata 0303 $listener = new Zend_Stdlib_CallbackHandler($callback, array('event' => $event, 'priority' => $priority)); 0304 0305 // Inject the callback handler into the queue 0306 $this->events[$event]->insert($listener, $priority); 0307 return $listener; 0308 } 0309 0310 /** 0311 * Attach a listener aggregate 0312 * 0313 * Listener aggregates accept an EventCollection instance, and call attach() 0314 * one or more times, typically to attach to multiple events using local 0315 * methods. 0316 * 0317 * @param Zend_EventManager_ListenerAggregate $aggregate 0318 * @param int $priority If provided, a suggested priority for the aggregate to use 0319 * @return mixed return value of {@link Zend_EventManager_ListenerAggregate::attach()} 0320 */ 0321 public function attachAggregate(Zend_EventManager_ListenerAggregate $aggregate, $priority = 1) 0322 { 0323 return $aggregate->attach($this, $priority); 0324 } 0325 0326 /** 0327 * Unsubscribe a listener from an event 0328 * 0329 * @param Zend_Stdlib_CallbackHandler|Zend_EventManager_ListenerAggregate $listener 0330 * @return bool Returns true if event and listener found, and unsubscribed; returns false if either event or listener not found 0331 * @throws Zend_EventManager_Exception_InvalidArgumentException if invalid listener provided 0332 */ 0333 public function detach($listener) 0334 { 0335 if ($listener instanceof Zend_EventManager_ListenerAggregate) { 0336 return $this->detachAggregate($listener); 0337 } 0338 0339 if (!$listener instanceof Zend_Stdlib_CallbackHandler) { 0340 // require_once 'Zend/EventManager/Exception/InvalidArgumentException.php'; 0341 throw new Zend_EventManager_Exception_InvalidArgumentException(sprintf( 0342 '%s: expected a Zend_EventManager_ListenerAggregate or Zend_Stdlib_CallbackHandler; received "%s"', 0343 __METHOD__, 0344 (is_object($listener) ? get_class($listener) : gettype($listener)) 0345 )); 0346 } 0347 0348 $event = $listener->getMetadatum('event'); 0349 if (!$event || empty($this->events[$event])) { 0350 return false; 0351 } 0352 $return = $this->events[$event]->remove($listener); 0353 if (!$return) { 0354 return false; 0355 } 0356 if (!count($this->events[$event])) { 0357 unset($this->events[$event]); 0358 } 0359 return true; 0360 } 0361 0362 /** 0363 * Detach a listener aggregate 0364 * 0365 * Listener aggregates accept an EventCollection instance, and call detach() 0366 * of all previously attached listeners. 0367 * 0368 * @param Zend_EventManager_ListenerAggregate $aggregate 0369 * @return mixed return value of {@link Zend_EventManager_ListenerAggregate::detach()} 0370 */ 0371 public function detachAggregate(Zend_EventManager_ListenerAggregate $aggregate) 0372 { 0373 return $aggregate->detach($this); 0374 } 0375 0376 /** 0377 * Retrieve all registered events 0378 * 0379 * @return array 0380 */ 0381 public function getEvents() 0382 { 0383 return array_keys($this->events); 0384 } 0385 0386 /** 0387 * Retrieve all listeners for a given event 0388 * 0389 * @param string $event 0390 * @return Zend_Stdlib_PriorityQueue 0391 */ 0392 public function getListeners($event) 0393 { 0394 if (!array_key_exists($event, $this->events)) { 0395 return new Zend_Stdlib_PriorityQueue(); 0396 } 0397 return $this->events[$event]; 0398 } 0399 0400 /** 0401 * Clear all listeners for a given event 0402 * 0403 * @param string $event 0404 * @return void 0405 */ 0406 public function clearListeners($event) 0407 { 0408 if (!empty($this->events[$event])) { 0409 unset($this->events[$event]); 0410 } 0411 } 0412 0413 /** 0414 * Prepare arguments 0415 * 0416 * Use this method if you want to be able to modify arguments from within a 0417 * listener. It returns an ArrayObject of the arguments, which may then be 0418 * passed to trigger() or triggerUntil(). 0419 * 0420 * @param array $args 0421 * @return ArrayObject 0422 */ 0423 public function prepareArgs(array $args) 0424 { 0425 return new ArrayObject($args); 0426 } 0427 0428 /** 0429 * Trigger listeners 0430 * 0431 * Actual functionality for triggering listeners, to which both trigger() and triggerUntil() 0432 * delegate. 0433 * 0434 * @param string $event Event name 0435 * @param EventDescription $e 0436 * @param null|callback $callback 0437 * @return ResponseCollection 0438 */ 0439 protected function triggerListeners($event, Zend_EventManager_EventDescription $e, $callback = null) 0440 { 0441 $responses = new Zend_EventManager_ResponseCollection; 0442 $listeners = $this->getListeners($event); 0443 0444 // Add shared/wildcard listeners to the list of listeners, 0445 // but don't modify the listeners object 0446 $sharedListeners = $this->getSharedListeners($event); 0447 $sharedWildcardListeners = $this->getSharedListeners('*'); 0448 $wildcardListeners = $this->getListeners('*'); 0449 if (count($sharedListeners) || count($sharedWildcardListeners) || count($wildcardListeners)) { 0450 $listeners = clone $listeners; 0451 } 0452 0453 // Shared listeners on this specific event 0454 $this->insertListeners($listeners, $sharedListeners); 0455 0456 // Shared wildcard listeners 0457 $this->insertListeners($listeners, $sharedWildcardListeners); 0458 0459 // Add wildcard listeners 0460 $this->insertListeners($listeners, $wildcardListeners); 0461 0462 if ($listeners->isEmpty()) { 0463 return $responses; 0464 } 0465 0466 foreach ($listeners as $listener) { 0467 // Trigger the listener's callback, and push its result onto the 0468 // response collection 0469 $responses->push(call_user_func($listener->getCallback(), $e)); 0470 0471 // If the event was asked to stop propagating, do so 0472 if ($e->propagationIsStopped()) { 0473 $responses->setStopped(true); 0474 break; 0475 } 0476 0477 // If the result causes our validation callback to return true, 0478 // stop propagation 0479 if ($callback && call_user_func($callback, $responses->last())) { 0480 $responses->setStopped(true); 0481 break; 0482 } 0483 } 0484 0485 return $responses; 0486 } 0487 0488 /** 0489 * Get list of all listeners attached to the shared collection for 0490 * identifiers registered by this instance 0491 * 0492 * @param string $event 0493 * @return array 0494 */ 0495 protected function getSharedListeners($event) 0496 { 0497 if (!$sharedCollections = $this->getSharedCollections()) { 0498 return array(); 0499 } 0500 0501 $identifiers = $this->getIdentifiers(); 0502 $sharedListeners = array(); 0503 0504 foreach ($identifiers as $id) { 0505 if (!$listeners = $sharedCollections->getListeners($id, $event)) { 0506 continue; 0507 } 0508 0509 if (!is_array($listeners) && !($listeners instanceof Traversable)) { 0510 continue; 0511 } 0512 0513 foreach ($listeners as $listener) { 0514 if (!$listener instanceof Zend_Stdlib_CallbackHandler) { 0515 continue; 0516 } 0517 $sharedListeners[] = $listener; 0518 } 0519 } 0520 0521 return $sharedListeners; 0522 } 0523 0524 /** 0525 * Add listeners to the master queue of listeners 0526 * 0527 * Used to inject shared listeners and wildcard listeners. 0528 * 0529 * @param Zend_Stdlib_PriorityQueue $masterListeners 0530 * @param Zend_Stdlib_PriorityQueue $listeners 0531 * @return void 0532 */ 0533 protected function insertListeners($masterListeners, $listeners) 0534 { 0535 if (!count($listeners)) { 0536 return; 0537 } 0538 0539 foreach ($listeners as $listener) { 0540 $priority = $listener->getMetadatum('priority'); 0541 if (null === $priority) { 0542 $priority = 1; 0543 } elseif (is_array($priority)) { 0544 // If we have an array, likely using PriorityQueue. Grab first 0545 // element of the array, as that's the actual priority. 0546 $priority = array_shift($priority); 0547 } 0548 $masterListeners->insert($listener, $priority); 0549 } 0550 } 0551 }