This commit is contained in:
Xes
2025-08-14 22:41:49 +02:00
parent 2de81ccc46
commit 8ce45119b6
39774 changed files with 4309466 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
/**
* Abstract aggregate listener
*/
abstract class AbstractListenerAggregate implements ListenerAggregateInterface
{
/**
* @var callable[]
*/
protected $listeners = [];
/**
* {@inheritDoc}
*/
public function detach(EventManagerInterface $events)
{
foreach ($this->listeners as $index => $callback) {
$events->detach($callback);
unset($this->listeners[$index]);
}
}
}

View File

@@ -0,0 +1,201 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
use ArrayAccess;
/**
* Representation of an event
*
* Encapsulates the target context and parameters passed, and provides some
* behavior for interacting with the event manager.
*/
class Event implements EventInterface
{
/**
* @var string Event name
*/
protected $name;
/**
* @var string|object The event target
*/
protected $target;
/**
* @var array|ArrayAccess|object The event parameters
*/
protected $params = [];
/**
* @var bool Whether or not to stop propagation
*/
protected $stopPropagation = false;
/**
* Constructor
*
* Accept a target and its parameters.
*
* @param string $name Event name
* @param string|object $target
* @param array|ArrayAccess $params
*/
public function __construct($name = null, $target = null, $params = null)
{
if (null !== $name) {
$this->setName($name);
}
if (null !== $target) {
$this->setTarget($target);
}
if (null !== $params) {
$this->setParams($params);
}
}
/**
* Get event name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Get the event target
*
* This may be either an object, or the name of a static method.
*
* @return string|object
*/
public function getTarget()
{
return $this->target;
}
/**
* Set parameters
*
* Overwrites parameters
*
* @param array|ArrayAccess|object $params
* @throws Exception\InvalidArgumentException
*/
public function setParams($params)
{
if (! is_array($params) && ! is_object($params)) {
throw new Exception\InvalidArgumentException(
sprintf('Event parameters must be an array or object; received "%s"', gettype($params))
);
}
$this->params = $params;
}
/**
* Get all parameters
*
* @return array|object|ArrayAccess
*/
public function getParams()
{
return $this->params;
}
/**
* Get an individual parameter
*
* If the parameter does not exist, the $default value will be returned.
*
* @param string|int $name
* @param mixed $default
* @return mixed
*/
public function getParam($name, $default = null)
{
// Check in params that are arrays or implement array access
if (is_array($this->params) || $this->params instanceof ArrayAccess) {
if (! isset($this->params[$name])) {
return $default;
}
return $this->params[$name];
}
// Check in normal objects
if (! isset($this->params->{$name})) {
return $default;
}
return $this->params->{$name};
}
/**
* Set the event name
*
* @param string $name
*/
public function setName($name)
{
$this->name = (string) $name;
}
/**
* Set the event target/context
*
* @param null|string|object $target
*/
public function setTarget($target)
{
$this->target = $target;
}
/**
* Set an individual parameter to a value
*
* @param string|int $name
* @param mixed $value
*/
public function setParam($name, $value)
{
if (is_array($this->params) || $this->params instanceof ArrayAccess) {
// Arrays or objects implementing array access
$this->params[$name] = $value;
return;
}
// Objects
$this->params->{$name} = $value;
}
/**
* Stop further event propagation
*
* @param bool $flag
*/
public function stopPropagation($flag = true)
{
$this->stopPropagation = (bool) $flag;
}
/**
* Is propagation stopped?
*
* @return bool
*/
public function propagationIsStopped()
{
return $this->stopPropagation;
}
}

View File

@@ -0,0 +1,96 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
use ArrayAccess;
/**
* Representation of an event
*/
interface EventInterface
{
/**
* Get event name
*
* @return string
*/
public function getName();
/**
* Get target/context from which event was triggered
*
* @return null|string|object
*/
public function getTarget();
/**
* Get parameters passed to the event
*
* @return array|ArrayAccess
*/
public function getParams();
/**
* Get a single parameter by name
*
* @param string $name
* @param mixed $default Default value to return if parameter does not exist
* @return mixed
*/
public function getParam($name, $default = null);
/**
* Set the event name
*
* @param string $name
* @return void
*/
public function setName($name);
/**
* Set the event target/context
*
* @param null|string|object $target
* @return void
*/
public function setTarget($target);
/**
* Set event parameters
*
* @param array|ArrayAccess $params
* @return void
*/
public function setParams($params);
/**
* Set a single parameter by key
*
* @param string $name
* @param mixed $value
* @return void
*/
public function setParam($name, $value);
/**
* Indicate whether or not the parent EventManagerInterface should stop propagating events
*
* @param bool $flag
* @return void
*/
public function stopPropagation($flag = true);
/**
* Has this event indicated event propagation should stop?
*
* @return bool
*/
public function propagationIsStopped();
}

View File

@@ -0,0 +1,343 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
use ArrayObject;
/**
* Event manager: notification system
*
* Use the EventManager when you want to create a per-instance notification
* system for your objects.
*/
class EventManager implements EventManagerInterface
{
/**
* Subscribed events and their listeners
*
* STRUCTURE:
* [
* <string name> => [
* <int priority> => [
* 0 => [<callable listener>, ...]
* ],
* ...
* ],
* ...
* ]
*
* NOTE:
* This structure helps us to reuse the list of listeners
* instead of first iterating over it and generating a new one
* -> In result it improves performance by up to 25% even if it looks a bit strange
*
* @var array[]
*/
protected $events = [];
/**
* @var EventInterface Prototype to use when creating an event at trigger().
*/
protected $eventPrototype;
/**
* Identifiers, used to pull shared signals from SharedEventManagerInterface instance
*
* @var array
*/
protected $identifiers = [];
/**
* Shared event manager
*
* @var null|SharedEventManagerInterface
*/
protected $sharedManager = null;
/**
* Constructor
*
* Allows optionally specifying identifier(s) to use to pull signals from a
* SharedEventManagerInterface.
*
* @param SharedEventManagerInterface $sharedEventManager
* @param array $identifiers
*/
public function __construct(SharedEventManagerInterface $sharedEventManager = null, array $identifiers = [])
{
if ($sharedEventManager) {
$this->sharedManager = $sharedEventManager;
$this->setIdentifiers($identifiers);
}
$this->eventPrototype = new Event();
}
/**
* @inheritDoc
*/
public function setEventPrototype(EventInterface $prototype)
{
$this->eventPrototype = $prototype;
}
/**
* Retrieve the shared event manager, if composed.
*
* @return null|SharedEventManagerInterface $sharedEventManager
*/
public function getSharedManager()
{
return $this->sharedManager;
}
/**
* @inheritDoc
*/
public function getIdentifiers()
{
return $this->identifiers;
}
/**
* @inheritDoc
*/
public function setIdentifiers(array $identifiers)
{
$this->identifiers = array_unique($identifiers);
}
/**
* @inheritDoc
*/
public function addIdentifiers(array $identifiers)
{
$this->identifiers = array_unique(array_merge(
$this->identifiers,
$identifiers
));
}
/**
* @inheritDoc
*/
public function trigger($eventName, $target = null, $argv = [])
{
$event = clone $this->eventPrototype;
$event->setName($eventName);
if ($target !== null) {
$event->setTarget($target);
}
if ($argv) {
$event->setParams($argv);
}
return $this->triggerListeners($event);
}
/**
* @inheritDoc
*/
public function triggerUntil(callable $callback, $eventName, $target = null, $argv = [])
{
$event = clone $this->eventPrototype;
$event->setName($eventName);
if ($target !== null) {
$event->setTarget($target);
}
if ($argv) {
$event->setParams($argv);
}
return $this->triggerListeners($event, $callback);
}
/**
* @inheritDoc
*/
public function triggerEvent(EventInterface $event)
{
return $this->triggerListeners($event);
}
/**
* @inheritDoc
*/
public function triggerEventUntil(callable $callback, EventInterface $event)
{
return $this->triggerListeners($event, $callback);
}
/**
* @inheritDoc
*/
public function attach($eventName, callable $listener, $priority = 1)
{
if (! is_string($eventName)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a string for the event; received %s',
__METHOD__,
(is_object($eventName) ? get_class($eventName) : gettype($eventName))
));
}
$this->events[$eventName][(int) $priority][0][] = $listener;
return $listener;
}
/**
* @inheritDoc
* @throws Exception\InvalidArgumentException for invalid event types.
*/
public function detach(callable $listener, $eventName = null, $force = false)
{
// If event is wildcard, we need to iterate through each listeners
if (null === $eventName || ('*' === $eventName && ! $force)) {
foreach (array_keys($this->events) as $eventName) {
$this->detach($listener, $eventName, true);
}
return;
}
if (! is_string($eventName)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects a string for the event; received %s',
__METHOD__,
(is_object($eventName) ? get_class($eventName) : gettype($eventName))
));
}
if (! isset($this->events[$eventName])) {
return;
}
foreach ($this->events[$eventName] as $priority => $listeners) {
foreach ($listeners[0] as $index => $evaluatedListener) {
if ($evaluatedListener !== $listener) {
continue;
}
// Found the listener; remove it.
unset($this->events[$eventName][$priority][0][$index]);
// If the queue for the given priority is empty, remove it.
if (empty($this->events[$eventName][$priority][0])) {
unset($this->events[$eventName][$priority]);
break;
}
}
}
// If the queue for the given event is empty, remove it.
if (empty($this->events[$eventName])) {
unset($this->events[$eventName]);
}
}
/**
* @inheritDoc
*/
public function clearListeners($eventName)
{
if (isset($this->events[$eventName])) {
unset($this->events[$eventName]);
}
}
/**
* 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().
*
* @param array $args
* @return ArrayObject
*/
public function prepareArgs(array $args)
{
return new ArrayObject($args);
}
/**
* Trigger listeners
*
* Actual functionality for triggering listeners, to which trigger() delegate.
*
* @param EventInterface $event
* @param null|callable $callback
* @return ResponseCollection
*/
protected function triggerListeners(EventInterface $event, callable $callback = null)
{
$name = $event->getName();
if (empty($name)) {
throw new Exception\RuntimeException('Event is missing a name; cannot trigger!');
}
if (isset($this->events[$name])) {
$listOfListenersByPriority = $this->events[$name];
if (isset($this->events['*'])) {
foreach ($this->events['*'] as $priority => $listOfListeners) {
$listOfListenersByPriority[$priority][] = $listOfListeners[0];
}
}
} elseif (isset($this->events['*'])) {
$listOfListenersByPriority = $this->events['*'];
} else {
$listOfListenersByPriority = [];
}
if ($this->sharedManager) {
foreach ($this->sharedManager->getListeners($this->identifiers, $name) as $priority => $listeners) {
$listOfListenersByPriority[$priority][] = $listeners;
}
}
// Sort by priority in reverse order
krsort($listOfListenersByPriority);
// Initial value of stop propagation flag should be false
$event->stopPropagation(false);
// Execute listeners
$responses = new ResponseCollection();
foreach ($listOfListenersByPriority as $listOfListeners) {
foreach ($listOfListeners as $listeners) {
foreach ($listeners as $listener) {
$response = $listener($event);
$responses->push($response);
// If the event was asked to stop propagating, do so
if ($event->propagationIsStopped()) {
$responses->setStopped(true);
return $responses;
}
// If the result causes our validation callback to return true,
// stop propagation
if ($callback && $callback($response)) {
$responses->setStopped(true);
return $responses;
}
}
}
}
return $responses;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
/**
* Interface to automate setter injection for an EventManager instance
*/
interface EventManagerAwareInterface extends EventsCapableInterface
{
/**
* Inject an EventManager instance
*
* @param EventManagerInterface $eventManager
* @return void
*/
public function setEventManager(EventManagerInterface $eventManager);
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
use Traversable;
/**
* A trait for objects that provide events.
*
* If you use this trait in an object, you will probably want to also implement
* EventManagerAwareInterface, which will make it so the default initializer in
* a ZF2 MVC application will automatically inject an instance of the
* EventManager into your object when it is pulled from the ServiceManager.
*
* @see Zend\Mvc\Service\ServiceManagerConfig
*/
trait EventManagerAwareTrait
{
/**
* @var EventManagerInterface
*/
protected $events;
/**
* Set the event manager instance used by this context.
*
* For convenience, this method will also set the class name / LSB name as
* identifiers, in addition to any string or array of strings set to the
* $this->eventIdentifier property.
*
* @param EventManagerInterface $events
*/
public function setEventManager(EventManagerInterface $events)
{
$identifiers = [__CLASS__, get_class($this)];
if (isset($this->eventIdentifier)) {
if ((is_string($this->eventIdentifier))
|| (is_array($this->eventIdentifier))
|| ($this->eventIdentifier instanceof Traversable)
) {
$identifiers = array_unique(array_merge($identifiers, (array) $this->eventIdentifier));
} elseif (is_object($this->eventIdentifier)) {
$identifiers[] = $this->eventIdentifier;
}
// silently ignore invalid eventIdentifier types
}
$events->setIdentifiers($identifiers);
$this->events = $events;
if (method_exists($this, 'attachDefaultListeners')) {
$this->attachDefaultListeners();
}
}
/**
* Retrieve the event manager
*
* Lazy-loads an EventManager instance if none registered.
*
* @return EventManagerInterface
*/
public function getEventManager()
{
if (! $this->events instanceof EventManagerInterface) {
$this->setEventManager(new EventManager());
}
return $this->events;
}
}

View File

@@ -0,0 +1,162 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
/**
* Interface for messengers
*/
interface EventManagerInterface extends SharedEventsCapableInterface
{
/**
* Create and trigger an event.
*
* Use this method when you do not want to create an EventInterface
* instance prior to triggering. You will be required to pass:
*
* - the event name
* - the event target (can be null)
* - any event parameters you want to provide (empty array by default)
*
* It will create the Event instance for you and then trigger all listeners
* related to the event.
*
* @param string $eventName
* @param null|object|string $target
* @param array|object $argv
* @return ResponseCollection
*/
public function trigger($eventName, $target = null, $argv = []);
/**
* Create and trigger an event, applying a callback to each listener result.
*
* Use this method when you do not want to create an EventInterface
* instance prior to triggering. You will be required to pass:
*
* - the event name
* - the event target (can be null)
* - any event parameters you want to provide (empty array by default)
*
* It will create the Event instance for you, and trigger all listeners
* related to the event.
*
* The result of each listener is passed to $callback; if $callback returns
* a boolean true value, the manager must short-circuit listener execution.
*
* @param callable $callback
* @param string $eventName
* @param null|object|string $target
* @param array|object $argv
* @return ResponseCollection
*/
public function triggerUntil(callable $callback, $eventName, $target = null, $argv = []);
/**
* Trigger an event
*
* Provided an EventInterface instance, this method will trigger listeners
* based on the event name, raising an exception if the event name is missing.
*
* @param EventInterface $event
* @return ResponseCollection
*/
public function triggerEvent(EventInterface $event);
/**
* Trigger an event, applying a callback to each listener result.
*
* Provided an EventInterface instance, this method will trigger listeners
* based on the event name, raising an exception if the event name is missing.
*
* The result of each listener is passed to $callback; if $callback returns
* a boolean true value, the manager must short-circuit listener execution.
*
* @param callable $callback
* @param EventInterface $event
* @return ResponseCollection
*/
public function triggerEventUntil(callable $callback, EventInterface $event);
/**
* Attach a listener to an event
*
* The first argument is the event, and the next argument is a
* callable that will respond to that event.
*
* 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 *that has registered listeners at the time
* it is attached*. As such, register wildcard events last whenever possible!
*
* @param string $eventName Event to which to listen.
* @param callable $listener
* @param int $priority Priority at which to register listener.
* @return callable
*/
public function attach($eventName, callable $listener, $priority = 1);
/**
* Detach a listener.
*
* If no $event or '*' is provided, detaches listener from all events;
* otherwise, detaches only from the named event.
*
* @param callable $listener
* @param null|string $eventName Event from which to detach; null and '*'
* indicate all events.
* @return void
*/
public function detach(callable $listener, $eventName = null);
/**
* Clear all listeners for a given event
*
* @param string $eventName
* @return void
*/
public function clearListeners($eventName);
/**
* Provide an event prototype to use with trigger().
*
* When `trigger()` needs to create an event instance, it should clone the
* prototype provided to this method.
*
* @param EventInterface $prototype
* @return void
*/
public function setEventPrototype(EventInterface $prototype);
/**
* Get the identifier(s) for this EventManager
*
* @return array
*/
public function getIdentifiers();
/**
* Set the identifiers (overrides any currently set identifiers)
*
* @param string[] $identifiers
* @return void
*/
public function setIdentifiers(array $identifiers);
/**
* Add identifier(s) (appends to any currently set identifiers)
*
* @param string[] $identifiers
* @return void
*/
public function addIdentifiers(array $identifiers);
}

View File

@@ -0,0 +1,25 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
/**
* Interface indicating that an object composes an EventManagerInterface instance.
*/
interface EventsCapableInterface
{
/**
* Retrieve the event manager
*
* Lazy-loads an EventManager instance if none registered.
*
* @return EventManagerInterface
*/
public function getEventManager();
}

View File

@@ -0,0 +1,14 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager\Exception;
class DomainException extends \DomainException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager\Exception;
/**
* Base exception interface
*/
interface ExceptionInterface
{
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager\Exception;
/**
* Invalid argument exception
*/
class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,17 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager\Exception;
/**
* Invalid callback exception
*/
class InvalidCallbackException extends DomainException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,16 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager\Exception;
use RuntimeException as SplRuntimeException;
class RuntimeException extends SplRuntimeException implements ExceptionInterface
{
}

View File

@@ -0,0 +1,63 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager\Filter;
use Zend\EventManager\ResponseCollection;
/**
* Interface for intercepting filter chains
*/
interface FilterInterface
{
/**
* Execute the filter chain
*
* @param string|object $context
* @param array $params
* @return mixed
*/
public function run($context, array $params = []);
/**
* Attach an intercepting filter
*
* @param callable $callback
*/
public function attach(callable $callback);
/**
* Detach an intercepting filter
*
* @param callable $filter
* @return bool
*/
public function detach(callable $filter);
/**
* Get all intercepting filters
*
* @return array
*/
public function getFilters();
/**
* Clear all filters
*
* @return void
*/
public function clearFilters();
/**
* Get all filter responses
*
* @return ResponseCollection
*/
public function getResponses();
}

View File

@@ -0,0 +1,120 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager\Filter;
use Zend\EventManager\Exception;
use Zend\Stdlib\FastPriorityQueue;
/**
* Specialized priority queue implementation for use with an intercepting
* filter chain.
*
* Allows removal
*/
class FilterIterator extends FastPriorityQueue
{
/**
* Does the queue contain a given value?
*
* @param mixed $datum
* @return bool
*/
public function contains($datum)
{
foreach ($this as $item) {
if ($item === $datum) {
return true;
}
}
return false;
}
/**
* Insert a value into the queue.
*
* Requires a callable.
*
* @param callable $value
* @param mixed $priority
* @return void
* @throws Exception\InvalidArgumentException for non-callable $value.
*/
public function insert($value, $priority)
{
if (! is_callable($value)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s can only aggregate callables; received %s',
__CLASS__,
(is_object($value) ? get_class($value) : gettype($value))
));
}
parent::insert($value, $priority);
}
/**
* Remove a value from the queue
*
* This is an expensive operation. It must first iterate through all values,
* and then re-populate itself. Use only if absolutely necessary.
*
* @param mixed $datum
* @return bool
*/
public function remove($datum)
{
$this->setExtractFlags(self::EXTR_BOTH);
// Iterate and remove any matches
$removed = false;
$items = [];
$this->rewind();
while (! $this->isEmpty()) {
$item = $this->extract();
if ($item['data'] === $datum) {
$removed = true;
continue;
}
$items[] = $item;
}
// Repopulate
foreach ($items as $item) {
$this->insert($item['data'], $item['priority']);
}
$this->setExtractFlags(self::EXTR_DATA);
return $removed;
}
/**
* Iterate the next filter in the chain
*
* Iterates and calls the next filter in the chain.
*
* @param mixed $context
* @param array $params
* @param FilterIterator $chain
* @return mixed
*/
public function next($context = null, array $params = [], $chain = null)
{
if (empty($context) || ($chain instanceof FilterIterator && $chain->isEmpty())) {
return;
}
//We can't extract from an empty heap
if ($this->isEmpty()) {
return;
}
$next = $this->extract();
return $next($context, $params, $chain);
}
}

View File

@@ -0,0 +1,112 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
/**
* FilterChain: intercepting filter manager
*/
class FilterChain implements Filter\FilterInterface
{
/**
* @var Filter\FilterIterator All filters
*/
protected $filters;
/**
* Constructor
*
* Initializes Filter\FilterIterator in which filters will be aggregated
*/
public function __construct()
{
$this->filters = new Filter\FilterIterator();
}
/**
* Apply the filters
*
* Begins iteration of the filters.
*
* @param mixed $context Object under observation
* @param mixed $argv Associative array of arguments
* @return mixed
*/
public function run($context, array $argv = [])
{
$chain = clone $this->getFilters();
if ($chain->isEmpty()) {
return;
}
$next = $chain->extract();
return $next($context, $argv, $chain);
}
/**
* Connect a filter to the chain
*
* @param callable $callback PHP Callback
* @param int $priority Priority in the queue at which to execute;
* defaults to 1 (higher numbers == higher priority)
* @return CallbackHandler (to allow later unsubscribe)
* @throws Exception\InvalidCallbackException
*/
public function attach(callable $callback, $priority = 1)
{
$this->filters->insert($callback, $priority);
return $callback;
}
/**
* Detach a filter from the chain
*
* @param callable $filter
* @return bool Returns true if filter found and unsubscribed; returns false otherwise
*/
public function detach(callable $filter)
{
return $this->filters->remove($filter);
}
/**
* Retrieve all filters
*
* @return Filter\FilterIterator
*/
public function getFilters()
{
return $this->filters;
}
/**
* Clear all filters
*
* @return void
*/
public function clearFilters()
{
$this->filters = new Filter\FilterIterator();
}
/**
* Return current responses
*
* Only available while the chain is still being iterated. Returns the
* current ResponseCollection.
*
* @return null|ResponseCollection
*/
public function getResponses()
{
return;
}
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
use Interop\Container\ContainerInterface;
/**
* Lazy listener instance for use with LazyListenerAggregate.
*
* Used as an internal class for the LazyAggregate to allow lazy creation of
* listeners via a dependency injection container.
*
* Lazy event listener definitions add the following members to what the
* LazyListener accepts:
*
* - event: the event name to attach to.
* - priority: the priority at which to attach the listener, if not the default.
*/
class LazyEventListener extends LazyListener
{
/**
* @var string Event name to which to attach.
*/
private $event;
/**
* @var null|int Priority at which to attach.
*/
private $priority;
/**
* @param array $definition
* @param ContainerInterface $container
* @param array $env
*/
public function __construct(array $definition, ContainerInterface $container, array $env = [])
{
parent::__construct($definition, $container, $env);
if ((! isset($definition['event'])
|| ! is_string($definition['event'])
|| empty($definition['event']))
) {
throw new Exception\InvalidArgumentException(
'Lazy listener definition is missing a valid "event" member; cannot create LazyListener'
);
}
$this->event = $definition['event'];
$this->priority = isset($definition['priority']) ? (int) $definition['priority'] : null;
}
/**
* @return string
*/
public function getEvent()
{
return $this->event;
}
/**
* @return int
*/
public function getPriority($default = 1)
{
return (null !== $this->priority) ? $this->priority : $default;
}
}

View File

@@ -0,0 +1,122 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
use Interop\Container\ContainerInterface;
/**
* Lazy listener instance.
*
* Used to allow lazy creation of listeners via a dependency injection
* container.
*
* Lazy listener definitions have the following members:
*
* - listener: the service name of the listener to use.
* - method: the method name of the listener to invoke for the specified event.
*
* If desired, you can pass $env at instantiation; this will be passed to the
* container's `build()` method, if it has one, when creating the listener
* instance.
*
* Pass instances directly to the event manager's `attach()` method as the
* listener argument.
*/
class LazyListener
{
/**
* @var ContainerInterface Container from which to pull listener.
*/
private $container;
/**
* @var array Variables/options to use during service creation, if any.
*/
private $env;
/**
* @var callable Marshaled listener callback.
*/
private $listener;
/**
* @var string Method name to invoke on listener.
*/
private $method;
/**
* @var string Service name of listener.
*/
private $service;
/**
* @param array $definition
* @param ContainerInterface $container
* @param array $env
*/
public function __construct(array $definition, ContainerInterface $container, array $env = [])
{
if ((! isset($definition['listener'])
|| ! is_string($definition['listener'])
|| empty($definition['listener']))
) {
throw new Exception\InvalidArgumentException(
'Lazy listener definition is missing a valid "listener" member; cannot create LazyListener'
);
}
if ((! isset($definition['method'])
|| ! is_string($definition['method'])
|| empty($definition['method']))
) {
throw new Exception\InvalidArgumentException(
'Lazy listener definition is missing a valid "method" member; cannot create LazyListener'
);
}
$this->service = $definition['listener'];
$this->method = $definition['method'];
$this->container = $container;
$this->env = $env;
}
/**
* Use the listener as an invokable, allowing direct attachment to an event manager.
*
* @param EventInterface $event
* @return callable
*/
public function __invoke(EventInterface $event)
{
$listener = $this->fetchListener();
$method = $this->method;
return $listener->{$method}($event);
}
/**
* @return callable
*/
private function fetchListener()
{
if ($this->listener) {
return $this->listener;
}
// In the future, typehint against Zend\ServiceManager\ServiceLocatorInterface,
// which defines this message starting in v3.
if (method_exists($this->container, 'build') && ! empty($this->env)) {
$this->listener = $this->container->build($this->service, $this->env);
return $this->listener;
}
$this->listener = $this->container->get($this->service);
return $this->listener;
}
}

View File

@@ -0,0 +1,110 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
use Interop\Container\ContainerInterface;
/**
* Aggregate listener for attaching lazy listeners.
*
* Lazy listeners are listeners where creation is deferred until they are
* triggered; this removes the most costly mechanism of pulling a listener
* from a container unless the listener is actually invoked.
*
* Usage is:
*
* <code>
* $events->attachAggregate(new LazyListenerAggregate(
* $lazyEventListenersOrDefinitions,
* $container
* ));
* </code>
*/
class LazyListenerAggregate implements ListenerAggregateInterface
{
use ListenerAggregateTrait;
/**
* @var ContainerInterface Container from which to pull lazy listeners.
*/
private $container;
/**
* @var array Additional environment/option variables to use when creating listener.
*/
private $env;
/**
* Generated LazyEventListener instances.
*
* @var LazyEventListener[]
*/
private $lazyListeners = [];
/**
* Constructor
*
* Accepts the composed $listeners, as well as the $container and $env in
* order to create a listener aggregate that defers listener creation until
* the listener is triggered.
*
* Listeners may be either LazyEventListener instances, or lazy event
* listener definitions that can be provided to a LazyEventListener
* constructor in order to create a new instance; in the latter case, the
* $container and $env will be passed at instantiation as well.
*
* @var array $listeners LazyEventListener instances or array definitions
* to pass to the LazyEventListener constructor.
* @var ContainerInterface $container
* @var array $env
* @throws Exception\InvalidArgumentException for invalid listener items.
*/
public function __construct(array $listeners, ContainerInterface $container, array $env = [])
{
$this->container = $container;
$this->env = $env;
// This would raise an exception for invalid structs
foreach ($listeners as $listener) {
if (is_array($listener)) {
$listener = new LazyEventListener($listener, $container, $env);
}
if (! $listener instanceof LazyEventListener) {
throw new Exception\InvalidArgumentException(sprintf(
'All listeners must be LazyEventListener instances or definitions; received %s',
(is_object($listener) ? get_class($listener) : gettype($listener))
));
}
$this->lazyListeners[] = $listener;
}
}
/**
* Attach the aggregate to the event manager.
*
* Loops through all composed lazy listeners, and attaches them to the
* event manager.
*
* @var EventManagerInterface $events
* @var int $priority
*/
public function attach(EventManagerInterface $events, $priority = 1)
{
foreach ($this->lazyListeners as $lazyListener) {
$this->listeners[] = $events->attach(
$lazyListener->getEvent(),
$lazyListener,
$lazyListener->getPriority($priority)
);
}
}
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
/**
* Interface for self-registering event listeners.
*
* Classes implementing this interface may be registered by name or instance
* with an EventManager, without an event name. The {@link attach()} method will
* then be called with the current EventManager instance, allowing the class to
* wire up one or more listeners.
*/
interface ListenerAggregateInterface
{
/**
* Attach one or more listeners
*
* Implementors may add an optional $priority argument; the EventManager
* implementation will pass this to the aggregate.
*
* @param EventManagerInterface $events
* @param int $priority
* @return void
*/
public function attach(EventManagerInterface $events, $priority = 1);
/**
* Detach all previously attached listeners
*
* @param EventManagerInterface $events
* @return void
*/
public function detach(EventManagerInterface $events);
}

View File

@@ -0,0 +1,33 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
/**
* Provides logic to easily create aggregate listeners, without worrying about
* manually detaching events
*/
trait ListenerAggregateTrait
{
/**
* @var callable[]
*/
protected $listeners = [];
/**
* {@inheritDoc}
*/
public function detach(EventManagerInterface $events)
{
foreach ($this->listeners as $index => $callback) {
$events->detach($callback);
unset($this->listeners[$index]);
}
}
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
use SplStack;
/**
* Collection of signal handler return values
*/
class ResponseCollection extends SplStack
{
protected $stopped = false;
/**
* Did the last response provided trigger a short circuit of the stack?
*
* @return bool
*/
public function stopped()
{
return $this->stopped;
}
/**
* Mark the collection as stopped (or its opposite)
*
* @param bool $flag
*/
public function setStopped($flag)
{
$this->stopped = (bool) $flag;
}
/**
* Convenient access to the first handler return value.
*
* @return mixed The first handler return value
*/
public function first()
{
return parent::bottom();
}
/**
* Convenient access to the last handler return value.
*
* If the collection is empty, returns null. Otherwise, returns value
* returned by last handler.
*
* @return mixed The last handler return value
*/
public function last()
{
if (count($this) === 0) {
return;
}
return parent::top();
}
/**
* Check if any of the responses match the given value.
*
* @param mixed $value The value to look for among responses
* @return bool
*/
public function contains($value)
{
foreach ($this as $response) {
if ($response === $value) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,235 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
/**
* Shared/contextual EventManager
*
* Allows attaching to EMs composed by other classes without having an instance first.
* The assumption is that the SharedEventManager will be injected into EventManager
* instances, and then queried for additional listeners when triggering an event.
*/
class SharedEventManager implements SharedEventManagerInterface
{
/**
* Identifiers with event connections
* @var array
*/
protected $identifiers = [];
/**
* Attach a listener to an event emitted by components with specific identifiers.
*
* Allows attaching a listener to an event offered by an identifying
* components. As an example, the following connects to the "getAll" event
* of both an AbstractResource and EntityResource:
*
* <code>
* $sharedEventManager = new SharedEventManager();
* foreach (['My\Resource\AbstractResource', 'My\Resource\EntityResource'] as $identifier) {
* $sharedEventManager->attach(
* $identifier,
* 'getAll',
* function ($e) use ($cache) {
* if (!$id = $e->getParam('id', false)) {
* return;
* }
* if (!$data = $cache->load(get_class($resource) . '::getOne::' . $id )) {
* return;
* }
* return $data;
* }
* );
* }
* </code>
*
* @param string $identifier Identifier for event emitting component.
* @param string $event
* @param callable $listener Listener that will handle the event.
* @param int $priority Priority at which listener should execute
* @return void
* @throws Exception\InvalidArgumentException for invalid identifier arguments.
* @throws Exception\InvalidArgumentException for invalid event arguments.
*/
public function attach($identifier, $event, callable $listener, $priority = 1)
{
if (! is_string($identifier) || empty($identifier)) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid identifier provided; must be a string; received "%s"',
(is_object($identifier) ? get_class($identifier) : gettype($identifier))
));
}
if (! is_string($event) || empty($event)) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid event provided; must be a non-empty string; received "%s"',
(is_object($event) ? get_class($event) : gettype($event))
));
}
$this->identifiers[$identifier][$event][(int) $priority][] = $listener;
}
/**
* @inheritDoc
*/
public function detach(callable $listener, $identifier = null, $eventName = null, $force = false)
{
// No identifier or wildcard identifier: loop through all identifiers and detach
if (null === $identifier || ('*' === $identifier && ! $force)) {
foreach (array_keys($this->identifiers) as $identifier) {
$this->detach($listener, $identifier, $eventName, true);
}
return;
}
if (! is_string($identifier) || empty($identifier)) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid identifier provided; must be a string, received %s',
(is_object($identifier) ? get_class($identifier) : gettype($identifier))
));
}
// Do we have any listeners on the provided identifier?
if (! isset($this->identifiers[$identifier])) {
return;
}
if (null === $eventName || ('*' === $eventName && ! $force)) {
foreach (array_keys($this->identifiers[$identifier]) as $eventName) {
$this->detach($listener, $identifier, $eventName, true);
}
return;
}
if (! is_string($eventName) || empty($eventName)) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid event name provided; must be a string, received %s',
(is_object($eventName) ? get_class($eventName) : gettype($eventName))
));
}
if (! isset($this->identifiers[$identifier][$eventName])) {
return;
}
foreach ($this->identifiers[$identifier][$eventName] as $priority => $listeners) {
foreach ($listeners as $index => $evaluatedListener) {
if ($evaluatedListener !== $listener) {
continue;
}
// Found the listener; remove it.
unset($this->identifiers[$identifier][$eventName][$priority][$index]);
// Is the priority queue empty?
if (empty($this->identifiers[$identifier][$eventName][$priority])) {
unset($this->identifiers[$identifier][$eventName][$priority]);
break;
}
}
// Is the event queue empty?
if (empty($this->identifiers[$identifier][$eventName])) {
unset($this->identifiers[$identifier][$eventName]);
break;
}
}
// Is the identifier queue now empty? Remove it.
if (empty($this->identifiers[$identifier])) {
unset($this->identifiers[$identifier]);
}
}
/**
* Retrieve all listeners for a given identifier and event
*
* @param string[] $identifiers
* @param string $eventName
* @return array[]
* @throws Exception\InvalidArgumentException
*/
public function getListeners(array $identifiers, $eventName)
{
if ('*' === $eventName || ! is_string($eventName) || empty($eventName)) {
throw new Exception\InvalidArgumentException(sprintf(
'Event name passed to %s must be a non-empty, non-wildcard string',
__METHOD__
));
}
$returnListeners = [];
foreach ($identifiers as $identifier) {
if ('*' === $identifier || ! is_string($identifier) || empty($identifier)) {
throw new Exception\InvalidArgumentException(sprintf(
'Identifier names passed to %s must be non-empty, non-wildcard strings',
__METHOD__
));
}
if (isset($this->identifiers[$identifier])) {
$listenersByIdentifier = $this->identifiers[$identifier];
if (isset($listenersByIdentifier[$eventName])) {
foreach ($listenersByIdentifier[$eventName] as $priority => $listeners) {
$returnListeners[$priority][] = $listeners;
}
}
if (isset($listenersByIdentifier['*'])) {
foreach ($listenersByIdentifier['*'] as $priority => $listeners) {
$returnListeners[$priority][] = $listeners;
}
}
}
}
if (isset($this->identifiers['*'])) {
$wildcardIdentifier = $this->identifiers['*'];
if (isset($wildcardIdentifier[$eventName])) {
foreach ($wildcardIdentifier[$eventName] as $priority => $listeners) {
$returnListeners[$priority][] = $listeners;
}
}
if (isset($wildcardIdentifier['*'])) {
foreach ($wildcardIdentifier['*'] as $priority => $listeners) {
$returnListeners[$priority][] = $listeners;
}
}
}
foreach ($returnListeners as $priority => $listOfListeners) {
$returnListeners[$priority] = array_merge(...$listOfListeners);
}
return $returnListeners;
}
/**
* @inheritDoc
*/
public function clearListeners($identifier, $eventName = null)
{
if (! isset($this->identifiers[$identifier])) {
return false;
}
if (null === $eventName) {
unset($this->identifiers[$identifier]);
return;
}
if (! isset($this->identifiers[$identifier][$eventName])) {
return;
}
unset($this->identifiers[$identifier][$eventName]);
}
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
/**
* Interface for shared event listener collections
*/
interface SharedEventManagerInterface
{
/**
* Attach a listener to an event emitted by components with specific identifiers.
*
* @param string $identifier Identifier for event emitting component
* @param string $eventName
* @param callable $listener Listener that will handle the event.
* @param int $priority Priority at which listener should execute
*/
public function attach($identifier, $eventName, callable $listener, $priority = 1);
/**
* Detach a shared listener.
*
* Allows detaching a listener from one or more events to which it may be
* attached.
*
* @param callable $listener Listener to detach.
* @param null|string $identifier Identifier from which to detach; null indicates
* all registered identifiers.
* @param null|string $eventName Event from which to detach; null indicates
* all registered events.
* @throws Exception\InvalidArgumentException for invalid identifier arguments.
* @throws Exception\InvalidArgumentException for invalid event arguments.
*/
public function detach(callable $listener, $identifier = null, $eventName = null);
/**
* Retrieve all listeners for given identifiers
*
* @param array $identifiers
* @param string $eventName
* @return array
*/
public function getListeners(array $identifiers, $eventName);
/**
* Clear all listeners for a given identifier, optionally for a specific event
*
* @param string $identifier
* @param null|string $eventName
*/
public function clearListeners($identifier, $eventName = null);
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
/**
* Interface indicating that an object composes or can compose a
* SharedEventManagerInterface instance.
*/
interface SharedEventsCapableInterface
{
/**
* Retrieve the shared event manager, if composed.
*
* @return null|SharedEventManagerInterface
*/
public function getSharedManager();
}

View File

@@ -0,0 +1,152 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zend-eventmanager for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager\Test;
use PHPUnit\Framework\Assert;
use ReflectionProperty;
use Zend\EventManager\EventManager;
/**
* Trait providing utility methods and assertions for use in PHPUnit test cases.
*
* This trait may be composed into a test case, and provides:
*
* - methods for introspecting events and listeners
* - methods for asserting listeners are attached at a specific priority
*
* Some functionality in this trait duplicates functionality present in the
* version 2 EventManagerInterface and/or EventManager implementation, but
* abstracts that functionality for use in v3. As such, components or code
* that is testing for listener registration should use the methods in this
* trait to ensure tests are forwards-compatible between zend-eventmanager
* versions.
*/
trait EventListenerIntrospectionTrait
{
/**
* Retrieve a list of event names from an event manager.
*
* @param EventManager $events
* @return string[]
*/
private function getEventsFromEventManager(EventManager $events)
{
$r = new ReflectionProperty($events, 'events');
$r->setAccessible(true);
$listeners = $r->getValue($events);
return array_keys($listeners);
}
/**
* Retrieve an interable list of listeners for an event.
*
* Given an event and an event manager, returns an iterator with the
* listeners for that event, in priority order.
*
* If $withPriority is true, the key values will be the priority at which
* the given listener is attached.
*
* Do not pass $withPriority if you want to cast the iterator to an array,
* as many listeners will likely have the same priority, and thus casting
* will collapse to the last added.
*
* @param string $event
* @param EventManager $events
* @param bool $withPriority
* @return \Traversable
*/
private function getListenersForEvent($event, EventManager $events, $withPriority = false)
{
$r = new ReflectionProperty($events, 'events');
$r->setAccessible(true);
$internal = $r->getValue($events);
$listeners = [];
foreach (isset($internal[$event]) ? $internal[$event] : [] as $p => $listOfListeners) {
foreach ($listOfListeners as $l) {
$listeners[$p] = isset($listeners[$p]) ? array_merge($listeners[$p], $l) : $l;
}
}
return $this->traverseListeners($listeners, $withPriority);
}
/**
* Assert that a given listener exists at the specified priority.
*
* @param callable $expectedListener
* @param int $expectedPriority
* @param string $event
* @param EventManager $events
* @param string $message Failure message to use, if any.
*/
private function assertListenerAtPriority(
callable $expectedListener,
$expectedPriority,
$event,
EventManager $events,
$message = ''
) {
$message = $message ?: sprintf(
'Listener not found for event "%s" and priority %d',
$event,
$expectedPriority
);
$listeners = $this->getListenersForEvent($event, $events, true);
$found = false;
foreach ($listeners as $priority => $listener) {
if ($listener === $expectedListener
&& $priority === $expectedPriority
) {
$found = true;
break;
}
}
Assert::assertTrue($found, $message);
}
/**
* Returns an indexed array of listeners for an event.
*
* Returns an indexed array of listeners for an event, in priority order.
* Priority values will not be included; use this only for testing if
* specific listeners are present, or for a count of listeners.
*
* @param string $event
* @param EventManager $events
* @return callable[]
*/
private function getArrayOfListenersForEvent($event, EventManager $events)
{
return iterator_to_array($this->getListenersForEvent($event, $events));
}
/**
* Generator for traversing listeners in priority order.
*
* @param array $listeners
* @param bool $withPriority When true, yields priority as key.
*/
public function traverseListeners(array $queue, $withPriority = false)
{
krsort($queue, SORT_NUMERIC);
foreach ($queue as $priority => $listeners) {
$priority = (int) $priority;
foreach ($listeners as $listener) {
if ($withPriority) {
yield $priority => $listener;
} else {
yield $listener;
}
}
}
}
}