($priority => ($listener, ...), ...), ...) * @var array */ protected $_listeners = []; /** * Two dimensional array of listeners sorter by priority ($eventName => ($listener, ...), ...) * @var array */ protected $_sorted = []; /** @var Qs_Event_Dispatcher */ protected static $_instance; public static function getInstance() { if (null === static::$_instance) { static::$_instance = new Qs_Event_Dispatcher(); $config = Qs_Config::getByInstance(static::$_instance); if (isset($config->dispatcher)) { static::$_instance->setOptions($config->dispatcher->toArray()); } } return static::$_instance; } /** * @param array|Zend_Config $options */ public function __construct($options = []) { $this->setOptions($options); return $this; } /** * @param array|Zend_Config $options * @return Qs_Event_Dispatcher */ public function setOptions($options = []) { Qs_Options::setOptions($this, $options); return $this; } /** * @param array $listeners * format: (string $eventName, callable $listener, [int $priority]), * format: ('eventName' => string $eventName, 'listener' => callable $listener, ['priority' => int $priority]) * @return Qs_Event_Dispatcher */ public function setListeners(array $listeners) { foreach ($listeners as $listenerInfo) { if (isset($listenerInfo['eventName']) && isset($listenerInfo['listener'])) { $eventName = $listenerInfo['eventName']; $listener = $listenerInfo['listener']; $priority = (isset($listenerInfo['priority'])) ? $listenerInfo['priority'] : 0; } else { $eventName = isset($listenerInfo[0]) ? $listenerInfo[0] : null; $listener = isset($listenerInfo[1]) ? $listenerInfo[1] : null; $priority = isset($listenerInfo[2]) ? $listenerInfo[2] : null; } $this->addListener($eventName, $listener, (int) $priority); } return $this; } /** * Dispatches an event to all registered listeners. * @param string $eventName The name of the event to dispatch. The name of * the event is the name of the method that is * invoked on listeners. * @param Qs_Event $event The event to pass to the event handlers/listeners. * If not supplied, an empty Event instance is created. * @param stdClass|string|null $eventNamespace [OPTIONAL] Event namespace * @return Qs_Event */ public function dispatch($eventName, Qs_Event $event = null, $eventNamespace = null) { if (null !== $eventNamespace) { $eventNamespace = (is_object($eventNamespace)) ? get_class($eventNamespace) : (string) $eventNamespace; $eventName = $eventNamespace . '/' . $eventName; } if (null === $event) { $event = new Qs_Event(); } $event->setDispatcher($this); $event->setName($eventName); if (!isset($this->_listeners[$eventName])) { return $event; } $this->_doDispatch($this->getListeners($eventName), $eventName, $event); return $event; } /** * Gets the listeners of a specific event or all listeners. * * @param string $eventName The name of the event * @return array The event listeners for the specified event, or all event listeners by event name */ public function getListeners($eventName = null) { if (null !== $eventName) { if (!isset($this->_sorted[$eventName])) { $this->_sortListeners($eventName); } return $this->_sorted[$eventName]; } foreach (array_keys($this->_listeners) as $eventName) { if (!isset($this->_sorted[$eventName])) { $this->_sortListeners($eventName); } } return $this->_sorted; } /** * Checks whether an event has any registered listeners. * * @param string $eventName The name of the event * * @return bool true if the specified event has any listeners, false otherwise */ public function hasListeners($eventName = null) { return (bool) count($this->getListeners($eventName)); } /** * Adds an event listener that listens on the specified events. * * @param string $eventName The event to listen on 'name:namespace' * @param callable $listener The listener * @param int $priority The higher this value, the earlier an event * listener will be triggered in the chain (defaults to 0) * @return Qs_Event_Dispatcher */ public function addListener($eventName, $listener, $priority = 0) { $this->_listeners[$eventName][$priority][] = $listener; unset($this->_sorted[$eventName]); return $this; } /** * Removes an event listener from the specified events. * * @param string|array $eventName The event(s) to remove a listener from * @param callable $listener The listener to remove * @return Qs_Event_Dispatcher */ public function removeListener($eventName, $listener) { if (!isset($this->_listeners[$eventName])) { return $this; } foreach ($this->_listeners[$eventName] as $priority => $listeners) { if (false !== ($key = array_search($listener, $listeners))) { unset($this->_listeners[$eventName][$priority][$key], $this->_sorted[$eventName]); } } return $this; } /** * Adds an event subscriber. * * The subscriber is asked for all the events he is * interested in and added as a listener for these events. * * @param Qs_Event_Subscriber_Interface $subscriber The subscriber. * @return Qs_Event_Dispatcher */ public function addSubscriber(Qs_Event_Subscriber_Interface $subscriber) { foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { if (is_string($params)) { $this->addListener($eventName, [$subscriber, $params]); } elseif (is_string($params[0])) { $this->addListener($eventName, [$subscriber, $params[0]], $params[1]); } else { foreach ($params as $listener) { $this->addListener($eventName, [$subscriber, $listener[0]], isset($listener[1]) ? $listener[1] : 0); } } } return $this; } /** * Removes an event subscriber. * * @param Qs_Event_Subscriber_Interface $subscriber The subscriber * @return Qs_Event_Dispatcher */ public function removeSubscriber(Qs_Event_Subscriber_Interface $subscriber) { foreach ($subscriber->getSubscribedEvents() as $eventName => $params) { if (is_array($params) && is_array($params[0])) { foreach ($params as $listener) { $this->removeListener($eventName, [$subscriber, $listener[0]]); } } else { $this->removeListener($eventName, [$subscriber, is_string($params) ? $params : $params[0]]); } } return $this; } /** * Triggers the listeners of an event. * * This method can be overridden to add functionality that is executed * for each listener. * * @param array $listeners The event listeners [callback, ...]. * @param string $eventName The name of the event to dispatch. * @param Qs_Event $event The event object to pass to the event handlers/listeners. * @return Qs_Event */ protected function _doDispatch($listeners, $eventName, Qs_Event $event) { foreach ($listeners as $listener) { call_user_func($listener, $event); if ($event->isPropagationStopped()) { break; } } return $event; } /** * Sorts the internal list of listeners for the given event by priority. * * @param string $eventName The name of the event. * @return Qs_Event_Dispatcher */ private function _sortListeners($eventName) { $this->_sorted[$eventName] = []; if (isset($this->_listeners[$eventName])) { krsort($this->_listeners[$eventName]); $this->_sorted[$eventName] = call_user_func_array('array_merge', $this->_listeners[$eventName]); } return $this; } }