Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Become a PSR-14 provider #12

Open
wants to merge 28 commits into
base: 3.4.x
Choose a base branch
from

Conversation

weierophinney
Copy link
Member

Q A
BC Break no
New Feature yes
RFC yes (see original ZF issue)

Description

This patch adapts EventManager to work as a PSR-14 EventDispatcher. It does so by doing the following:

  • Moving all listener aggregation into classes within a ListenerProvider subnamespace; this includes both the listener attachment previously in the EventManager instance as well as the SharedEventManager instance.
  • Renaming "listener aggregates" to "listener subscribers" in the new ListenerProvider subnamespace.
  • Adapting EventManager to consume listener providers. By default, it will create a default priority-based listener provider, and have its various listener attachment methods proxy to that provider; it then aggregates that provider with the SharedEventManager (which is itself a provider now) in order to retrieve listeners. This is done in such a way that behavior is completely backwards compatible with current usage.
  • A new named constructor, createUsingListenerProvider(), allows providing a specific listener provider for use with the EventManager. If the provider is attachment capable, the various listener attachment methods will proxy to it; otherwise, they will raise an exception.

The patch also deprecates a number of features, including:

  • Most interfaces, including the EventInterface.
  • The entire "shared event manager" concept (this can be accomplished via event object hierarchies instead)

TODO

  • Write changelog entries
  • Create documentation of new features
  • Document how to start migrating to the new features in order to prepare for version 4

Fixes #2

@weierophinney weierophinney added this to the 3.4.0 milestone Jan 8, 2021
@weierophinney weierophinney force-pushed the feature/psr-14-duck-typing branch 2 times, most recently from 57c1a8c to f04eef4 Compare January 8, 2021 20:39
composer.json Outdated Show resolved Hide resolved
@weierophinney weierophinney force-pushed the feature/psr-14-duck-typing branch 2 times, most recently from 48fb87a to 7e4eac6 Compare January 21, 2021 22:47
This patch updates the `Event` implementation to implement the PSR-14 `StoppableEventInterface`, and deprecates its `propagationIsStopped()` method in favor of the PSR-14 `isPropagationStopped()` method.

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
If the method `isPropagationStopped()` is defined, use it over the `propagationIsStopped()` method.

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
Modifies Event::propagationIsStopped such that it now proxies to the
isPropagationStopped method, and documents in the deprecation notice
that this happens.

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
For use in getting a lookup table of priorities and associated listeners, optionally using identifiers for lookup.

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
This provides the methods necessary for attaching listeners. It does not extend `PrioritizedListenerProviderInterface`, as we want to be able to re-use that particular interface with shared providers, which will have a different attachment mechanism in version 3 releases.

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
New provider implements `PrioritizedListenerAttachmentInterface` and `PrioritizedListenerProviderInterface`, and will iterate attached listeners in priority order.

Each iteration will take into account both the event name, if a `getName()` method is available, the event class, and any wildcard listeners, and listeners of the same priority will be returned in the order they are attached, based on those criteria.

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
The PrioritizedIdentifierListenerProvider mimics functionality present in the SharedEventManager (and implements the SharedEventManagerInterface).
Its purpose is to be a drop-in replacement for the `SharedEventManager` to allow users to start migrating to PSR-14 functionality.

In the process of working on this implementation, I discovered some complexity in the data structure returned from `getListenersForEventByPriority` implementation of `PrioritizedListenerProvider` that, when mimiced in `PrioritizedIdentifierListenerProvider`, made verifying behavior difficult.
In particular, it was this line:

```php
$prioritizedListeners[$priority][] = $listOfListeners[0];
```

The problem that arose is that the `$prioritizedListeners` returned were now two levels deep, which made comparisons far harder. I changed this to read:

```
$prioritizedListeners[$priority] = isset($prioritizedListeners[$priority])
    ? array_merge($prioritizedListeners[$priority], $listOfListeners[0])
    : $listOfListeners[0];
```

This makes the return value far simpler, and _should_ keep speed reasonable, though I have yet to benchmark it.

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
This version acts like the combination of EventManager+SharedEventManager in terms of how it aggregates and resolves priority for listeners.

The class aggregates a list of `PrioritizedListenerAttachmentInterface` instances (and implements the interface itself), looping over each in ordert to build up a prioritized list of all listeners from all providers.
Since they are done in order, the order in which they should be attached generally is:

- PrioritizedListenerProvider
- PrioritizedIdentifierListenerProvider

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
…rProvider

Doing so will allow us to use it in a PrioritizedAggregateListenerProvider within the EventManager later.

Required a couple changes to tests, as PrioritizedIdentifierListenerProvider widens what are allowed as events and identifiers when retrieving listeners.

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
This is necessary to keep feature parity with current versions, but can be removed in version 4.

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
Added to the ListenerProvider namespace.
Accepts a PrioritizedListenerAttachmentInterface argument, to which it will subscribe listeners.

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
Each implements ListenerSubscriberInterface::detach

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
Combines the features of LazyListener and LazyEventListener into `Laminas\EventManager\ListenerProvider\LazyListener`.
`LazyListenerSubscriber` is based on `LazyListenerAggregate`, but simplifies it by having it compose `LazyListener` instances only (no creation within it).

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
…PrioritizedListenerAttachmentInterface

Allows the EventManager to act as its own provider.

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
- Adds `Laminas\EventManager\SharedEventManager\SharedEventManagerDecorator`, which decorates generic `SharedEventManagerInterface` instances as listener providers.
- Modifies `PrioritizedAggregateListenerProvider` to accept an optional `ListenerProviderInterface $default` argument. This allows non-prioritized `SharedEventManagerInterface` instances (such as the `SharedEventManagerDecorator` in the previous item) to be fallback providers.
- Modifies `Laminas\EventManager\EventManager` as follows:
  - It now implements the PSR-14 `EventDispatcherInterface`
  - It now composes a `$provider` property, and an optional `$prioritizedProvider` property. If you instantiate it per previous versions, it creates a `PrioritizedListenerProvider` instance and assigns it to the `$prioritizedProvider` property.
    It then checks to see if a shared manager was provided, and the type provided, to either assign the `$prioritizedProvider` as the `$provider`, or a `PrioritizedAggregateListenerProvider` that composes both the `$prioritizedProvider` and shared manager instances.
  - It adds a static named constructor, `createUsingListenerProvider()`, which accepts a single `ListenerProviderInterface` instance.
    This value is assigned to `$provider`, and, if it is a `PrioritizedListenerAttachmentInterface` instance, to the `$prioritizedProvider` property as well.
  - Each of the listener attachment methods (attach, detach, clearListeners, *WildcardListeners) now proxy to the composed `$prioritizedProvider`, if any.
    If there is none, theses methods now raise an exception.
  - The `getListenersForEvent()` method now proxies to the underling `$provider` property.
  - The `triggerListeners()` method now consumes the value of `getListenersForEvent()`.
  - It adds the method `dispatch($event)`, which proxies to `triggerListeners()`, and returns the `$event` it was passed.
    The method raises an exception of `$event` is a non-object.
  - Each of `trigger()`, `triggerUntil`, `triggerEvent`, `triggerEventUntil`, `getIdentifiers`, `setIdentifiers`, `addIdenitifers`, `getSharedManager`, `attach`, `detach`, `attachWildcardListener`, `detachWildcardListener`, `clearListeners`, and `getListenersForEvent` have been marked deprecated.
- Updates `EventListenerIntrospectionTrait` to work with the new internals of the `EventManager`.
- Updates `EventManagerTest`:
  - updates `getListenersForEvent()` to work with the new `EventManager` internals
  - Removes `testAttachShouldAddEventIfItDoesNotExist` as it was irrelevant even before the changes.
  - Removes the `testTriggeringAnEventWithAnEmptyNameRaisesAnException` test, as this is no longer true; you can use any object as an event now.
  - Modifies a few tests where they were accessing internal structures that have changed, while keeping the same assertions in place.
- Adds `EventManagerWithProviderTest` to demonstrate usage when creating an `EventManager` via its `createUsingListenerProvider()` method.
- Updates `EventListenerIntrospectionTraitTest` to work with the new internals of the `EventManager`.

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
Basically, a counterpart to the current EventManagerAwareInterface, but for EventDispatcherInterface composition.

Also revises the Deprecations list, as we can keep EventManager as an EventDispatcherInterface implementation for 4.0.

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
- `EventInterface`
- `EventManagerInterface`
- `EventManagerAwareInterface`
- `EventManagerAwareTrait`
- `EventsCapableInterface` (points people to `EventDispatchingInterface`)
- `SharedEventManager`
- `SharedEventManagerInterface`
- `SharedEventsCapableInterface`
- `ListenerAggregateInterface` (points people to the `PrioritizedListenerAttachmentInterface`)
- `ListenerAggregateTrait` (points people to `ListenerSubscriberTrait`)
- `AbstractListenerAggregate` (points people to `AbstractListenerSubscriber` and/or `ListenerSubscriberTrait`)
- `ResponseCollection` (tells people to aggregate state/results in the event itself)
- `LazyListener` (points people to `ListenerProvider\LazyListener`)
- `LazyEventListener` (points people to `ListenerProvider\LazyListener`)
- `LazyListenerAggregate` (points people to `ListenerProvider\LazyListenerSubscriber`)
- `FilterChain` and `Filter` subnamespace (this should be done in a separate component)

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
We now require ^1.2 to ensure that PSR-11 interfaces are also present, allowing new classes to typehint only on the PSR-11 interfaces.
This will allow compatibility to continue as the 1.2 variants extend the PSR-11 interfaces.

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
Simplifies test definitions.

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
whitespace and long lines

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
weierophinney and others added 7 commits February 10, 2021 10:29
Notes all new features, major changes, and deprecations.

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
- Updates `PrioritizedListenerAttachmentInterface` to remove any BC breaks that would occur if `EventManager` implements it.
- Update to use non-deprecated assertions where possible.
- Fix any failing tests.
- Update checklist.

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
PHPUnit 8.5 + Prophecy 1.8 was resulting in errors when lowest version was used, due to differences in how prophecy did type comparisons.
Bumping versions corrects the issues.

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
Missed previously when rebasing

Signed-off-by: Matthew Weier O'Phinney <matthew@weierophinney.net>
@chris1312
Copy link

@weierophinney @Xerkus any thoughts when this can be released?

@weierophinney
Copy link
Member Author

@chris1312 I need to find time to document and create migration documentation (and/or scripts). It's a pretty big change, and I've been swamped with the migration to github actions, unfortunately.

@chris1312
Copy link

Roger that. Thanks for the update!

@weierophinney weierophinney removed this from the 3.4.0 milestone Sep 7, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Provide duck-typed PSR-14 implementation
3 participants