Augmentation vers version 3.3.0

This commit is contained in:
Gauvain Boiché
2020-03-31 15:31:03 +02:00
parent d926806907
commit a1864c0414
2618 changed files with 406015 additions and 31377 deletions

View File

@@ -0,0 +1,279 @@
# Changelog
All notable changes to this project will be documented in this file, in reverse chronological order by release.
## 3.2.1 - 2018-04-25
### Added
- [#66](https://github.com/zendframework/zend-eventmanager/pull/66) adds support for PHP 7.2.
### Changed
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- Nothing.
## 3.2.0 - 2017-07-11
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- [#47](https://github.com/zendframework/zend-eventmanager/pull/47) removes
support for PHP 5.5 and HHVM.
### Fixed
- Nothing.
## 3.1.0 - 2016-12-19
### Added
- [#26](https://github.com/zendframework/zend-eventmanager/pull/26) publishes
the documentation to https://zendframework.github.io/zend-eventmanager/
### Changes
- [#17](https://github.com/zendframework/zend-eventmanager/pull/17) makes a
number of internal changes to how listeners are stored in order to improve
performance, by as much as 10% in the scenario used in the MVC layer.
Additionally, it optimizes when the target and event arguments are injected
into an event, eliminating that step entirely when either is unavailable.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- Nothing.
## 3.0.1 - 2016-02-18
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#24](https://github.com/zendframework/zend-eventmanager/pull/24) updates the
zend-stdlib dependency to `^2.7.3 || ^3.0`, allowing either major version.
## 3.0.0 - 2016-01-12
### Added
- [Migration documentation](doc/book/migration/) was added.
- [Automated benchmarks](benchmarks/) were added.
- `EventManager::__construct()` now accepts an optional
`SharedEventManagerInterface` instance as the first argument, and an optional
array of identifiers as the second. As identifiers have no meaning without a
shared manager present, they are secondary to providing the shared manager.
- `EventManagerInterface::trigger()` changes its signature to
`trigger($eventName, $target = null, $argv = [])`; each argument has exactly
one possible meaning; the `$eventName` can only be a string event name. The
fourth `$callback` argument is removed.
- `EventManagerInterface::triggerUntil()` changes its signature to
`triggerUntil(callable $callback, $eventName, $target = null, $argv = null)`.
Each argument has exactly one meaning.
- `EventManagerInterface` adds two new methods for triggering provided
`EventInterface` arguments: `triggerEvent(EventInterface $event)` and
`triggerEventUntil(callable $callback, EventInterface $event)`.
- `EventManagerInterface::attach()` and `detach()` change their signatures to
`attach($eventName, callable $listener, $priority = 1)` and `detach(callable
$listener, $eventName = null)`, respectively. Note that `$eventName` can now
only be a string event name, not an array or `Traversable`.
- `EventManagerInterface::setIdentifiers()` and `addIdentifiers()` change their
signatures to each only accept an *array* of identifiers.
- `SharedEventManagerInterface::getListeners()` changes signature to
`getListeners(array $identifiers, $eventName)` and now guarantees return of an
array. Note that the second argument is now *required*.
- `SharedEventManagerInterface::attach()` changes signature to
`attach($identifier, $eventName, callable $listener, $priority = 1)`. The
`$identifier` and `$eventName` **must** be strings.
- `SharedEventManagerInterface::detach()` changes signature to `detach(callable
$listener, $identifier = null, $eventName = null)`; `$identifier` and
`$eventName` **must** be strings if passed.
- `ListenerAggregateInterface::attach()` adds an optional `$priority = 1`
argument. This was used already in v2, but not dictated by the interface.
- `FilterInterface::attach()` and `detach()` have changed signature to
`attach(callable $callback)` and `detach(callable $ilter)`, respectively.
- `LazyListener` allows wrapping:
- fetching a listener service from a container-interop container, and
- invoking a designated listener method with the provided event.
- `LazyEventListener` extends `LazyListener`, and provides metadata for
discovering the intended event name and priority at which to attach the lazy
listener; these are consumed by:
- `LazyListenerAggregate`, which, provided a list of `LazyEventListeners` and/or
definitions to use to create them, acts as an aggregate for attaching a number
of such listeners at once.
- [#20](https://github.com/zendframework/zend-eventmanager/pull/20) updates the
trait `Zend\EventManager\Test\EventListenerIntrospectionTrait` so that the
implementation will work with the v3 changes; the tests written for v2
continue to pass, allowing this trait to be used to provide compatibility
testing between v2 and v3.
### Deprecated
- Nothing.
### Removed
- `GlobalEventManager` and `StaticEventManager` are removed (with prejudice!).
- `ProvidesEvents`, which was previously deprecated, is removed.
- `EventManagerInterface::setSharedManager()` is removed. Shared managers are
now expected to be injected during instantiation.
- `EventManagerInterface::getEvents()` and `getListeners()` are removed; they
had now purpose within the implementation.
- `EventManagerInterface::setEventClass()` was renamed to `setEventPrototype()`,
which now expects an `EventInterface` instance. That instance will be cloned
whenever a new event is created.
- `EventManagerInterface::attachAggregate()` and `detachAggregate()` are
removed. Users should use the `attach()` and `detach()` methods of the
aggregates themselves.
- `SharedEventAggregateAwareInterface` and `SharedListenerAggregateInterface`
are removed. This was an undocumented and largely unused feature.
- `SharedEventManagerAwareInterface` is removed. A new interface,
`SharedEventsCapableInterface` defines the `getSharedManager()` method from
the interface, and `EventManagerInterface` extends that new interface.
- `SharedEventManagerInterface::getEvents()` is removed, as it had no purpose in
the implementation.
- `ResponseCollection::setStopped()` no longer implements a fluent interface.
### Fixed
- `FilterIterator::insert()` has been modified to raise an exception if the value provided is not a callable.
## 2.6.2 - 2016-01-12
### Added
- [#19](https://github.com/zendframework/zend-eventmanager/pull/19) adds a new
trait, `Zend\EventManager\Test\EventListenerIntrospectionTrait`, intended for
composition in unit tests. It provides a number of methods that can be used
to retrieve listeners with or without associated priority, and the assertion
`assertListenerAtPriority(callable $listener, $priority, $event, EventManager $events, $message = '')`,
which can be used for testing that a listener was registered at the specified
priority with the specified event.
The features in this patch are intended to facilitate testing against both
version 2 and version 3 of zend-eventmanager, as it provides a consistent API
for retrieving lists of events and listeners between the two versions.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- Nothing.
## 2.6.0 - 2015-09-29
### Added
- Added `Zend\EventManager\SharedEventsCapableInterface`. This interface will
largely replace `Zend\EventManager\SharedEventManagerAwareInterface` in
version 3, and the latter was updated to extend it.
- Added `EventManager::triggerEvent(EventInterface $event)` as a
forwards-compatibility feature.
- Add `EventManager::triggerEventUntil(callable $callback, EventIterface $event)`
as a forwards-compatibility feature.
- Adds [Athletic](https://github.com/polyfractal/athletic) benchmarks to aid in
gauging performanc impact of changes; these are a development change only.
### Deprecated
- Marked `GlobalEventManager` as deprecated; this class will be removed in
version 3.
- Marked `StaticEventManager` as deprecated; this class will be removed in
version 3.
- Marked `SharedListenerAggregateInterface` as deprecated; this interface will
be removed in version 3.
- Marked `SharedEventAggregateAwareInterface` as deprecated; this interface will
be removed in version 3.
- Marked `SharedEventManagerAwareInterface` as deprecated; this interface will
be removed in version 3.
- Marked `EventManager::setSharedManager()` as deprecated; this method will be
removed in version 3.
- Marked `EventManager::unsetSharedManager()` as deprecated; this method will be
removed in version 3.
- Marked `EventManagerInterface::` and `EventManager::getEvents()` as
deprecated; this method will be removed in version 3.
- Marked `EventManagerInterface::` and `EventManager::getListeners()` as
deprecated; this method will be removed in version 3.
- Marked `EventManagerInterface::` and `Eventmanager::setEventClass()` as
deprecated; this method is renamed to `setEventPrototype(EventInterface $event)`
in version 3.
- Marked `EventManagerInterface::` and `EventManager::attachAggregate()` as
deprecated; this method will be removed in version 3.
- Marked `EventManagerInterface::` and `EventManager::detachAggregate()` as
deprecated; this method will be removed in version 3.
- Marked `SharedEventManagerInterface::` and `SharedEventManager::getEvents()`
as deprecated; this method will be removed in version 3.
### Removed
- Nothing.
### Fixed
- Nothing.
## 2.5.2 - 2015-07-16
### Added
- [#5](https://github.com/zendframework/zend-eventmanager/pull/5) adds a number
of unit tests to improve test coverage, and thus maintainability and
stability.
### Deprecated
- Nothing.
### Removed
- [#3](https://github.com/zendframework/zend-eventmanager/pull/3) removes some
PHP 5.3- and 5.4-isms (such as marking Traits as requiring 5.4, and closing
over a copy of `$this`) from the test suite.
### Fixed
- [#5](https://github.com/zendframework/zend-eventmanager/pull/5) fixes a bug in
`FilterIterator` that occurs when attempting to extract from an empty heap.

View File

@@ -0,0 +1,43 @@
# Contributor Code of Conduct
The Zend Framework project adheres to [The Code Manifesto](http://codemanifesto.com)
as its guidelines for contributor interactions.
## The Code Manifesto
We want to work in an ecosystem that empowers developers to reach their
potential — one that encourages growth and effective collaboration. A space that
is safe for all.
A space such as this benefits everyone that participates in it. It encourages
new developers to enter our field. It is through discussion and collaboration
that we grow, and through growth that we improve.
In the effort to create such a place, we hold to these values:
1. **Discrimination limits us.** This includes discrimination on the basis of
race, gender, sexual orientation, gender identity, age, nationality, technology
and any other arbitrary exclusion of a group of people.
2. **Boundaries honor us.** Your comfort levels are not everyones comfort
levels. Remember that, and if brought to your attention, heed it.
3. **We are our biggest assets.** None of us were born masters of our trade.
Each of us has been helped along the way. Return that favor, when and where
you can.
4. **We are resources for the future.** As an extension of #3, share what you
know. Make yourself a resource to help those that come after you.
5. **Respect defines us.** Treat others as you wish to be treated. Make your
discussions, criticisms and debates from a position of respectfulness. Ask
yourself, is it true? Is it necessary? Is it constructive? Anything less is
unacceptable.
6. **Reactions require grace.** Angry responses are valid, but abusive language
and vindictive actions are toxic. When something happens that offends you,
handle it assertively, but be respectful. Escalate reasonably, and try to
allow the offender an opportunity to explain themselves, and possibly correct
the issue.
7. **Opinions are just that: opinions.** Each and every one of us, due to our
background and upbringing, have varying opinions. The fact of the matter, is
that is perfectly acceptable. Remember this: if you respect your own
opinions, you should respect the opinions of others.
8. **To err is human.** You might not intend it, but mistakes do happen and
contribute to build experience. Tolerate honest mistakes, and don't hesitate
to apologize if you make one yourself.

View File

@@ -1,36 +1,58 @@
{
"name": "zendframework/zend-eventmanager",
"description": " ",
"description": "Trigger and listen to events within a PHP application",
"license": "BSD-3-Clause",
"keywords": [
"zf2",
"event",
"events",
"eventmanager"
],
"homepage": "https://github.com/zendframework/zend-eventmanager",
"minimum-stability": "dev",
"prefer-stable": true,
"extra": {
"branch-alias": {
"dev-master": "3.2-dev",
"dev-develop": "3.3-dev"
}
},
"autoload": {
"psr-4": {
"Zend\\EventManager\\": "src/"
}
},
"require": {
"php": ">=5.3.23",
"zendframework/zend-stdlib": "~2.5"
},
"minimum-stability": "dev",
"prefer-stable": true,
"extra": {
"branch-alias": {
"dev-master": "2.5-dev",
"dev-develop": "2.6-dev"
}
},
"autoload-dev": {
"psr-4": {
"ZendTest\\EventManager\\": "test/"
}
"ZendTest\\EventManager\\": "test/",
"ZendBench\\EventManager\\": "benchmarks/"
},
"files": [
"test/_autoload.php"
]
},
"require": {
"php": "^5.6 || ^7.0"
},
"require-dev": {
"fabpot/php-cs-fixer": "1.7.*",
"phpunit/PHPUnit": "~4.0"
"phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2",
"athletic/athletic": "^0.1",
"zendframework/zend-stdlib": "^2.7.3 || ^3.0",
"container-interop/container-interop": "^1.1.0",
"zendframework/zend-coding-standard": "~1.0.0"
},
"suggest": {
"container-interop/container-interop": "^1.1.0, to use the lazy listeners feature",
"zendframework/zend-stdlib": "^2.7.3 || ^3.0, to use the FilterChain feature"
},
"scripts": {
"check": [
"@cs-check",
"@test"
],
"cs-check": "phpcs",
"cs-fix": "phpcbf",
"test": "phpunit --colors=always",
"test-coverage": "phpunit --colors=always --coverage-clover clover.xml"
}
}

1937
vendor/zendframework/zend-eventmanager/composer.lock generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,9 +2,9 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
@@ -15,9 +15,9 @@ namespace Zend\EventManager;
abstract class AbstractListenerAggregate implements ListenerAggregateInterface
{
/**
* @var \Zend\Stdlib\CallbackHandler[]
* @var callable[]
*/
protected $listeners = array();
protected $listeners = [];
/**
* {@inheritDoc}
@@ -25,9 +25,8 @@ abstract class AbstractListenerAggregate implements ListenerAggregateInterface
public function detach(EventManagerInterface $events)
{
foreach ($this->listeners as $index => $callback) {
if ($events->detach($callback)) {
unset($this->listeners[$index]);
}
$events->detach($callback);
unset($this->listeners[$index]);
}
}
}

View File

@@ -2,9 +2,9 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
@@ -32,7 +32,7 @@ class Event implements EventInterface
/**
* @var array|ArrayAccess|object The event parameters
*/
protected $params = array();
protected $params = [];
/**
* @var bool Whether or not to stop propagation
@@ -91,19 +91,17 @@ class Event implements EventInterface
* Overwrites parameters
*
* @param array|ArrayAccess|object $params
* @return Event
* @throws Exception\InvalidArgumentException
*/
public function setParams($params)
{
if (!is_array($params) && !is_object($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;
return $this;
}
/**
@@ -129,7 +127,7 @@ class Event implements EventInterface
{
// Check in params that are arrays or implement array access
if (is_array($this->params) || $this->params instanceof ArrayAccess) {
if (!isset($this->params[$name])) {
if (! isset($this->params[$name])) {
return $default;
}
@@ -137,7 +135,7 @@ class Event implements EventInterface
}
// Check in normal objects
if (!isset($this->params->{$name})) {
if (! isset($this->params->{$name})) {
return $default;
}
return $this->params->{$name};
@@ -147,24 +145,20 @@ class Event implements EventInterface
* Set the event name
*
* @param string $name
* @return Event
*/
public function setName($name)
{
$this->name = (string) $name;
return $this;
}
/**
* Set the event target/context
*
* @param null|string|object $target
* @return Event
*/
public function setTarget($target)
{
$this->target = $target;
return $this;
}
/**
@@ -172,25 +166,23 @@ class Event implements EventInterface
*
* @param string|int $name
* @param mixed $value
* @return Event
*/
public function setParam($name, $value)
{
if (is_array($this->params) || $this->params instanceof ArrayAccess) {
// Arrays or objects implementing array access
$this->params[$name] = $value;
} else {
// Objects
$this->params->{$name} = $value;
return;
}
return $this;
// Objects
$this->params->{$name} = $value;
}
/**
* Stop further event propagation
*
* @param bool $flag
* @return void
*/
public function stopPropagation($flag = true)
{

View File

@@ -2,9 +2,9 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
@@ -65,7 +65,7 @@ interface EventInterface
/**
* Set event parameters
*
* @param string $params
* @param array|ArrayAccess $params
* @return void
*/
public function setParams($params);

View File

@@ -2,18 +2,14 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
use ArrayAccess;
use ArrayObject;
use Traversable;
use Zend\Stdlib\CallbackHandler;
use Zend\Stdlib\PriorityQueue;
/**
* Event manager: notification system
@@ -25,24 +21,43 @@ class EventManager implements EventManagerInterface
{
/**
* Subscribed events and their listeners
* @var array Array of PriorityQueue objects
*
* 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 = array();
protected $events = [];
/**
* @var string Class representing the event being emitted
* @var EventInterface Prototype to use when creating an event at trigger().
*/
protected $eventClass = 'Zend\EventManager\Event';
protected $eventPrototype;
/**
* Identifiers, used to pull shared signals from SharedEventManagerInterface instance
*
* @var array
*/
protected $identifiers = array();
protected $identifiers = [];
/**
* Shared event manager
* @var false|null|SharedEventManagerInterface
*
* @var null|SharedEventManagerInterface
*/
protected $sharedManager = null;
@@ -52,80 +67,39 @@ class EventManager implements EventManagerInterface
* Allows optionally specifying identifier(s) to use to pull signals from a
* SharedEventManagerInterface.
*
* @param null|string|int|array|Traversable $identifiers
*/
public function __construct($identifiers = null)
{
$this->setIdentifiers($identifiers);
}
/**
* Set the event class to utilize
*
* @param string $class
* @return EventManager
*/
public function setEventClass($class)
{
$this->eventClass = $class;
return $this;
}
/**
* Set shared event manager
*
* @param SharedEventManagerInterface $sharedEventManager
* @return EventManager
* @param array $identifiers
*/
public function setSharedManager(SharedEventManagerInterface $sharedEventManager)
public function __construct(SharedEventManagerInterface $sharedEventManager = null, array $identifiers = [])
{
$this->sharedManager = $sharedEventManager;
StaticEventManager::setInstance($sharedEventManager);
return $this;
if ($sharedEventManager) {
$this->sharedManager = $sharedEventManager;
$this->setIdentifiers($identifiers);
}
$this->eventPrototype = new Event();
}
/**
* Remove any shared event manager currently attached
*
* @return void
* @inheritDoc
*/
public function unsetSharedManager()
public function setEventPrototype(EventInterface $prototype)
{
$this->sharedManager = false;
$this->eventPrototype = $prototype;
}
/**
* Get shared event manager
* Retrieve the shared event manager, if composed.
*
* If one is not defined, but we have a static instance in
* StaticEventManager, that one will be used and set in this instance.
*
* If none is available in the StaticEventManager, a boolean false is
* returned.
*
* @return false|SharedEventManagerInterface
* @return null|SharedEventManagerInterface $sharedEventManager
*/
public function getSharedManager()
{
// "false" means "I do not want a shared manager; don't try and fetch one"
if (false === $this->sharedManager
|| $this->sharedManager instanceof SharedEventManagerInterface
) {
return $this->sharedManager;
}
if (!StaticEventManager::hasInstance()) {
return false;
}
$this->sharedManager = StaticEventManager::getInstance();
return $this->sharedManager;
}
/**
* Get the identifier(s) for this EventManager
*
* @return array
* @inheritDoc
*/
public function getIdentifiers()
{
@@ -133,258 +107,152 @@ class EventManager implements EventManagerInterface
}
/**
* Set the identifiers (overrides any currently set identifiers)
*
* @param string|int|array|Traversable $identifiers
* @return EventManager Provides a fluent interface
* @inheritDoc
*/
public function setIdentifiers($identifiers)
public function setIdentifiers(array $identifiers)
{
if (is_array($identifiers) || $identifiers instanceof Traversable) {
$this->identifiers = array_unique((array) $identifiers);
} elseif ($identifiers !== null) {
$this->identifiers = array($identifiers);
}
return $this;
$this->identifiers = array_unique($identifiers);
}
/**
* Add some identifier(s) (appends to any currently set identifiers)
*
* @param string|int|array|Traversable $identifiers
* @return EventManager Provides a fluent interface
* @inheritDoc
*/
public function addIdentifiers($identifiers)
public function addIdentifiers(array $identifiers)
{
if (is_array($identifiers) || $identifiers instanceof Traversable) {
$this->identifiers = array_unique(array_merge($this->identifiers, (array) $identifiers));
} elseif ($identifiers !== null) {
$this->identifiers = array_unique(array_merge($this->identifiers, array($identifiers)));
}
return $this;
$this->identifiers = array_unique(array_merge(
$this->identifiers,
$identifiers
));
}
/**
* Trigger all listeners for a given event
*
* @param string|EventInterface $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|callable $callback Trigger listeners until return value of this callback evaluate to true
* @return ResponseCollection All listener return values
* @throws Exception\InvalidCallbackException
* @inheritDoc
*/
public function trigger($event, $target = null, $argv = array(), $callback = null)
public function trigger($eventName, $target = null, $argv = [])
{
if ($event instanceof EventInterface) {
$e = $event;
$event = $e->getName();
$callback = $target;
} elseif ($target instanceof EventInterface) {
$e = $target;
$e->setName($event);
$callback = $argv;
} elseif ($argv instanceof EventInterface) {
$e = $argv;
$e->setName($event);
$e->setTarget($target);
} else {
$e = new $this->eventClass();
$e->setName($event);
$e->setTarget($target);
$e->setParams($argv);
$event = clone $this->eventPrototype;
$event->setName($eventName);
if ($target !== null) {
$event->setTarget($target);
}
if ($callback && !is_callable($callback)) {
throw new Exception\InvalidCallbackException('Invalid callback provided');
if ($argv) {
$event->setParams($argv);
}
// Initial value of stop propagation flag should be false
$e->stopPropagation(false);
return $this->triggerListeners($event, $e, $callback);
return $this->triggerListeners($event);
}
/**
* 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|EventInterface $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
* @return ResponseCollection
* @deprecated Please use trigger()
* @throws Exception\InvalidCallbackException if invalid callable provided
* @inheritDoc
*/
public function triggerUntil($event, $target, $argv = null, $callback = null)
public function triggerUntil(callable $callback, $eventName, $target = null, $argv = [])
{
trigger_error(
'This method is deprecated and will be removed in the future. Please use trigger() instead.',
E_USER_DEPRECATED
);
return $this->trigger($event, $target, $argv, $callback);
$event = clone $this->eventPrototype;
$event->setName($eventName);
if ($target !== null) {
$event->setTarget($target);
}
if ($argv) {
$event->setParams($argv);
}
return $this->triggerListeners($event, $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|ListenerAggregateInterface $event An event or array of event names. If a ListenerAggregateInterface, proxies to {@link attachAggregate()}.
* @param callable|int $callback If string $event provided, expects PHP callback; for a ListenerAggregateInterface $event, this will be the priority
* @param int $priority If provided, the priority at which to register the callable
* @return CallbackHandler|mixed CallbackHandler if attaching callable (to allow later unsubscribe); mixed if attaching aggregate
* @throws Exception\InvalidArgumentException
* @inheritDoc
*/
public function attach($event, $callback = null, $priority = 1)
public function triggerEvent(EventInterface $event)
{
// Proxy ListenerAggregateInterface arguments to attachAggregate()
if ($event instanceof ListenerAggregateInterface) {
return $this->attachAggregate($event, $callback);
}
return $this->triggerListeners($event);
}
// Null callback is invalid
if (null === $callback) {
/**
* @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 callback; none provided',
__METHOD__
'%s expects a string for the event; received %s',
__METHOD__,
(is_object($eventName) ? get_class($eventName) : gettype($eventName))
));
}
// 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 PriorityQueue();
}
// Create a callback handler, setting the event and priority in its metadata
$listener = new CallbackHandler($callback, array('event' => $event, 'priority' => $priority));
// Inject the callback handler into the queue
$this->events[$event]->insert($listener, $priority);
$this->events[$eventName][(int) $priority][0][] = $listener;
return $listener;
}
/**
* Attach a listener aggregate
*
* Listener aggregates accept an EventManagerInterface instance, and call attach()
* one or more times, typically to attach to multiple events using local
* methods.
*
* @param ListenerAggregateInterface $aggregate
* @param int $priority If provided, a suggested priority for the aggregate to use
* @return mixed return value of {@link ListenerAggregateInterface::attach()}
* @inheritDoc
* @throws Exception\InvalidArgumentException for invalid event types.
*/
public function attachAggregate(ListenerAggregateInterface $aggregate, $priority = 1)
public function detach(callable $listener, $eventName = null, $force = false)
{
return $aggregate->attach($this, $priority);
}
/**
* Unsubscribe a listener from an event
*
* @param CallbackHandler|ListenerAggregateInterface $listener
* @return bool Returns true if event and listener found, and unsubscribed; returns false if either event or listener not found
* @throws Exception\InvalidArgumentException if invalid listener provided
*/
public function detach($listener)
{
if ($listener instanceof ListenerAggregateInterface) {
return $this->detachAggregate($listener);
// 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 (!$listener instanceof CallbackHandler) {
if (! is_string($eventName)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s: expected a ListenerAggregateInterface or CallbackHandler; received "%s"',
'%s expects a string for the event; received %s',
__METHOD__,
(is_object($listener) ? get_class($listener) : gettype($listener))
(is_object($eventName) ? get_class($eventName) : gettype($eventName))
));
}
$event = $listener->getMetadatum('event');
if (!$event || empty($this->events[$event])) {
return false;
if (! isset($this->events[$eventName])) {
return;
}
$return = $this->events[$event]->remove($listener);
if (!$return) {
return false;
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 (!count($this->events[$event])) {
unset($this->events[$event]);
// If the queue for the given event is empty, remove it.
if (empty($this->events[$eventName])) {
unset($this->events[$eventName]);
}
return true;
}
/**
* Detach a listener aggregate
*
* Listener aggregates accept an EventManagerInterface instance, and call detach()
* of all previously attached listeners.
*
* @param ListenerAggregateInterface $aggregate
* @return mixed return value of {@link ListenerAggregateInterface::detach()}
* @inheritDoc
*/
public function detachAggregate(ListenerAggregateInterface $aggregate)
public function clearListeners($eventName)
{
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 PriorityQueue
*/
public function getListeners($event)
{
if (!array_key_exists($event, $this->events)) {
return new 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]);
if (isset($this->events[$eventName])) {
unset($this->events[$eventName]);
}
}
@@ -408,119 +276,68 @@ class EventManager implements EventManagerInterface
*
* Actual functionality for triggering listeners, to which trigger() delegate.
*
* @param string $event Event name
* @param EventInterface $e
* @param null|callable $callback
* @param EventInterface $event
* @param null|callable $callback
* @return ResponseCollection
*/
protected function triggerListeners($event, EventInterface $e, $callback = null)
protected function triggerListeners(EventInterface $event, callable $callback = null)
{
$responses = new ResponseCollection;
$listeners = $this->getListeners($event);
$name = $event->getName();
// 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 (empty($name)) {
throw new Exception\RuntimeException('Event is missing a name; cannot trigger!');
}
foreach ($listeners as $listener) {
$listenerCallback = $listener->getCallback();
if (isset($this->events[$name])) {
$listOfListenersByPriority = $this->events[$name];
// Trigger the listener's callback, and push its result onto the
// response collection
$responses->push(call_user_func($listenerCallback, $e));
// If the event was asked to stop propagating, do so
if ($e->propagationIsStopped()) {
$responses->setStopped(true);
break;
if (isset($this->events['*'])) {
foreach ($this->events['*'] as $priority => $listOfListeners) {
$listOfListenersByPriority[$priority][] = $listOfListeners[0];
}
}
} elseif (isset($this->events['*'])) {
$listOfListenersByPriority = $this->events['*'];
} else {
$listOfListenersByPriority = [];
}
// If the result causes our validation callback to return true,
// stop propagation
if ($callback && call_user_func($callback, $responses->last())) {
$responses->setStopped(true);
break;
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;
}
/**
* Get list of all listeners attached to the shared event manager for
* identifiers registered by this instance
*
* @param string $event
* @return array
*/
protected function getSharedListeners($event)
{
if (!$sharedManager = $this->getSharedManager()) {
return array();
}
$identifiers = $this->getIdentifiers();
//Add wildcard id to the search, if not already added
if (!in_array('*', $identifiers)) {
$identifiers[] = '*';
}
$sharedListeners = array();
foreach ($identifiers as $id) {
if (!$listeners = $sharedManager->getListeners($id, $event)) {
continue;
}
if (!is_array($listeners) && !($listeners instanceof Traversable)) {
continue;
}
foreach ($listeners as $listener) {
if (!$listener instanceof CallbackHandler) {
continue;
}
$sharedListeners[] = $listener;
}
}
return $sharedListeners;
}
/**
* Add listeners to the master queue of listeners
*
* Used to inject shared listeners and wildcard listeners.
*
* @param PriorityQueue $masterListeners
* @param array|Traversable $listeners
* @return void
*/
protected function insertListeners($masterListeners, $listeners)
{
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);
}
}
}

View File

@@ -2,9 +2,9 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;

View File

@@ -2,9 +2,9 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
@@ -36,11 +36,10 @@ trait EventManagerAwareTrait
* $this->eventIdentifier property.
*
* @param EventManagerInterface $events
* @return mixed
*/
public function setEventManager(EventManagerInterface $events)
{
$identifiers = array(__CLASS__, get_class($this));
$identifiers = [__CLASS__, get_class($this)];
if (isset($this->eventIdentifier)) {
if ((is_string($this->eventIdentifier))
|| (is_array($this->eventIdentifier))
@@ -57,7 +56,6 @@ trait EventManagerAwareTrait
if (method_exists($this, 'attachDefaultListeners')) {
$this->attachDefaultListeners();
}
return $this;
}
/**
@@ -69,7 +67,7 @@ trait EventManagerAwareTrait
*/
public function getEventManager()
{
if (!$this->events instanceof EventManagerInterface) {
if (! $this->events instanceof EventManagerInterface) {
$this->setEventManager(new EventManager());
}
return $this->events;

View File

@@ -2,105 +2,140 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
use Traversable;
use Zend\Stdlib\CallbackHandler;
/**
* Interface for messengers
*/
interface EventManagerInterface extends SharedEventManagerAwareInterface
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
*
* Should allow handling the following scenarios:
* - Passing Event object only
* - Passing event name and Event object only
* - Passing event name, target, and Event object
* - Passing event name, target, and array|ArrayAccess of arguments
* - Passing event name, target, array|ArrayAccess of arguments, and callback
* Provided an EventInterface instance, this method will trigger listeners
* based on the event name, raising an exception if the event name is missing.
*
* @param string|EventInterface $event
* @param object|string $target
* @param array|object $argv
* @param null|callable $callback
* @param EventInterface $event
* @return ResponseCollection
*/
public function trigger($event, $target = null, $argv = array(), $callback = null);
public function triggerEvent(EventInterface $event);
/**
* Trigger an event until the given callback returns a boolean true
* Trigger an event, applying a callback to each listener result.
*
* Should allow handling the following scenarios:
* - Passing Event object and callback only
* - Passing event name, Event object, and callback only
* - Passing event name, target, Event object, and callback
* - Passing event name, target, array|ArrayAccess of arguments, and callback
* 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 string|EventInterface $event
* @param object|string $target
* @param array|object $argv
* @param callable $callback
* @param EventInterface $event
* @return ResponseCollection
* @deprecated Please use trigger()
*/
public function triggerUntil($event, $target, $argv = null, $callback = null);
public function triggerEventUntil(callable $callback, EventInterface $event);
/**
* Attach a listener to an event
*
* @param string $event
* @param callable $callback
* @param int $priority Priority at which to register listener
* @return CallbackHandler
* 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($event, $callback = null, $priority = 1);
public function attach($eventName, callable $listener, $priority = 1);
/**
* Detach an event listener
* Detach a listener.
*
* @param CallbackHandler|ListenerAggregateInterface $listener
* @return bool
*/
public function detach($listener);
/**
* Get a list of events for which this collection has listeners
* If no $event or '*' is provided, detaches listener from all events;
* otherwise, detaches only from the named event.
*
* @return array
* @param callable $listener
* @param null|string $eventName Event from which to detach; null and '*'
* indicate all events.
* @return void
*/
public function getEvents();
/**
* Retrieve a list of listeners registered to a given event
*
* @param string $event
* @return array|object
*/
public function getListeners($event);
public function detach(callable $listener, $eventName = null);
/**
* Clear all listeners for a given event
*
* @param string $event
* @param string $eventName
* @return void
*/
public function clearListeners($event);
public function clearListeners($eventName);
/**
* Set the event class to utilize
* Provide an event prototype to use with trigger().
*
* @param string $class
* @return EventManagerInterface
* When `trigger()` needs to create an event instance, it should clone the
* prototype provided to this method.
*
* @param EventInterface $prototype
* @return void
*/
public function setEventClass($class);
public function setEventPrototype(EventInterface $prototype);
/**
* Get the identifier(s) for this EventManager
@@ -112,33 +147,16 @@ interface EventManagerInterface extends SharedEventManagerAwareInterface
/**
* Set the identifiers (overrides any currently set identifiers)
*
* @param string|int|array|Traversable $identifiers
* @return EventManagerInterface
* @param string[] $identifiers
* @return void
*/
public function setIdentifiers($identifiers);
public function setIdentifiers(array $identifiers);
/**
* Add some identifier(s) (appends to any currently set identifiers)
* Add identifier(s) (appends to any currently set identifiers)
*
* @param string|int|array|Traversable $identifiers
* @return EventManagerInterface
* @param string[] $identifiers
* @return void
*/
public function addIdentifiers($identifiers);
/**
* Attach a listener aggregate
*
* @param ListenerAggregateInterface $aggregate
* @param int $priority If provided, a suggested priority for the aggregate to use
* @return mixed return value of {@link ListenerAggregateInterface::attach()}
*/
public function attachAggregate(ListenerAggregateInterface $aggregate, $priority = 1);
/**
* Detach a listener aggregate
*
* @param ListenerAggregateInterface $aggregate
* @return mixed return value of {@link ListenerAggregateInterface::detach()}
*/
public function detachAggregate(ListenerAggregateInterface $aggregate);
public function addIdentifiers(array $identifiers);
}

View File

@@ -2,15 +2,15 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
/**
* Interface providing events that can be attached, detached and triggered.
* Interface indicating that an object composes an EventManagerInterface instance.
*/
interface EventsCapableInterface
{

View File

@@ -2,9 +2,9 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager\Exception;

View File

@@ -2,9 +2,9 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager\Exception;

View File

@@ -2,9 +2,9 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager\Exception;

View File

@@ -2,9 +2,9 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager\Exception;

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

@@ -2,15 +2,14 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager\Filter;
use Zend\EventManager\ResponseCollection;
use Zend\Stdlib\CallbackHandler;
/**
* Interface for intercepting filter chains
@@ -24,23 +23,22 @@ interface FilterInterface
* @param array $params
* @return mixed
*/
public function run($context, array $params = array());
public function run($context, array $params = []);
/**
* Attach an intercepting filter
*
* @param callable $callback
* @return CallbackHandler
*/
public function attach($callback);
public function attach(callable $callback);
/**
* Detach an intercepting filter
*
* @param CallbackHandler $filter
* @param callable $filter
* @return bool
*/
public function detach(CallbackHandler $filter);
public function detach(callable $filter);
/**
* Get all intercepting filters

View File

@@ -2,15 +2,15 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager\Filter;
use Zend\Stdlib\CallbackHandler;
use Zend\Stdlib\SplPriorityQueue;
use Zend\EventManager\Exception;
use Zend\Stdlib\FastPriorityQueue;
/**
* Specialized priority queue implementation for use with an intercepting
@@ -18,7 +18,7 @@ use Zend\Stdlib\SplPriorityQueue;
*
* Allows removal
*/
class FilterIterator extends SplPriorityQueue
class FilterIterator extends FastPriorityQueue
{
/**
* Does the queue contain a given value?
@@ -28,8 +28,7 @@ class FilterIterator extends SplPriorityQueue
*/
public function contains($datum)
{
$chain = clone $this;
foreach ($chain as $item) {
foreach ($this as $item) {
if ($item === $datum) {
return true;
}
@@ -37,6 +36,28 @@ class FilterIterator extends SplPriorityQueue
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
*
@@ -52,9 +73,9 @@ class FilterIterator extends SplPriorityQueue
// Iterate and remove any matches
$removed = false;
$items = array();
$items = [];
$this->rewind();
while (!$this->isEmpty()) {
while (! $this->isEmpty()) {
$item = $this->extract();
if ($item['data'] === $datum) {
$removed = true;
@@ -82,18 +103,18 @@ class FilterIterator extends SplPriorityQueue
* @param FilterIterator $chain
* @return mixed
*/
public function next($context = null, array $params = array(), $chain = null)
public function next($context = null, array $params = [], $chain = null)
{
if (empty($context) || $chain->isEmpty()) {
if (empty($context) || ($chain instanceof FilterIterator && $chain->isEmpty())) {
return;
}
//We can't extract from an empty heap
if ($this->isEmpty()) {
return;
}
$next = $this->extract();
if (!$next instanceof CallbackHandler) {
return;
}
$return = call_user_func($next->getCallback(), $context, $params, $chain);
return $return;
return $next($context, $params, $chain);
}
}

View File

@@ -2,15 +2,13 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
use Zend\Stdlib\CallbackHandler;
/**
* FilterChain: intercepting filter manager
*/
@@ -40,7 +38,7 @@ class FilterChain implements Filter\FilterInterface
* @param mixed $argv Associative array of arguments
* @return mixed
*/
public function run($context, array $argv = array())
public function run($context, array $argv = [])
{
$chain = clone $this->getFilters();
@@ -49,38 +47,32 @@ class FilterChain implements Filter\FilterInterface
}
$next = $chain->extract();
if (!$next instanceof CallbackHandler) {
return;
}
return call_user_func($next->getCallback(), $context, $argv, $chain);
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)
* @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($callback, $priority = 1)
public function attach(callable $callback, $priority = 1)
{
if (empty($callback)) {
throw new Exception\InvalidCallbackException('No callback provided');
}
$filter = new CallbackHandler($callback, array('priority' => $priority));
$this->filters->insert($filter, $priority);
return $filter;
$this->filters->insert($callback, $priority);
return $callback;
}
/**
* Detach a filter from the chain
*
* @param CallbackHandler $filter
* @param callable $filter
* @return bool Returns true if filter found and unsubscribed; returns false otherwise
*/
public function detach(CallbackHandler $filter)
public function detach(callable $filter)
{
return $this->filters->remove($filter);
}

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

@@ -2,9 +2,9 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
@@ -26,16 +26,15 @@ interface ListenerAggregateInterface
* implementation will pass this to the aggregate.
*
* @param EventManagerInterface $events
*
* @param int $priority
* @return void
*/
public function attach(EventManagerInterface $events);
public function attach(EventManagerInterface $events, $priority = 1);
/**
* Detach all previously attached listeners
*
* @param EventManagerInterface $events
*
* @return void
*/
public function detach(EventManagerInterface $events);

View File

@@ -2,9 +2,9 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
@@ -16,9 +16,9 @@ namespace Zend\EventManager;
trait ListenerAggregateTrait
{
/**
* @var \Zend\Stdlib\CallbackHandler[]
* @var callable[]
*/
protected $listeners = array();
protected $listeners = [];
/**
* {@inheritDoc}
@@ -26,9 +26,8 @@ trait ListenerAggregateTrait
public function detach(EventManagerInterface $events)
{
foreach ($this->listeners as $index => $callback) {
if ($events->detach($callback)) {
unset($this->listeners[$index]);
}
$events->detach($callback);
unset($this->listeners[$index]);
}
}
}

View File

@@ -2,9 +2,9 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
@@ -32,12 +32,10 @@ class ResponseCollection extends SplStack
* Mark the collection as stopped (or its opposite)
*
* @param bool $flag
* @return ResponseCollection
*/
public function setStopped($flag)
{
$this->stopped = (bool) $flag;
return $this;
}
/**

View File

@@ -2,16 +2,13 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
use Zend\Stdlib\CallbackHandler;
use Zend\Stdlib\PriorityQueue;
/**
* Shared/contextual EventManager
*
@@ -19,158 +16,220 @@ use Zend\Stdlib\PriorityQueue;
* 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
SharedEventAggregateAwareInterface,
SharedEventManagerInterface
class SharedEventManager implements SharedEventManagerInterface
{
/**
* Identifiers with event connections
* @var array
*/
protected $identifiers = array();
protected $identifiers = [];
/**
* Attach a listener to an event
* Attach a listener to an event emitted by components with specific identifiers.
*
* Allows attaching a callback to an event offered by one or more
* identifying components. As an example, the following connects to the
* "getAll" event of both an AbstractResource and EntityResource:
* 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();
* $sharedEventManager->attach(
* array('My\Resource\AbstractResource', 'My\Resource\EntityResource'),
* 'getAll',
* function ($e) use ($cache) {
* if (!$id = $e->getParam('id', false)) {
* return;
* 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;
* }
* if (!$data = $cache->load(get_class($resource) . '::getOne::' . $id )) {
* return;
* }
* return $data;
* }
* );
* );
* }
* </code>
*
* @param string|array $id Identifier(s) for event emitting component(s)
* @param string $identifier Identifier for event emitting component.
* @param string $event
* @param callable $callback PHP Callback
* @param callable $listener Listener that will handle the event.
* @param int $priority Priority at which listener should execute
* @return CallbackHandler|array Either CallbackHandler or array of CallbackHandlers
* @return void
* @throws Exception\InvalidArgumentException for invalid identifier arguments.
* @throws Exception\InvalidArgumentException for invalid event arguments.
*/
public function attach($id, $event, $callback, $priority = 1)
public function attach($identifier, $event, callable $listener, $priority = 1)
{
$ids = (array) $id;
$listeners = array();
foreach ($ids as $id) {
if (!array_key_exists($id, $this->identifiers)) {
$this->identifiers[$id] = new EventManager($id);
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);
}
$listeners[] = $this->identifiers[$id]->attach($event, $callback, $priority);
return;
}
if (count($listeners) > 1) {
return $listeners;
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))
));
}
return $listeners[0];
}
/**
* Attach a listener aggregate
*
* Listener aggregates accept an EventManagerInterface instance, and call attachShared()
* one or more times, typically to attach to multiple events using local
* methods.
*
* @param SharedListenerAggregateInterface $aggregate
* @param int $priority If provided, a suggested priority for the aggregate to use
* @return mixed return value of {@link ListenerAggregateInterface::attachShared()}
*/
public function attachAggregate(SharedListenerAggregateInterface $aggregate, $priority = 1)
{
return $aggregate->attachShared($this, $priority);
}
/**
* Detach a listener from an event offered by a given resource
*
* @param string|int $id
* @param CallbackHandler $listener
* @return bool Returns true if event and listener found, and unsubscribed; returns false if either event or listener not found
*/
public function detach($id, CallbackHandler $listener)
{
if (!array_key_exists($id, $this->identifiers)) {
return false;
// Do we have any listeners on the provided identifier?
if (! isset($this->identifiers[$identifier])) {
return;
}
return $this->identifiers[$id]->detach($listener);
}
/**
* Detach a listener aggregate
*
* Listener aggregates accept a SharedEventManagerInterface instance, and call detachShared()
* of all previously attached listeners.
*
* @param SharedListenerAggregateInterface $aggregate
* @return mixed return value of {@link SharedListenerAggregateInterface::detachShared()}
*/
public function detachAggregate(SharedListenerAggregateInterface $aggregate)
{
return $aggregate->detachShared($this);
}
/**
* Retrieve all registered events for a given resource
*
* @param string|int $id
* @return array
*/
public function getEvents($id)
{
if (!array_key_exists($id, $this->identifiers)) {
//Check if there are any id wildcards listeners
if ('*' != $id && array_key_exists('*', $this->identifiers)) {
return $this->identifiers['*']->getEvents();
if (null === $eventName || ('*' === $eventName && ! $force)) {
foreach (array_keys($this->identifiers[$identifier]) as $eventName) {
$this->detach($listener, $identifier, $eventName, true);
}
return false;
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]);
}
return $this->identifiers[$id]->getEvents();
}
/**
* Retrieve all listeners for a given identifier and event
*
* @param string|int $id
* @param string|int $event
* @return false|PriorityQueue
* @param string[] $identifiers
* @param string $eventName
* @return array[]
* @throws Exception\InvalidArgumentException
*/
public function getListeners($id, $event)
public function getListeners(array $identifiers, $eventName)
{
if (!array_key_exists($id, $this->identifiers)) {
return false;
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__
));
}
return $this->identifiers[$id]->getListeners($event);
$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;
}
/**
* Clear all listeners for a given identifier, optionally for a specific event
*
* @param string|int $id
* @param null|string $event
* @return bool
* @inheritDoc
*/
public function clearListeners($id, $event = null)
public function clearListeners($identifier, $eventName = null)
{
if (!array_key_exists($id, $this->identifiers)) {
if (! isset($this->identifiers[$identifier])) {
return false;
}
if (null === $event) {
unset($this->identifiers[$id]);
return true;
if (null === $eventName) {
unset($this->identifiers[$identifier]);
return;
}
return $this->identifiers[$id]->clearListeners($event);
if (! isset($this->identifiers[$identifier][$eventName])) {
return;
}
unset($this->identifiers[$identifier][$eventName]);
}
}

View File

@@ -2,64 +2,58 @@
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @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 http://framework.zend.com/license/new-bsd New BSD License
* @license https://github.com/zendframework/zend-eventmanager/blob/master/LICENSE.md
*/
namespace Zend\EventManager;
use Zend\Stdlib\CallbackHandler;
use Zend\Stdlib\PriorityQueue;
/**
* Interface for shared event listener collections
*/
interface SharedEventManagerInterface
{
/**
* Retrieve all listeners for a given identifier and event
* Attach a listener to an event emitted by components with specific identifiers.
*
* @param string|int $id
* @param string|int $event
* @return false|PriorityQueue
*/
public function getListeners($id, $event);
/**
* Attach a listener to an event
*
* @param string|array $id Identifier(s) for event emitting component(s)
* @param string $event
* @param callable $callback PHP Callback
* @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
* @return CallbackHandler|array Either CallbackHandler or array of CallbackHandlers
*/
public function attach($id, $event, $callback, $priority = 1);
public function attach($identifier, $eventName, callable $listener, $priority = 1);
/**
* Detach a listener from an event offered by a given resource
* Detach a shared listener.
*
* @param string|int $id
* @param CallbackHandler $listener
* @return bool Returns true if event and listener found, and unsubscribed; returns false if either event or listener not found
* 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($id, CallbackHandler $listener);
public function detach(callable $listener, $identifier = null, $eventName = null);
/**
* Retrieve all registered events for a given resource
* Retrieve all listeners for given identifiers
*
* @param string|int $id
* @param array $identifiers
* @param string $eventName
* @return array
*/
public function getEvents($id);
public function getListeners(array $identifiers, $eventName);
/**
* Clear all listeners for a given identifier, optionally for a specific event
*
* @param string|int $id
* @param null|string $event
* @return bool
* @param string $identifier
* @param null|string $eventName
*/
public function clearListeners($id, $event = null);
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;
}
}
}
}
}