Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Commit

Permalink
Merge af36356 into 627f3e8
Browse files Browse the repository at this point in the history
  • Loading branch information
weierophinney committed Apr 10, 2019
2 parents 627f3e8 + af36356 commit f901e3e
Show file tree
Hide file tree
Showing 54 changed files with 3,078 additions and 421 deletions.
114 changes: 112 additions & 2 deletions CHANGELOG.md
Expand Up @@ -8,13 +8,123 @@ All notable changes to this project will be documented in this file, in reverse

- [#72](https://github.com/zendframework/zend-eventmanager/pull/72) adds support for PHP 7.3.

- [#73](https://github.com/zendframework/zend-eventmanager/pull/73) adds interfaces to allow duck-typing the EventManager as a [PSR-14](https://www.php-fig.org/psr/psr-14/) event
dispatcher. Full support is not provided yet as this version still supports
PHP 5.6. The new interfaces include:

- `Zend\EventManager\EventDispatcherInterface`
- `Zend\EventManager\ListenerProvider\ListenerProviderInterface`
- `Zend\EventManager\StoppableEventInterface`

These interfaces will be removed in version 4.0, in favor of the official
PSR-14 interfaces.

- [#73](https://github.com/zendframework/zend-eventmanager/pull/73) adds the following interfaces:
- `Zend\EventManager\EventDispatchingInterface`, for indicating a class
composes an `EventDispatcherInterface` instance. This interface will replace
the `Zend\EventManager\EventsCapableInterface` in version 4.0.
- `Zend\Expressive\ListenerProvider\PrioritizedListenerProviderInterface`,
which extends the `ListenerProviderInterface`, and adds the method
`getListenersForEventByPriority($event, $identifiers = [])`. This method
will return a list of integer priority keys mapping to lists of callable
listeners.
- `Zend\Expressive\ListenerProvider\PrioritizedListenerAttachmentInterface`,
which provides methods for attaching and detaching listeners with optional
priority values. This interface largely replaces the various related methods
in the current `EventManagerInterface`, and is for use with listener
providers.
- `Zend\Expressive\ListenerProvider\ListenerSubscriberInterface`, for
indicating that a class can attach multiple listeners to a
`PrioritizedListenerAttachmentInterface` instance. This largely replaces the
current `ListenerAggregateInterface` functionality. Users should likely use
the PSR-14 utility package's `DelegatingProvider` instead, however.

- [#73](https://github.com/zendframework/zend-eventmanager/pull/73) adds the following listener provider classes and utilities:
- `AbstractListenerSubscriber` and `ListenerSubscriberTrait` can be used to
provide a generic way to detach subscribers. In most cases,
`ListenerSubscriberInterface` implementations should define their own logic
for doing so.
- `PrioritizedListenerProvider` implements `PrioritizedListenerProviderInterface`
and `PrioritizedListenerAttachmentInterface` in order to provide the various
listener attachment and retrieval capabilities in previous versions of the
`EventManager` class.
- `PrioritizedIdentifierListenerProvider` implements `PrioritizedListenerProviderInterface`
and `SharedEventManagerInterface`, and provides all features of the
`SharedEventManager` class from previous versions of the package.
- `PrioritizedAggregateListenerProvider` implements `PrioritizedListenerProviderInterface`
and accepts a list of `PrioritizedListenerProviderInterface` instances and
optionally a generic `ListenerProviderInterface` instance to its
constructor. When retrieving listeners, it will loop through the
`PrioritizedListenerProviderInterface` instance in order, yielding from
each, and then, if present, yield from the generic
`ListenerProviderInterface` instance. This approach essentially replaces the
listener and shared listener aggregation in previous versions of the
`EventManager`.
- `LazyListener` combines the functionalities of `Zend\EventManager\LazyListener`
and `Zend\EventManager\LazyEventListener`. If no event or priority are
provided to the constructor, than the `getEvent()` and `getPriority()`
methods will each return `null`. When invoked, the listener will pull the
specified service from the provided DI container, and then invoke it.
- `LazyListenerSubscriber` implements `ListenerSubscriberInterface` and
accepts a list of `LazyListener` instances to its constructor; any
non-`LazyListener` instances or any that do not define an event will cause
th constructor to raise an exception. When its `attach()` method is called,
it attaches the lazy listeners based on the event an priority values it
pulls from them.

- [#73](https://github.com/zendframework/zend-eventmanager/pull/73) adds the static method `createUsingListenerProvider()` to the `EventManager`
class. This method takes a `ListenerProviderInterface`, and will then pull
directly from it when triggering events. If the provider also implements
`PrioritizedListenerAttachmentInterface`, the various listener attachment
methods defined in `EventManager` will proxy to it.

- [#73](https://github.com/zendframework/zend-eventmanager/pull/73) adds the static method `createUsingListenerProvider()` to the `EventManager`

### Changed

- Nothing.
- [#73](https://github.com/zendframework/zend-eventmanager/pull/73) modifies the `SharedEventManager` class to extend the new
`Zend\EventManager\ListenerProvider\PrioritizedIdentifierListenerProvider` class.

- [#73](https://github.com/zendframework/zend-eventmanager/pull/73) modifies the `EventManager` class as follows:
- It now implements each of `ListenerProviderInterface` and
`PrioritizedListenerAttachmentInterface`.
- If constructed normally, it will create a `PrioritizedListenerProvider`
instance, and use that for all listener attachment. If a
`SharedEventManagerInterface` is provided, it will create a
`PrioritizedAggregateListenerProvider` using its own
`PrioritizedListenerProvider` and the shared manager, and use that for
fetching listeners.
- Adds a `dispatch()` method as an alternative to the various `trigger*()` methods.

### Deprecated

- Nothing.
- [#73](https://github.com/zendframework/zend-eventmanager/pull/73) deprecates the following interfaces and classes:
- `Zend\EventManager\EventInterface`. Users should start using vanilla PHP
objects that encapsulate all expected behavior for setting and retrieving
values and otherwise mutating state, including how and when propagation of the
event should stop.
- `Zend\EventManager\EventManagerInterface`; start typehinting against the
PSR-14 `EventDispatcherInterface` (or, in the meantime, the package-specific
variant).
- `Zend\EventManager\EventManagerAwareInterface`
- `Zend\EventManager\EventManagerAwareTrait`
- `Zend\EventManager\EventsCapableInterface`; start using `EventDispatchingInterface` instead.
- `Zend\EventManager\SharedEventManager`; start using listener providers
instead, attaching to identifiers based on event types.
- `Zend\EventManager\SharedEventManagerInterface`
- `Zend\EventManager\SharedEventsCapableInterface`
- `Zend\EventManager\ListenerAggregateInterface`; use the new `ListenerSubscriberInterface` instead.
- `Zend\EventManager\ListenerAggregateTrait`; use the new
`ListenerSubscriberTrait`, or define your own detachment logic.
- `Zend\EventManager\AbstractListenerAggregate`; use the new
`AbstractListenerSubscriber`, or define your own detachment logic.
- `Zend\EventManager\ResponseCollection`; aggregate state in the event itself,
and have the event determine when propagation needs to stop.
- `Zend\EventManager\LazyListener`; use `Zend\EventManager\ListenerProvider\LazyListener` instead.
- `Zend\EventManager\LazyEventListener`; use `Zend\EventManager\ListenerProvider\LazyListener` instead.
- `Zend\EventManager\LazyListenerAggregate`; use `Zend\EventManager\ListenerProvider\LazyListenerSubscriber` instead.
- `Zend\EventManager\FilterChain` and the `Filter` subnamespace; these will
move to a separate package in the future.

### Removed

Expand Down
194 changes: 194 additions & 0 deletions TODO-PSR-14.md
@@ -0,0 +1,194 @@
# TODO for PSR-14 implementation

## 3.3.0 forwards-compatibility release

- [x] `StoppableEventInterface` implementation
- [x] Create a `StoppableEventInterface`
- [x] Make `Event` implement it
- [x] Deprecate `propagationIsStopped()` in both `EventInterface` and `Event`
- [x] Have `Event::propagationIsStopped()` proxy to `Event::isPropagationStopped()`
- [x] Modify `EventManager` internals to use the PSR-14 method if available
- [x] Mark `StoppableEventInterface` as deprecated
- [ ] Listener provider implementation
- [x] Create a `ListenerProvider` subnamespace
- [x] Create a `ListenerProviderInterface` shim
- [x] Create a `PrioritizedListenerProvider` interface extending the
`ListenerProviderInterface` and defining a
`getListenersForEventByPriority($event, array $identifiers = []) : array<int, callable[]>` method.
- [x] Create a `PrioritizedListenerAttachmentInterface`, defining:
- [x] `attach($event, callable $listener, $priority = 1)` (where `$event`
can be an object or string name)
- [x] `detach(callable $listener, $event = null, $force = false)` (where `$event`
can be an object or string name and `$force` is boolean)
- [x] `attachWildcardListener(callable $listener, $priority = 1)`
(`attach('*', $listener, $priority)` will proxy to this method)
- [x] `detachWildcardListener(callable $listener, $force = false)`
(`detach($listener, '*', $force)` will proxy to this method)
- [x] `clearListeners($event)`
- [x] Create a `PrioritizedListenerProvider` implementation of the above based
on the internals of `EventManager`
- [x] attachment/detachment
- [x] getListenersForEvent should take into account event name if an EventInterface
- [x] getListenersForEvent should also pull wildcard listeners
- [x] getListenersForEvent should accept an optional second argument, an
array of identifiers. This method will return all listeners in prioritized
order.
- [x] implement `getListenersForEventByPriority`
- [x] Create a `PrioritizedIdentifierListenerProvider` that implements
both the `PrioritizedListenerProvider` interface and the
`SharedEventManagerInterface`
- [x] implement `getListenersForEventByPriority`
- [x] `SharedEventManager` will extend this class
- [x] mark as deprecated (will not use this in v4)
- [x] Create a `PrioritizedAggregateListenerProvider` implementation
- [x] Accepts a list of `PrioritizedListenerProvider` instances
- [x] `getListenersByEvent()` will loop through each, in order, calling the
`getListenersForEventByPriority()` method of each, returning the
aggregated listeners in priority order.
- [x] Make `SharedEventManager` an extension of `PrioritizedIdentifierListenerProvider`
- [x] Create `ListenerSubscriberInterface`
- [x] `attach(PrioritizedListenerAttachmentInterface $provider, $priority = 1)`
- [x] `detach(PrioritizedListenerAttachmentInterface $provider)`
- [x] Create `AbstractListenerSubscriber` and/or `ListenerSubscriberTrait`
- [x] define a default `detach()` implementation
- [x] Create `LazyListenerSubscriber` based on `LazyListenerAggregate`
- [x] Define an alternate LazyListener:
- [x] `__construct(ContainerInterface $container, string $event = null, int $priority = 1)`
- [x] implements functionality from both `LazyListener` and `LazyEventListener`, minus passing env to container
- [x] without an event, can be attached to any provider
- [x] with an event, can be attached to `LazyListenerSubscriber`
- [x] Constructor aggregates `LazyListener` _instances_ only
- [x] raises exception when `getEvent()` returns null
- [x] Adapter for SharedEventManagerInterface
Since we type-hint on SharedEventManagerInterface, we need to adapt generic
implementations to work as ListenerProviders.
- [x] Class that adapts SharedEventManagerInterface instances to ListenerProviders
- [x] Event Dispatcher implementation
- [x] Implement `PrioritizedListenerAttachmentInterface` (if BC)
- [x] Implement `ListenerProviderInterface` (if BC)
- [x] Create a `PrioritizedListenerProvider` instance in the `EventManger`
constructor
- [x] Decorate it in a `PrioritizedAggregateListenerProvider`
- [x] Have the various `attach()`, `detach()`, etc. methods proxy to it.
- [x] Adapt any provided `SharedEventManagerInterface` instance, and add it
to the `PrioritizedAggregateListenerProvider`
- [x] Create a named constructor that accepts a listener provider and which
then uses it internally.
- [x] If the instance is a `PrioritizedListenerAttachmentInterface`
instance, allow the attach/detach/clear methods to proxy to it.
- [x] When triggering listeners, create a `PrioritizedAggregateListenerProvider`
with the composed `PrioritizedListenerProvider` and `SharedListenerProvider` /
`PrioritizedIdentifierListenerProvider` implementations, in that order.
- [x] Replace logic of `triggerListeners()` to just call
`getListenersForEvent()` on the provider. It can continue to aggregate the
responses in a `ResponseCollection`
- [x] `triggerListeners()` no longer needs to type-hint its first argument
- [x] Create a `dispatch()` method
- [x] Method will act like `triggerEvent()`, except
- [x] it will return the event itself
- [x] it will need to validate that it received an object before calling
`triggerListeners`
- [x] Additional utilities
- [x] `EventDispatchingInterface` with a `getEventDispatcher()` method
- [x] Deprecations
- [x] `EventInterface`
- [x] `EventManagerInterface`
- [x] `EventManagerAwareInterface`
- [x] `EventManagerAwareTrait`
- [x] `EventsCapableInterface` (point people to `EventDispatchingInterface`)
- [x] `SharedEventManager`
- [x] `SharedEventManagerInterface`
- [x] `SharedEventsCapableInterface`
- [x] `ListenerAggregateInterface` (point people to the `PrioritizedListenerAttachmentInterface`)
- [x] `ListenerAggregateTrait` (point people to `ListenerSubscriberTrait`)
- [x] `AbstractListenerAggregate` (point people to `AbstractListenerSubscriber` and/or `ListenerSubscriberTrait`)
- [x] `ResponseCollection` (tell people to aggregate state/results in the event itself)
- [x] `LazyListener` (point people to `ListenerProvider\LazyListener`)
- [x] `LazyEventListener` (point people to `ListenerProvider\LazyListener`)
- [x] `LazyListenerAggregate` (point people to `ListenerProvider\LazyListenerSubscriber`)
- [x] `FilterChain` and `Filter` subnamespace (this should be done in a separate component)

## 4.0.0 full release

- [ ] Removals
- [ ] `EventInterface`
- [ ] `EventManagerInterface`
- [ ] `EventManagerAwareInterface`
- [ ] `EventManagerAwareTrait`
- [ ] `EventsCapableInterface`
- [ ] `SharedEventManager`
- [ ] `SharedEventManagerInterface`
- [ ] `SharedEventsCapableInterface`
- [ ] `ListenerAggregateInterface`
- [ ] `ListenerAggregateTrait`
- [ ] `AbstractListenerAggregate`
- [ ] `ResponseCollection`
- [ ] `LazyListener`
- [ ] `LazyEventListener`
- [ ] `LazyListenerAggregate`
- [ ] `FilterChain` and `Filter` subnamespace
- [ ] `StoppableEventInterface` (will use PSR-14 version)
- [ ] `ListenerProviderInterface` (will use PSR-14 version)
- [ ] `PrioritizedIdentifierListenerProvider`
- Changes
- [ ] `PrioritizedListenerAttachmentInterface` (and implementations)
- [ ] extend PSR-14 `ListenerProviderInterface`
- [ ] add `string` typehint to `$event` in `attach()` and `detach()`
- [ ] add `bool` typehint to `$force` argument of `detach()`
- [ ] `PrioritizedListenerProvider` interface (and implementations)
- [ ] Fulfill PSR-14 `ListenerProviderInterface`
- [ ] remove `$identifiers` argument to getListenersForEventByPriority and getListenersForEvent
- [ ] add `object` typehint to `getListenersForEventByPriority`
- [ ] `EventDispatcher`
- [ ] implement PSR-14 `EventDispatcherInterface`

## Concerns

### MVC

Currently, the MVC relies heavily on:

- event names (vs types)
- event targets
- event params
- `stopPropagation($flag)` (vs custom stop conditions in events)
- `triggerEventUntil()` (vs custom stop conditions in events)

We would need to draw attention to usage of methods that are not specific to an
event implementation, and recommend usage of other methods where available.
(We would likely keep the params implementation, however, to allow passing
messages via the event instance(s).)

Additionally, we will need to have some sort of event hierarchy:

- a base MVC event from which all others derive. This will be necessary to
ensure that existing code continues to work.
- a BootstrapEvent
- a RouteEvent
- a DispatchEvent
- a DispatchControllerEvent
- a DispatchErrorEvent
- Potentially broken into a RouteUnmatchedEvent, DispatchExceptionEvent,
MiddlewareExceptionEvent, ControllerNotFoundEvent, InvalidControllerEvent,
and InvalidMiddlewareEvent
- a RenderEvent
- a RenderErrorEvent
- a FinishEvent
- a SendResponseEvent (this one is not an MvcEvent, however)

The event names associated with each would be based on existing event names,
allowing the ability to attach using legacy names OR the class name.

We can allow using `stopPropagation()`, but have it trigger a deprecation
notice, asking users to use more specific methods of the event to stop
propagation, or, in the case of errors, raising exceptions.

- `setError()` would cause `isPropagationStopped()` to return true.
- A new method, `setFinalResponse()` would both set the response instance, as
well as cause `isPropagationStopped()` to return true.
- The `RouteEvent` would also halt propagation when `setRouteResult()` is
called.

Internally, we will also stop using the `*Until()` methods, and instead rely on
the events to handle this for us. If we need a return value, we will instead
pull it from the event on completion.
2 changes: 1 addition & 1 deletion composer.json
Expand Up @@ -38,7 +38,7 @@
"phpbench/phpbench": "^0.13",
"phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1.2",
"zendframework/zend-stdlib": "^2.7.3 || ^3.0",
"container-interop/container-interop": "^1.1.0",
"container-interop/container-interop": "^1.2.0",
"zendframework/zend-coding-standard": "~1.0.0"
},
"suggest": {
Expand Down
2 changes: 1 addition & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/AbstractListenerAggregate.php
Expand Up @@ -11,6 +11,11 @@

/**
* Abstract aggregate listener
*
* @deprecated since 3.3.0. This class will be removed in version 4.0.0, in
* favor of the ListenerProvider\AbstractListenerSubscriber. In most cases,
* subscribers should fully implement ListenerSubscriberInterface on their
* own, however.
*/
abstract class AbstractListenerAggregate implements ListenerAggregateInterface
{
Expand Down

0 comments on commit f901e3e

Please sign in to comment.