Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/quality-assurance.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php: [ '8.0', '8.1', '8.2', '8.3', '8.4', '8.5' ]
php: [ '8.1', '8.2', '8.3', '8.4', '8.5' ]
composer-flags: [ '' ]
phpunit-flags: [ '--coverage-text' ]
steps:
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
build
composer.lock
vendor
.phpunit.result.cache
67 changes: 54 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@

[![Latest Version on Packagist][ico-version]][link-packagist]
[![Software License][ico-license]](LICENSE.md)
[![Build Status][ico-travis]][link-travis]
[![Coverage Status][ico-scrutinizer]][link-scrutinizer]
[![Quality Score][ico-code-quality]][link-code-quality]
[![Total Downloads][ico-downloads]][link-downloads]


Expand All @@ -21,7 +18,57 @@ $ composer require fig/event-dispatcher-util

## Usage

Coming Soon!
### Aggregate provider

The `AggregateProvider` class is a `ListenerProvider` implementation that collects and concatenates other listener providers. It is a simple wrapper that `yield`s from a series of configured providers, in order.

```php
use Fig\EventDispatcher\AggregateProvider;

$provider1 = new YourProvider();
$provider2 = new SomeOtherProvider();

$provider = (new AggregateProvider())
->addProvider($provider1)
->addProvider($provider2)
;

$dispatcher = new SomeDispatcher($provider);
```

The aggregate provider will now return all the listeners from `YourProvider`, followed by all the listeners from `SomeOtherProvider`.

### Delegating provider

The `DelegatingProvider` is another multi-provider front-end. In this case, it can be configured such that certain event types will use one sub-provider, and others will use a different one. A given type can be configured with multiple sub-providers, and events will be matched using `instanceof` so that sub-classes of an event are also affected.

```php
use Fig\EventDispatcher\DelegatingProvider;

$requestProvider = new KernelListenerProvider();
$ormProvider = new OrmListenerProvider();

$defaultProvider = new SomeDefaultProvider();

// Assume HttpEvent and OrmEvent are interfaces for event classes.

$provider = (new DelegatingProvider($defaultProvider))
->addProvider($requestProvider, [HttpEvent::class])
->addProvider($ormProvider, [OrmEvent::class])
;

$dispatcher = new SomeDispatcher($provider);
```

Now, events sent to the dispatcher that implement `HttpEvent` will be passed along to `KernelListenerProvider` only, while those that implement `OrmEvent` will be passed along to `OrmListenerProvider` only. Any other events will be passed on to the default provider only.

`AggregateProvider` and `DelegateProvider` are fully compatible with each other, so either can use an instance of the other as one of its sub-providers.

### Parameter provider helpers

The `ParameterDeriverTrait` is a set of tools to help listener provider implementations with deriving the type of a callable's parameter, to know what type of event it is. PHP supports a wide variety of callable types, which are not always easy to disambiguate. The tools in this class help ease that process.

If you are not writing your own Provider registration mechanism, this trait will not be useful.

## Testing

Expand All @@ -35,21 +82,15 @@ Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

## Security

If you discover any security related issues, please email one of the current PHP-FIG Secretaries instead of using the issue tracker.
If you discover any security-related issues, please email one of the current PHP-FIG Secretaries instead of using the issue tracker.

## License

This package is released under the MIT license. Please see [License File](LICENSE.md) for more information.

[ico-version]: https://img.shields.io/packagist/v/Crell/Tukio.svg?style=flat-square
[ico-version]: https://img.shields.io/packagist/v/fig/event-dispatcher-util.svg?style=flat-square
[ico-license]: https://img.shields.io/badge/License-MIT-green.svg?style=flat-square
[ico-travis]: https://img.shields.io/travis/Crell/Tukio/master.svg?style=flat-square
[ico-scrutinizer]: https://img.shields.io/scrutinizer/coverage/g/Crell/Tukio.svg?style=flat-square
[ico-code-quality]: https://img.shields.io/scrutinizer/g/Crell/Tukio.svg?style=flat-square
[ico-downloads]: https://img.shields.io/packagist/dt/Crell/Tukio.svg?style=flat-square
[ico-downloads]: https://img.shields.io/packagist/dt/fig/event-dispatcher-util.svg?style=flat-square

[link-packagist]: https://packagist.org/packages/fig/event-dispatcher-util
[link-travis]: https://travis-ci.org/fig/event-dispatcher-util
[link-scrutinizer]: https://scrutinizer-ci.com/g/fig/event-dispatcher-util/code-structure
[link-code-quality]: https://scrutinizer-ci.com/g/fig/event-dispatcher-util
[link-downloads]: https://packagist.org/packages/fig/event-dispatcher-util
14 changes: 6 additions & 8 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@
"PSR-14",
"Events"
],
"repositories": [
{
"type": "vcs",
"url": "https://github.com/php-fig/event-dispatcher"
}
],
"homepage": "https://github.com/php-fig/event-dispatcher-util",
"license": "MIT",
"authors": [
Expand All @@ -27,7 +21,7 @@
}
],
"require": {
"php": ">=7.2",
"php": ">=8.1",
"psr/event-dispatcher": "^1.0"
},
"autoload": {
Expand All @@ -41,6 +35,10 @@
}
},
"require-dev": {
"phpunit/phpunit": "^8.5"
"phpunit/phpunit": "^10.5"
Comment thread
Crell marked this conversation as resolved.
},
"scripts": {
"test": "vendor/bin/phpunit",
"coverage": "vendor/bin/phpunit --coverage-text"
}
}
43 changes: 19 additions & 24 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,29 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
bootstrap="vendor/autoload.php"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
verbose="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
<testsuites>
<testsuite name="Event Dispatcher Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src/</directory>
</whitelist>
</filter>
<logging>
<log type="tap" target="build/report.tap"/>
<log type="junit" target="build/report.junit.xml"/>
<log type="coverage-html" target="build/coverage"/>
<log type="coverage-text" target="build/coverage.txt"/>
<log type="coverage-clover" target="build/logs/clover.xml"/>
</logging>
stopOnFailure="false"
cacheDirectory="build/phpunit.cache"
backupStaticProperties="false"
>
<testsuites>
<testsuite name="Event Dispatcher Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<logging>
<junit outputFile="build/report.junit.xml"/>
</logging>
<source>
<include>
<directory suffix=".php">src/</directory>
</include>
</source>
</phpunit>
7 changes: 5 additions & 2 deletions src/AggregateProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@
class AggregateProvider implements ListenerProviderInterface
{
/**
* @var array
* @var array<ListenerProviderInterface>
*/
protected $providers = [];
protected array $providers = [];
Comment thread
Crell marked this conversation as resolved.

/**
* @return iterable<callable>
*/
public function getListenersForEvent(object $event): iterable
{
/** @var ListenerProviderInterface $provider */
Expand Down
18 changes: 6 additions & 12 deletions src/DelegatingProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,14 @@
class DelegatingProvider implements ListenerProviderInterface
{
/**
* @var array
* @var array<class-string, array<ListenerProviderInterface>>
*
* An array of type to provider maps. The keys are class name strings.
* The values are an array of provider objects that should be called for that type.
*/
protected $providers =[];
protected array $providers = [];

/** @var ListenerProviderInterface|null */
protected $defaultProvider;

public function __construct(?ListenerProviderInterface $defaultProvider = null)
{
if ($defaultProvider) {
$this->defaultProvider = $defaultProvider;
}
}
public function __construct(protected ?ListenerProviderInterface $defaultProvider = null) {}

/**
* Adds a provider that will be deferred to for the specified Event types.
Expand Down Expand Up @@ -76,11 +68,13 @@ public function setDefaultProvider(ListenerProviderInterface $provider): self
return $this;
}

/**
* @return iterable<callable>
Comment thread
Crell marked this conversation as resolved.
*/
public function getListenersForEvent(object $event): iterable
{
foreach ($this->providers as $type => $providers) {
if ($event instanceof $type) {
/** @var ListenerProviderInterface $provider */
foreach ($providers as $provider) {
yield from $provider->getListenersForEvent($event);
}
Expand Down
5 changes: 2 additions & 3 deletions src/ParameterDeriverTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ trait ParameterDeriverTrait
*
* @param callable $callable
* The callable for which we want the parameter type.
* @return string
* @return class-string
* The class the parameter is type hinted on.
*/
protected function getParameterType($callable): string
Expand All @@ -35,8 +35,7 @@ protected function getParameterType($callable): string

$rType = $params[0]->getType();

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect there's a decent amount of refactoring and error handling that could be done here, but that's out of scope for this PR. I just wanted to hit some low-hanging fruit before tagging the new 8.1-specific release. If you want to try improving that, though, follow-on PRs are welcome.

(For that matter, it's possible we could simplify most of it with Closure::fromCallable()? Dunno.)

if (
$rType === null // no type declared
Comment thread
Crell marked this conversation as resolved.
|| !($rType instanceof \ReflectionNamedType) // type is union or intersection
!($rType instanceof \ReflectionNamedType) // type is missing, union, or intersection
|| $rType->isBuiltIn() // type is built-in, aka scalar/primitive
) {
throw new \InvalidArgumentException('Listeners must declare a single class/interface type they can accept.');
Expand Down
7 changes: 5 additions & 2 deletions src/TaggedProviderTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,17 @@ abstract protected function getListenersForTag(string $tag): iterable;
* Note that this method is called after getListenersForTag(), so
* tag-specific Listeners will always be invoked first.
*
* @return iterable
* @return iterable<callable>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @return iterable<callable>
* @return iterable<callable(object)>

* An ordered iterable of candidate Listeners. The key of each entry
* is the Event type the Listener is for. (It will also apply if the
* $type is a parent of the Event's type.) The value is an iterable
* of Listeners that apply to that type.
*/
abstract protected function getListenersForAllTags() : iterable;

/**
* @return iterable<callable>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @return iterable<callable>
* @template T
* @param T $event
* @return iterable<callable(T)>

*/
public function getListenersForEvent(object $event) : iterable
{
$eventType = $this->eventType();
Expand All @@ -101,7 +104,7 @@ public function getListenersForEvent(object $event) : iterable
* The Event to match against.
* @param iterable $listenerSet
* An iterable in the format returned by getListenersForTag()/getListenersForAllTags().
* @return iterable
* @return iterable<callable>
* An iterable of listeners to be called.
*/
protected function filterListenersForEvent(object $event, iterable $listenerSet): iterable
Expand Down
18 changes: 8 additions & 10 deletions tests/Tagged/TaggedProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,26 @@
use Fig\EventDispatcher\ParameterDeriverTrait;
use Fig\EventDispatcher\TaggedProviderTrait;
use Fig\EventDispatcher\CollectingEvent;
use PHPUnit\Framework\Attributes\Before;
use PHPUnit\Framework\TestCase;
use Psr\EventDispatcher\ListenerProviderInterface;

class TaggedProviderTest extends TestCase
{
protected ListenerProviderInterface $provider;

/** @var ListenerProviderInterface */
protected $provider;

public function setUp(): void
#[Before]
public function setupMockProvider(): void
{
parent::setUp();

$this->provider = new class implements ListenerProviderInterface {
use TaggedProviderTrait;
use ParameterDeriverTrait;

/** @var array */
protected $listeners = [];
/** @var array<callable> */
protected array $listeners = [];

/** @var array */
protected $all = [];
/** @var array<callable> */
protected array $all = [];


protected function eventType(): string
Expand Down
Loading