| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551 |
- <?php
- /**
- * Zend Framework
- *
- * LICENSE
- *
- * This source file is subject to the new BSD license that is bundled
- * with this package in the file LICENSE.txt.
- * It is also available through the world-wide-web at this URL:
- * http://framework.zend.com/license/new-bsd
- * If you did not receive a copy of the license and are unable to
- * obtain it through the world-wide-web, please send an email
- * to license@zend.com so we can send you a copy immediately.
- *
- * @category Zend
- * @package Zend_EventManager
- * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
- * @license http://framework.zend.com/license/new-bsd New BSD License
- */
- require_once 'Zend/EventManager/Event.php';
- require_once 'Zend/EventManager/EventCollection.php';
- require_once 'Zend/EventManager/ResponseCollection.php';
- require_once 'Zend/EventManager/SharedEventCollectionAware.php';
- require_once 'Zend/EventManager/StaticEventManager.php';
- require_once 'Zend/Stdlib/CallbackHandler.php';
- require_once 'Zend/Stdlib/PriorityQueue.php';
- /**
- * Event manager: notification system
- *
- * Use the EventManager when you want to create a per-instance notification
- * system for your objects.
- *
- * @category Zend
- * @package Zend_EventManager
- * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
- * @license http://framework.zend.com/license/new-bsd New BSD License
- */
- class Zend_EventManager_EventManager implements Zend_EventManager_EventCollection, Zend_EventManager_SharedEventCollectionAware
- {
- /**
- * Subscribed events and their listeners
- * @var array Array of Zend_Stdlib_PriorityQueue objects
- */
- protected $events = array();
- /**
- * @var string Class representing the event being emitted
- */
- protected $eventClass = 'Zend_EventManager_Event';
- /**
- * Identifiers, used to pull static signals from StaticEventManager
- * @var array
- */
- protected $identifiers = array();
- /**
- * Static collections
- * @var false|null|Zend_EventManager_StaticEventCollection
- */
- protected $sharedCollections = null;
- /**
- * Constructor
- *
- * Allows optionally specifying identifier(s) to use to pull signals from a
- * StaticEventManager.
- *
- * @param null|string|int|array|Traversable $identifiers
- * @return void
- */
- public function __construct($identifiers = null)
- {
- $this->setIdentifiers($identifiers);
- }
- /**
- * Set the event class to utilize
- *
- * @param string $class
- * @return Zend_EventManager_EventManager
- */
- public function setEventClass($class)
- {
- $this->eventClass = $class;
- return $this;
- }
- /**
- * Set static collections container
- *
- * @param Zend_EventManager_SharedEventCollection $collections
- * @return $this
- */
- public function setSharedCollections(Zend_EventManager_SharedEventCollection $collections)
- {
- $this->sharedCollections = $collections;
- return $this;
- }
- /**
- * Remove any shared collections
- *
- * Sets {@link $sharedCollections} to boolean false to disable ability
- * to lazy-load static event manager instance.
- *
- * @return void
- */
- public function unsetSharedCollections()
- {
- $this->sharedCollections = false;
- }
- /**
- * Get static collections container
- *
- * @return false|Zend_EventManager_SharedEventCollection
- */
- public function getSharedCollections()
- {
- if (null === $this->sharedCollections) {
- $this->setSharedCollections(Zend_EventManager_StaticEventManager::getInstance());
- }
- return $this->sharedCollections;
- }
- /**
- * Get the identifier(s) for this Zend_EventManager_EventManager
- *
- * @return array
- */
- public function getIdentifiers()
- {
- return $this->identifiers;
- }
- /**
- * Set the identifiers (overrides any currently set identifiers)
- *
- * @param string|int|array|Traversable $identifiers
- * @return Zend_EventManager_EventManager
- */
- public function setIdentifiers($identifiers)
- {
- if (is_array($identifiers) || $identifiers instanceof Traversable) {
- $this->identifiers = array_unique((array) $identifiers);
- } elseif ($identifiers !== null) {
- $this->identifiers = array($identifiers);
- }
- return $this;
- }
- /**
- * Add some identifier(s) (appends to any currently set identifiers)
- *
- * @param string|int|array|Traversable $identifiers
- * @return Zend_EventManager_EventManager
- */
- public function addIdentifiers($identifiers)
- {
- if (is_array($identifiers) || $identifiers instanceof Traversable) {
- $this->identifiers = array_unique($this->identifiers + (array) $identifiers);
- } elseif ($identifiers !== null) {
- $this->identifiers = array_unique(array_merge($this->identifiers, array($identifiers)));
- }
- return $this;
- }
- /**
- * Trigger all listeners for a given event
- *
- * Can emulate triggerUntil() if the last argument provided is a callback.
- *
- * @param string $event
- * @param string|object $target Object calling emit, or symbol describing target (such as static method name)
- * @param array|ArrayAccess $argv Array of arguments; typically, should be associative
- * @param null|callback $callback
- * @return Zend_EventManager_ResponseCollection All listener return values
- */
- public function trigger($event, $target = null, $argv = array(), $callback = null)
- {
- if ($event instanceof Zend_EventManager_EventDescription) {
- $e = $event;
- $event = $e->getName();
- $callback = $target;
- } elseif ($target instanceof Zend_EventManager_EventDescription) {
- $e = $target;
- $e->setName($event);
- $callback = $argv;
- } elseif ($argv instanceof Zend_EventManager_EventDescription) {
- $e = $argv;
- $e->setName($event);
- $e->setTarget($target);
- } else {
- $e = new $this->eventClass();
- $e->setName($event);
- $e->setTarget($target);
- $e->setParams($argv);
- }
- if ($callback && !is_callable($callback)) {
- require_once 'Zend/Stdlib/Exception/InvalidCallbackException.php';
- throw new Zend_Stdlib_Exception_InvalidCallbackException('Invalid callback provided');
- }
- return $this->triggerListeners($event, $e, $callback);
- }
- /**
- * Trigger listeners until return value of one causes a callback to
- * evaluate to true
- *
- * Triggers listeners until the provided callback evaluates the return
- * value of one as true, or until all listeners have been executed.
- *
- * @param string $event
- * @param string|object $target Object calling emit, or symbol describing target (such as static method name)
- * @param array|ArrayAccess $argv Array of arguments; typically, should be associative
- * @param Callable $callback
- * @throws Zend_Stdlib_Exception_InvalidCallbackException if invalid callback provided
- */
- public function triggerUntil($event, $target, $argv = null, $callback = null)
- {
- if ($event instanceof Zend_EventManager_EventDescription) {
- $e = $event;
- $event = $e->getName();
- $callback = $target;
- } elseif ($target instanceof Zend_EventManager_EventDescription) {
- $e = $target;
- $e->setName($event);
- $callback = $argv;
- } elseif ($argv instanceof Zend_EventManager_EventDescription) {
- $e = $argv;
- $e->setName($event);
- $e->setTarget($target);
- } else {
- $e = new $this->eventClass();
- $e->setName($event);
- $e->setTarget($target);
- $e->setParams($argv);
- }
- if (!is_callable($callback)) {
- require_once 'Zend/Stdlib/Exception/InvalidCallbackException.php';
- throw new Zend_Stdlib_Exception_InvalidCallbackException('Invalid callback provided');
- }
- return $this->triggerListeners($event, $e, $callback);
- }
- /**
- * Attach a listener to an event
- *
- * The first argument is the event, and the next argument describes a
- * callback that will respond to that event. A CallbackHandler instance
- * describing the event listener combination will be returned.
- *
- * The last argument indicates a priority at which the event should be
- * executed. By default, this value is 1; however, you may set it for any
- * integer value. Higher values have higher priority (i.e., execute first).
- *
- * You can specify "*" for the event name. In such cases, the listener will
- * be triggered for every event.
- *
- * @param string|array|Zend_EventManager_ListenerAggregate $event An event or array of event names. If a ListenerAggregate, proxies to {@link attachAggregate()}.
- * @param callback|int $callback If string $event provided, expects PHP callback; for a ListenerAggregate $event, this will be the priority
- * @param int $priority If provided, the priority at which to register the callback
- * @return Zend_Stdlib_CallbackHandler|mixed CallbackHandler if attaching callback (to allow later unsubscribe); mixed if attaching aggregate
- */
- public function attach($event, $callback = null, $priority = 1)
- {
- // Proxy ListenerAggregate arguments to attachAggregate()
- if ($event instanceof Zend_EventManager_ListenerAggregate) {
- return $this->attachAggregate($event, $callback);
- }
- // Null callback is invalid
- if (null === $callback) {
- require_once 'Zend/EventManager/Exception/InvalidArgumentException.php';
- throw new Zend_EventManager_Exception_InvalidArgumentException(sprintf(
- '%s: expects a callback; none provided',
- __METHOD__
- ));
- }
- // Array of events should be registered individually, and return an array of all listeners
- if (is_array($event)) {
- $listeners = array();
- foreach ($event as $name) {
- $listeners[] = $this->attach($name, $callback, $priority);
- }
- return $listeners;
- }
- // If we don't have a priority queue for the event yet, create one
- if (empty($this->events[$event])) {
- $this->events[$event] = new Zend_Stdlib_PriorityQueue();
- }
- // Create a callback handler, setting the event and priority in its metadata
- $listener = new Zend_Stdlib_CallbackHandler($callback, array('event' => $event, 'priority' => $priority));
- // Inject the callback handler into the queue
- $this->events[$event]->insert($listener, $priority);
- return $listener;
- }
- /**
- * Attach a listener aggregate
- *
- * Listener aggregates accept an EventCollection instance, and call attach()
- * one or more times, typically to attach to multiple events using local
- * methods.
- *
- * @param Zend_EventManager_ListenerAggregate $aggregate
- * @param int $priority If provided, a suggested priority for the aggregate to use
- * @return mixed return value of {@link Zend_EventManager_ListenerAggregate::attach()}
- */
- public function attachAggregate(Zend_EventManager_ListenerAggregate $aggregate, $priority = 1)
- {
- return $aggregate->attach($this, $priority);
- }
- /**
- * Unsubscribe a listener from an event
- *
- * @param Zend_Stdlib_CallbackHandler|Zend_EventManager_ListenerAggregate $listener
- * @return bool Returns true if event and listener found, and unsubscribed; returns false if either event or listener not found
- * @throws Zend_EventManager_Exception_InvalidArgumentException if invalid listener provided
- */
- public function detach($listener)
- {
- if ($listener instanceof Zend_EventManager_ListenerAggregate) {
- return $this->detachAggregate($listener);
- }
- if (!$listener instanceof Zend_Stdlib_CallbackHandler) {
- require_once 'Zend/EventManager/Exception/InvalidArgumentException.php';
- throw new Zend_EventManager_Exception_InvalidArgumentException(sprintf(
- '%s: expected a Zend_EventManager_ListenerAggregate or Zend_Stdlib_CallbackHandler; received "%s"',
- __METHOD__,
- (is_object($listener) ? get_class($listener) : gettype($listener))
- ));
- }
- $event = $listener->getMetadatum('event');
- if (!$event || empty($this->events[$event])) {
- return false;
- }
- $return = $this->events[$event]->remove($listener);
- if (!$return) {
- return false;
- }
- if (!count($this->events[$event])) {
- unset($this->events[$event]);
- }
- return true;
- }
- /**
- * Detach a listener aggregate
- *
- * Listener aggregates accept an EventCollection instance, and call detach()
- * of all previously attached listeners.
- *
- * @param Zend_EventManager_ListenerAggregate $aggregate
- * @return mixed return value of {@link Zend_EventManager_ListenerAggregate::detach()}
- */
- public function detachAggregate(Zend_EventManager_ListenerAggregate $aggregate)
- {
- return $aggregate->detach($this);
- }
- /**
- * Retrieve all registered events
- *
- * @return array
- */
- public function getEvents()
- {
- return array_keys($this->events);
- }
- /**
- * Retrieve all listeners for a given event
- *
- * @param string $event
- * @return Zend_Stdlib_PriorityQueue
- */
- public function getListeners($event)
- {
- if (!array_key_exists($event, $this->events)) {
- return new Zend_Stdlib_PriorityQueue();
- }
- return $this->events[$event];
- }
- /**
- * Clear all listeners for a given event
- *
- * @param string $event
- * @return void
- */
- public function clearListeners($event)
- {
- if (!empty($this->events[$event])) {
- unset($this->events[$event]);
- }
- }
- /**
- * Prepare arguments
- *
- * Use this method if you want to be able to modify arguments from within a
- * listener. It returns an ArrayObject of the arguments, which may then be
- * passed to trigger() or triggerUntil().
- *
- * @param array $args
- * @return ArrayObject
- */
- public function prepareArgs(array $args)
- {
- return new ArrayObject($args);
- }
- /**
- * Trigger listeners
- *
- * Actual functionality for triggering listeners, to which both trigger() and triggerUntil()
- * delegate.
- *
- * @param string $event Event name
- * @param EventDescription $e
- * @param null|callback $callback
- * @return ResponseCollection
- */
- protected function triggerListeners($event, Zend_EventManager_EventDescription $e, $callback = null)
- {
- $responses = new Zend_EventManager_ResponseCollection;
- $listeners = $this->getListeners($event);
- // Add shared/wildcard listeners to the list of listeners,
- // but don't modify the listeners object
- $sharedListeners = $this->getSharedListeners($event);
- $sharedWildcardListeners = $this->getSharedListeners('*');
- $wildcardListeners = $this->getListeners('*');
- if (count($sharedListeners) || count($sharedWildcardListeners) || count($wildcardListeners)) {
- $listeners = clone $listeners;
- }
- // Shared listeners on this specific event
- $this->insertListeners($listeners, $sharedListeners);
- // Shared wildcard listeners
- $this->insertListeners($listeners, $sharedWildcardListeners);
- // Add wildcard listeners
- $this->insertListeners($listeners, $wildcardListeners);
- if ($listeners->isEmpty()) {
- return $responses;
- }
- foreach ($listeners as $listener) {
- // Trigger the listener's callback, and push its result onto the
- // response collection
- $responses->push(call_user_func($listener->getCallback(), $e));
- // If the event was asked to stop propagating, do so
- if ($e->propagationIsStopped()) {
- $responses->setStopped(true);
- break;
- }
- // If the result causes our validation callback to return true,
- // stop propagation
- if ($callback && call_user_func($callback, $responses->last())) {
- $responses->setStopped(true);
- break;
- }
- }
- return $responses;
- }
- /**
- * Get list of all listeners attached to the shared collection for
- * identifiers registered by this instance
- *
- * @param string $event
- * @return array
- */
- protected function getSharedListeners($event)
- {
- if (!$sharedCollections = $this->getSharedCollections()) {
- return array();
- }
- $identifiers = $this->getIdentifiers();
- $sharedListeners = array();
- foreach ($identifiers as $id) {
- if (!$listeners = $sharedCollections->getListeners($id, $event)) {
- continue;
- }
- if (!is_array($listeners) && !($listeners instanceof Traversable)) {
- continue;
- }
- foreach ($listeners as $listener) {
- if (!$listener instanceof Zend_Stdlib_CallbackHandler) {
- continue;
- }
- $sharedListeners[] = $listener;
- }
- }
- return $sharedListeners;
- }
- /**
- * Add listeners to the master queue of listeners
- *
- * Used to inject shared listeners and wildcard listeners.
- *
- * @param Zend_Stdlib_PriorityQueue $masterListeners
- * @param Zend_Stdlib_PriorityQueue $listeners
- * @return void
- */
- protected function insertListeners($masterListeners, $listeners)
- {
- if (!count($listeners)) {
- return;
- }
- foreach ($listeners as $listener) {
- $priority = $listener->getMetadatum('priority');
- if (null === $priority) {
- $priority = 1;
- } elseif (is_array($priority)) {
- // If we have an array, likely using PriorityQueue. Grab first
- // element of the array, as that's the actual priority.
- $priority = array_shift($priority);
- }
- $masterListeners->insert($listener, $priority);
- }
- }
- }
|