Tukio is a complete and robust implementation of the PSR-14 Event Dispatcher specification

Related tags

API Tukio
Overview

Tukio

Latest Version on Packagist Software License Total Downloads

Tukio is a complete and robust implementation of the PSR-14 Event Dispatcher specification. It supports normal and debug Event Dispatchers, both runtime and compiled Providers, complex ordering of Listeners, and attribute-based registration on PHP 8.

"Tukio" is the Swahili word for "Event".

Install

Via Composer

$ composer require crell/tukio

PSR-14 Usage

PSR-14 consists of two key components: A Dispatcher and a Provider. A Dispatcher is what a client library will compose and then pass Events objects to. A Provider is what a framework will give to a Dispatcher to match the Event to Listeners. Tukio includes multiple implementations of both.

The general structure of using any PSR-14-compliant implementation is:

$provider = new SomeProvider();
// Do some sort of setup on $provider so that it knows what Listeners it should match to what Events.
// This could be pretty much anything depending on the implementation.

$dispatcher = new SomeDispatcher($provider);

// Pass $dispatcher to some client code somewhere, and then:

$thingHappened = new ThingHappened($thing);

$dispatcher->dispatch($thingHappened);

Note that dispatch() will return $thingHappened as well, so if Listeners are expected to add data to it then you can chain a method call on it if desired:

$reactions = $dispatcher->dispatch(new ThingHappened($thing))->getReactions();

In practice most of that will be handled through a Dependency Injection Container, but there's no requirement that it do so.

Dispatchers

Tukio includes two Dispatchers: Dispatcher is the standard implementation, which should be used 95% of the time. It can optionally take a PSR-3 Logger, in which case it will log a warning on any exception thrown from a Listener.

The second is DebugEventDispatcher, which like the names says is useful for debugging. It logs every Event that is passed and then delegates to a composed dispatcher (such as Dispatcher) to actually dispatch the Event.

For example:

use Crell\Tukio\Dispatcher;
use Crell\Tukio\DebugEventDispatcher;

$provider = new SomeProvider();

$logger = new SomeLogger();

$dispatcher = new Dispatcher($provider, $logger);

$debugDispatcher = new DebugEventDispatcher($dispatcher, $logger);

// Now pass $debugDispatcher around and use it like any other dispatcher.

Providers

Tukio includes multiple options for building Providers, and it's encouraged that you combine them with the generic ones included in the fig/event-dispatcher-util library provided by the FIG.

Which Provider or Providers you use depends on your use case. All of them are valid for certain use cases.

OrderedListenerProvider

As the name implies, OrderedListenerProvider is all about ordering. Users can explicitly register Listeners on it that will be matched against an Event based on its type.

If no order is specified, then the order that Listeners will be returned is undefined (although in practice order-added is what it will most likely be). That makes the degenerate case super-easy:

use Crell\Tukio\OrderedListenerProvider;

class StuffHappened {}

class SpecificStuffHappened extends StuffHappened {}

function handleStuff(StuffHappened $stuff) { ... }


$provider = new OrderedListenerProvider();

$provider->addListener(function(SpecificStuffHappened) {
    // ...  
});

$provider->addListener('handleStuff');

That adds two Listeners to the Provider; one anonymous function and one named function. The anonymous function will be called for any SpecificStuffHappened event. The named function will be called for any StuffHappened or SpecificStuffHappened event. And the user doesn't really care which one happens first (which is the typical case).

Ordering listeners

However, the user can also be picky about the order in which Listeners will fire. The most common approach for that today is via an integral "priority", and Tukio offers that:

use Crell\Tukio\OrderedListenerProvider;

$provider = new OrderedListenerProvider();

$provider->addListener(function(SpecificStuffHappened) {
    // ...  
}, 10);

$provider->addListener('handleStuff', 20);

Now, the named function Listener will get called before the anonymous function does. (Higher priority number comes first, and negative numbers are totally legal.) If two listeners have the same priority then their order relative to each other is undefined.

Sometimes, though, you may not know the priority of another Listener, but it's important your Listener happen before or after it. For that we need to add a new concept: IDs. Every Listener has an ID, which can be provided when the Listener is added or will be auto-generated if not. The auto-generated value is predictable (the name of a function, the class-and-method of an object method, etc.), so in most cases it's not necessary to read the return value of addListener() although that is slightly more robust.

addListener('handleStuff'); // This Listener will get called before handleStuff does. If you want to specify an ID // you can, since anonymous functions just get a random string as their generated ID. $provider->addListenerBefore($id, function(SpecificStuffHappened) { // ... }, 'my_specifics');">
use Crell\Tukio\OrderedListenerProvider;

$provider = new OrderedListenerProvider();

// The ID will be "handleStuff", unless there is such an ID already,
//in which case it would be "handleStuff-1" or similar.
$id = $provider->addListener('handleStuff');

// This Listener will get called before handleStuff does. If you want to specify an ID
// you can, since anonymous functions just get a random string as their generated ID.
$provider->addListenerBefore($id, function(SpecificStuffHappened) {
    // ...  
}, 'my_specifics');

Here, the priority of handleStuff is undefined; the user doesn't care when it gets called. However, the anonymous function, if it should get called at all, will always get called after handleStuff does. It's possible that some other Listener could also be called in between the two, but one will always happen after the other.

The full API for those three methods is:

public function addListener(callable $listener, $priority = 0, string $id = null, string $type = null): string;

public function addListenerBefore(string $before, callable $listener, string $id = null, string $type = null): string;

public function addListenerAfter(string $after, callable $listener, string $id = null, string $type = null): string;

All three can register any callable as a Listener and return an ID. If desired the $type parameter allows a user to specify the Event type that the Listener is for if different than the type declaration in the function. For example, if the Listener doesn't have a type declaration or should only apply to some parent class of what it's type declaration is. (That's a rare edge case, which is why it's the last parameter.)

Service Listeners

Often, though, Listeners are themselves methods of objects that should not be instantiated until and unless needed. That's exactly what a Dependency Injection Container allows, and OrderedListenerProvider fully supports those, called "Service Listeners". They work almost exactly the same, except you specify a service and method name:

public function addListenerService(string $serviceName, string $methodName, string $type, $priority = 0, string $id = null): string;

public function addListenerServiceBefore(string $before, string $serviceName, string $methodName, string $type, string $id = null): string;

public function addListenerServiceAfter(string $after, string $serviceName, string $methodName, string $type, string $id = null) : string;

All three take a service name and method name to identify a Listener. However, since the callable itself doesn't exist yet it's not possible to derive the Event it should listen to through reflection. It must be specified explicitly. All three also return an ID, so the same "priority-or-before/after" functionality is supported.

The services themselves can be from any PSR-11-compatible Container.

use Crell\Tukio\OrderedListenerProvider;

$container = new SomePsr11Container();
// Configure the container somehow.

$provider = new OrderedListenerProvider($container);

$provider->addListenerService('some_service', 'methodA', ThingHappened::class);

$id = $provider->addListenerServiceBefore('some_service-methodA', 'some_service', 'methodB', SpecificThingHappened::class);

$provider->addListenerServiceAfter($id, 'some_other_service', 'methodC', SpecificThingHappened::class);

In this example, we assume that $container has two services defined: some_service and some_other_service. (Creative, I know.) We then register three Listeners: Two of them are methods on some_service, the other on some_other_service. Both services will be requested from the container as needed, so won't be instantiated until the Listener method is about to be called.

Of note, the methodB Listener is referencing the methodA listener by an explict ID. The generated ID is as noted predictable, so in most cases you don't need to use the return value. The return value is the more robust and reliable option, though, as if the requested ID is already in-use a new one will be generated.

Subscribers

As in the last example, it's quite common to have multiple Listeners in a single service. In fact, it's common to have a service that is nothing but listeners. Tukio calls those "Subscribers" (a name openly and unashamedly borrowed from Symfony), and has specific support for them.

The basic case works like this:

use Crell\Tukio\OrderedListenerProvider;

class Subscriber
{
    public function onThingsHappening(ThingHappened $event) : void { ... }

    public function onSpecialEvent(SpecificThingHappened $event) : void { ... }
}

$container = new SomePsr11Container();
// Configure the container so that the service 'listeners' is an instance of Subscriber above.

$provider = new OrderedListenerProvider($container);


$provider->addSubscriber(Subscriber::class, 'listeners');

That's it! Because we don't know what the class of the service is it will need to be specified explicitly, but the rest is automatic. Any public method whose name begins with "on" will be registered as a listener, and the Event type it is for will be derived from reflection, while the rest of the class is ignored. There is no limit to how many listeners can be added this way. The method names can be anything that makes sense in context, so make it descriptive. And because the service is pulled on-demand from the container it will only be instantiated once, and not before it's needed. That's the ideal.

Sometimes, though, you want to order the Listeners in the Subscriber, or need to use a different naming convention for the Listener method. For that case, your Subscriber class can implement an extra interface that allows for manual registration:

use Crell\Tukio\OrderedListenerProvider;
use Crell\Tukio\SubscriberInterface;

class Subscriber implements SubscriberInterface
{
    public function onThingsHappening(ThingHappened $event) : void { ... }

    public function onSpecialEvent(SpecificThingHappened $event) : void { ... }

    public function somethingElse(ThingHappened $event) : void { ... }

    public static function registerListeners(ListenerProxy $proxy) : void
    {
        $id = $proxy->addListener('somethingElse', 10);
        $proxy->addListenerAfter($id, 'onSpecialEvent');
    }
}

$container = new SomePsr11Container();
// Configure the container so that the service 'listeners' is an instance of Subscriber above.

$provider = new OrderedListenerProvider($container);

$provider->addSubscriber(Subscriber::class, 'listeners');

As before, onThingsHappen() will be registered automatically. However, somethingElse() will also be registered as a Listener with a priority of 10, and onSpecialEvent() will be registered to fire after it.

In practice, using Subscribers is the most robust way to register Listeners in a production system and so is the recommended approach. However, all approaches have their uses and can be used as desired.

If you are using PHP 8.0 or later, you can use attributes to register your subscriber methods instead. That is preferred, in fact. See the section below.

Compiled Provider

All of that registration and ordering logic is powerful, and it's surprisingly fast in practice. What's even faster, though, is not having to re-register on every request. For that, Tukio offers a compiled provider option.

The compiled provider comes in three parts: ProviderBuilder, ProviderCompiler, and a generated provider class. ProviderBuilder, as the name implies, is an object that allows you to build up a set of Listeners that will make up a Provider. They work exactly the same as on OrderedListenerProvider, and in fact it exposes the same OrderedProviderInterface.

ProviderCompiler then takes a builder object and writes a new PHP class to a provided stream (presumably a file on disk) that matches the definitions in the builder. That built Provider is fixed; it cannot be modified and no new Listeners can be added to it, but all of the ordering and sorting has already been done, making it notably faster (to say nothing of skipping the registration process itself).

Let's see it in action:

use Crell\Tukio\ProviderBuilder;
use Crell\Tukio\ProviderCompiler;

$builder = new ProviderBuilder();

$builder->addListener('listenerA', 100);
$builder->addListenerAfter('listenerA', 'listenerB');
$builder->addListener([Listen::class, 'listen']);
$builder->addListenerService('listeners', 'listen', CollectingEvent::class);
$builder->addSubscriber('subscriber', Subscriber::class);

$compiler = new ProviderCompiler();

// Write the generated compiler out to a file.
$filename = 'MyCompiledProvider.php';
$out = fopen($filename, 'w');

// Here's the magic:
$compiler->compile($builder, $out, 'MyCompiledProvider', '\\Name\\Space\\Of\\My\\App');

fclose($out);

$builder can do anything that OrderedListenerProvider can do, except that it only supports statically-defined Listeners. That means it does not support anonymous functions or methods of an object, but it will still handle functions, static methods, services, and subscribers just fine. In practice when using a compiled container you will most likely want to use almost entirely service listeners and subscribers, since you'll most likely be using it with a container.

That gives you a file on disk named MyCompiledProvider.php, which contains Name\Space\Of\My\App\MyCompiledProvider. (Name it something logical for you.) At runtime, then, do this:

addService('D', new ListenService()); include('MyCompiledProvider.php'); $provider = new Name\Space\Of\My\App\MyCompiledProvider($container);">
// Configure the container such that it has a service "listeners"
// and another named "subscriber".

$container = new Psr11Container();
$container->addService('D', new ListenService());

include('MyCompiledProvider.php');

$provider = new Name\Space\Of\My\App\MyCompiledProvider($container);

And boom! $provider is now a fully functional Provider you can pass to a Dispatcher. It will work just like any other, but faster.

But what if you want to have most of your listeners pre-registered, but have some that you add conditionally at runtime? Have a look at the FIG's AggregateProvider, and combine your compiled Provider with an instance of OrderedListenerProvider.

Attribute-based registration

If you are using PHP 8.0 or later, Tukio fully supports attributes as a means of registration for both OrderedListenerProvider and ProviderBuilder. There are four relevant attributes: Listener, ListenerPriority, ListenerBefore, and ListenerAfter. All can be used with sequential parameters or named parameters. In most cases, named parameters will be more self-documenting. All attributes are valid only on functions and methods.

  • Listener declares a callable a listener and optionally sets the id and type: #[Listener(id: 'a_listener', type: SomeClass`)].
  • ListenerPriority has a required priority parameter, and optional id and type: #[ListenerPriority(5)]or#[ListeenPriority(priority: 3, id: "a_listener")]`.
  • ListenerBefore has a required before parameter, and optional id and type: #[ListenerBefore('other_listener')]or#[ListenerBefore(before: 'other_listener', id: "a_listener")]`.
  • ListenerAfter has a required after parameter, and optional id and type: #[ListenerAfter('other_listener')]or#[ListenerAfter(after: 'other_listener', id: "a_listener")]`.

Each attribute matches a corresponding method, and the values in the attribute will be used as if they were passed directly to the addListener*() method. If a value is passed directly to addListener() and specified in the attribute, the directly-passed value takes precedence.

If you call addListenerBefore()/addListenerAfter(), the before/after/priority attribute parameter is ignored in favor of the method's standard behavior. That is, addListenerBefore(), if called with a function that has a #[ListenerAfter] attribute, will still add the listener "before" the specified other listener.

There are two common use cases for attributes: One, using just #[Listener] to define a custom id or type and then calling the appropriate add* method as normal. The other is on Subscribers, where attributes completely eliminate the need for the registerListeners() method. If you're on PHP 8.0, please use attributes instead of registerListeners(). It's not deprecated yet, but it may end up deprecated sometime in the future.

For example:

add('C'); } // Registers, after listener "a" above. #[ListenerAfter(after: 'a')] public function onD(CollectingEvent $event) : void { $event->add('D'); } // This still self-registers because of the name. public function onE(CollectingEvent $event) : void { $event->add('E'); } // Registers, with a given priority despite its non-standard name. #[ListenerPriority(priority: -5)] public function notNormalName(CollectingEvent $event) : void { $event->add('F'); } // No attribute, non-standard name, this method is not registered. public function ignoredMethodThatDoesNothing() : void { throw new \Exception('What are you doing here?'); } }">
class SomeSubscriber
{
    // Registers, with a custom ID.
    #[Listener(id: 'a')]
    public function onA(CollectingEvent $event) : void
    {
        $event->add('A');
    }

    // Registers, with a custom priority.
    #[ListenerPriority(priority: 5)]
    public function onB(CollectingEvent $event) : void
    {
        $event->add('B');
    }

    // Registers, before listener "a" above.
    #[ListenerBefore(before: 'a')]
    public function onC(CollectingEvent $event) : void
    {
        $event->add('C');
    }

    // Registers, after listener "a" above.
    #[ListenerAfter(after: 'a')]
    public function onD(CollectingEvent $event) : void
    {
        $event->add('D');
    }

    // This still self-registers because of the name.
    public function onE(CollectingEvent $event) : void
    {
        $event->add('E');
    }

    // Registers, with a given priority despite its non-standard name.
    #[ListenerPriority(priority: -5)]
    public function notNormalName(CollectingEvent $event) : void
    {
        $event->add('F');
    }

    // No attribute, non-standard name, this method is not registered.
    public function ignoredMethodThatDoesNothing() : void
    {
        throw new \Exception('What are you doing here?');
    }
}

CallbackProvider

The third option Tukio provides is a CallbackProvider, which takes an entirely different approach. In this case, the Provider works only on events that have a CallbackEventInterface. The use case is for Events that are carrying some other object, which itself has methods on it that should be called at certain times. Think lifecycle callbacks for a domain object, for example.

To see it in action, we'll use an example straight out of Tukio's test suite:

use Crell\Tukio\CallbackEventInterface;
use Crell\Tukio\CallbackProvider;

class LifecycleEvent implements CallbackEventInterface
{
    protected $entity;

    public function __construct(FakeEntity $entity)
    {
        $this->entity = $entity;
    }

    public function getSubject() : object
    {
        return $this->entity;
    }
}

class LoadEvent extends LifecycleEvent {}

class SaveEvent extends LifecycleEvent {}

class FakeEntity
{

    public function load(LoadEvent $event) : void { ... }

    public function save(SaveEvent $event) : void { ... }

    public function stuff(StuffEvent $event) : void { ... }

    public function all(LifecycleEvent $event) : void { ... }
}

$provider = new CallbackProvider();

$entity = new FakeEntity();

$provider->addCallbackMethod(LoadEvent::class, 'load');
$provider->addCallbackMethod(SaveEvent::class, 'save');
$provider->addCallbackMethod(LifecycleEvent::class, 'all');

$event = new LoadEvent($entity);

$provider->getListenersForEvent($event);

In this example, the provider is configured not with Listeners but with method names that correspond to Events. Those methods are methods on the "subject" object. The Provider will now return callables for [$entity, 'load'] and [$entity, 'all'] when called with a LoadEvent. That allows a domain object itself to have Listeners on it that will get called at the appropriate time.

Change log

Please see CHANGELOG for more information on what has changed recently.

Testing

$ composer test

Contributing

Please see CONTRIBUTING and CODE_OF_CONDUCT for details.

Security

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

Credits

License

The Lesser GPL version 3 or later. Please see License File for more information.

Comments
  • Add type errors to OrderedListenerProvider

    Add type errors to OrderedListenerProvider

    Adds an exception type to facilitate type-related errors, integrates it into the OrderedListenerProvider, and provides tests for its functionality.

    Description

    The new Exception type (InvalidTypeException) supports verbose source information, aiding in tracking down listener problems. For example, where formerly an error would either be Fatal or the vague InvalidArgumentException "Listeners must declare an object type they can accept.", we now receive Function does not specify a valid type (<qualified\class>::<method>) for class methods, or Function does not specify a valid type (<path>\<filename>:<line>) for standalone or anonymous functions.

    Motivation and context

    Facilitating listener registration with more meaningful errors. See discussion here.

    Concerns

    All locations that now throw InvalidTypeExceptions either used to throw an InvalidArgumentException or threw Fatal errors. Per discussion here, changing exception types is considered non-breaking in this context, but it's worth noting that use cases designed specifically around catching InvalidArgumentExceptions during registration may experience changed behavior.

    Testing

    The new Exception type has test coverage anywhere it is invoked, covering automatic service registration, manual service registration, and manual listener registration.

    opened by Niflthaena 7
  • Add support for PHP 8.1, and remove support for 7.2 and 7.3

    Add support for PHP 8.1, and remove support for 7.2 and 7.3

    Description

    Add support for PHP 8.1, and remove support for PHP 7.2 and 7.3.

    Dropping 7.2 support is the easiest way to upgrade to PHPUnit 9.5 which supports >=7.3 and also 8.1. Older version of PHPUnit 8.x does access the $GLOBALS which is no longer supported in PHP 8.1.

    Types of changes

    • [ ] Bug fix (non-breaking change which fixes an issue)
    • [ ] New feature (non-breaking change which adds functionality)
    • [x] Breaking change (fix or feature that would cause existing functionality to change)

    Checklist:

    Please, please, please, don't send your pull request until all of the boxes are ticked. Once your pull request is created, it will trigger a build on our continuous integration server to make sure your tests and code style pass.

    • [x] I have read the CONTRIBUTING document.
    • [x] My pull request addresses exactly one patch/feature.
    • [x] I have created a branch for this patch/feature.
    • [X] Each individual commit in the pull request is meaningful.
    • [ ] I have added tests to cover my changes.
    • [ ] If my change requires a change to the documentation, I have updated it accordingly.

    If you're unsure about any of these, don't hesitate to ask. We're here to help!

    opened by martinssipenko 6
  • Compatibility with psr/container 2.0

    Compatibility with psr/container 2.0

    Description

    Provides compatibility with psr/container v2.0

    Motivation and context

    This will allow tukio to be used alongside libraries that require psr/container 2.0, and me to test v2.0 of my PHP extension against your repo.

    How has this been tested?

    The config and require-dev sections in composer.json as-is prevent testing psr/container v2.0. I removed those sections (except for phpunit) and ran the unit tests manually (after running composer update) on PHP 7.3 and PHP 8.0.

    If my understanding is correct, CI will not test the library with psr/container 2.0 due the the presence of the config section and nunomaduro/phpinsights

    I looked into submitting a similar patch to nunomaduro/phpinsights but the same issue exists in some of their dependencies, and that's about as far down the rabbit hole as I want to go right now.

    I would personally probably not merge this as-is without either a new release from or removing nunomaduro/phpinsights, or maybe installing it globally in CI if it is an executable.

    Once the above issue is resolved, there could be a new PHP 8.0 build added in CI with composer update --prefer-lowest to test v1 of psr/container on PHP 8.0 (I believe this is what this does).

    Checklist:

    • [x] I have read the CONTRIBUTING document.
    • [x] My pull request addresses exactly one patch/feature.
    • [x] I have created a branch for this patch/feature.
    • [x] Each individual commit in the pull request is meaningful.
    • [ ] I have added tests to cover my changes.
    • [x] If my change requires a change to the documentation, I have updated it accordingly.
    opened by jbboehr 6
  • Subscriber method registration bug

    Subscriber method registration bug

    Detailed description

    Subscriber registration, based on the documentation, sounds very useful and easy to integrate. Its implementation is decidedly less so. Specifically, the documentation says that any method on a Subscriber whose name begins with 'on' is registered. However, the code is written such that any method containing the text 'on' is caught. (for example, "encodeJson".) Per src/OrderedListenerProvider.php:131: if (!in_array($methodName, $proxy->getRegisteredMethods()) && strpos($methodName, 'on') !== false) {

    To make matters worse, if the first parameter of any such method does not have a typehint, auto-registration immediately throws a fatal error, rather than simply failing to register. Per src/OrderedListenerProvider.php:133: $type = $params[0]->getType()->getName(); getType() returns null, getName() throws a 'member function on null' fatal error.

    Context

    When integrating with an existing codebase, it would be nice to be able to use some convention (or annotation) to turn manager classes into subscribers. Instead, auto-registration has the above issue, leaving manual registration as the alternative, or refactoring the classes that wish to subscribe to avoid placing an 'n' after an 'o'.

    Test concerns

    The current automated tests have methods that are not to be registered, but coincidentally these methods do not include the text 'on' anywhere in their name. A test case such "public function doNotAttach_onRegistration" would test for this, and have caught the issue.

    Possible implementation

    A potential fix would be simply comparing strpos($methodName, 'on') === 0.

    opened by Niflthaena 6
  • Move `phpstan/phpstan` to `require-dev`

    Move `phpstan/phpstan` to `require-dev`

    Description

    Move phpstan/phpstan to require-dev so that it's not installed in projects using this package.

    Types of changes

    What types of changes does your code introduce? Put an x in all the boxes that apply:

    • [X] Bug fix (non-breaking change which fixes an issue)
    • [ ] New feature (non-breaking change which adds functionality)
    • [ ] Breaking change (fix or feature that would cause existing functionality to change)

    Checklist:

    • [X] I have read the CONTRIBUTING document.
    • [X] My pull request addresses exactly one patch/feature.
    • [X] I have created a branch for this patch/feature.
    • [X] Each individual commit in the pull request is meaningful.
    • [ ] ~I have added tests to cover my changes.~
    • [ ] ~If my change requires a change to the documentation, I have updated it accordingly.~
    opened by martinssipenko 4
  • Compatibility with psr/log 2.0 and 3.0

    Compatibility with psr/log 2.0 and 3.0

    Description

    Provides compatibility with psr/log v2.0 and v3.0

    Motivation and context

    This will allow Tukio to be used alongside libraries that require psr/log v2.0 and/or v3.0.

    How has this been tested?

    I think CI tries to install stable and lowest dependancies.

    Checklist:

    • [x] I have read the CONTRIBUTING document.
    • [x] My pull request addresses exactly one patch/feature.
    • [x] I have created a branch for this patch/feature.
    • [x] Each individual commit in the pull request is meaningful.
    • [x] I have added tests to cover my changes.
    • [x] If my change requires a change to the documentation, I have updated it accordingly.
    opened by martinssipenko 2
  • Add Event Dispatcher PSR in

    Add Event Dispatcher PSR in "provide" section of the Composer schema

    As described in Composer documentation, the "provide" section allows to define which other packages are provided by this library.

    The Event Dispatcher PSR has been added in this section, it will for instance add more visibility for this package on Packagist page.

    opened by romm 1
  • Fix subscriber method name registration.

    Fix subscriber method name registration.

    Fixes the issue raised here.

    Updated subscriber registration to require the 'on' in 'onSomething' to occur only at the start of the method name, such that (as an example) onEvent is caught, but handleJson is ignored.

    This was tested by updating the MockSubscriber test class to include a method that should be ignored, but has 'on' later in the name. It yields 2 testing errors on master, and none after the updates from this PR.

    opened by Niflthaena 1
  • Missing Psr\EventDispatcher\EventInterface ?

    Missing Psr\EventDispatcher\EventInterface ?

    Hi @Crell ,

    I have been looking at the event-dispatcher.

    Missing Psr\EventDispatcher\EventInterface at https://github.com/php-fig/event-dispatcher/tree/8380006c4192e22528ce40c3a56b81590707128a/src

    But the interface is used in many places. Eg : https://github.com/php-fig/event-dispatcher-util/blob/8bb440229e615a06a5e85e1cdf8e3c99abaa0a53/src/AggregateProvider.php#L7

    The interface is mentioned at https://github.com/php-fig/fig-standards/blob/d8693cde3fae95ae3127649fe5482384e819e0e0/proposed/event-dispatcher.md#interfaces

    opened by harikt 1
  • Use Doctrine annotations for registration

    Use Doctrine annotations for registration

    Leaving this as a note to myself...

    To effectively register subscribers and provide alternate IDs we probably need to go ahead and just use Doctrine annotations. While it might be fun to write an annotation parser it's probably out of scope, but it's another reason that PHP needs native annotations.

    opened by Crell 1
  • Run PHP Insights

    Run PHP Insights

    Some cleanup using PHP Insights. Other things it complains about I need to check into later, or disagree with and need to configure it to not whine about. :smile:

    opened by Crell 0
Owner
Larry Garfield
Staff Engineer for @TYPO3. PHP fan. Functional programming enthusiast. Anything worth doing is worth doing well.
Larry Garfield
A PHP implementation of the GraphQL specification based on the JavaScript reference implementation

GraphQL This is a PHP implementation of the GraphQL specification based on the JavaScript reference implementation. Related projects DateTime scalar R

Digia 219 Nov 16, 2022
Generates OpenApi specification for Laravel, Lumen or Dingo using a configuration array and cebe/php-openapi

OpenApi Generator for Laravel, Lumen and Dingo. About The openapi-gen package provides a convenient way to create OpenApi specifications for Laravel,

Jean Dormehl 5 Jan 25, 2022
Best resources restful api for developers (with JSON:API standar specification design)

List API Best resources restful api for developers (with JSON:API standar specification design). API Resource Endpoint Name Resource Description Al Qu

Noval 2 Jan 18, 2022
A robust JSON decoder/encoder with support for schema validation.

A robust wrapper for json_encode()/json_decode() that normalizes their behavior across PHP versions, throws meaningful exceptions and supports schema validation by default.

Bernhard Schussek 356 Dec 21, 2022
This bundle provides tools to build a complete GraphQL server in your Symfony App.

OverblogGraphQLBundle This Symfony bundle provides integration of GraphQL using webonyx/graphql-php and GraphQL Relay. It also supports: batching with

Webedia - Overblog 720 Dec 25, 2022
A complete Notion SDK for PHP developers.

notion-sdk-php A complete Notion SDK for PHP developers. Installation composer require mariosimao/notion-php Getting started A Notion token will be n

Mario Simão 77 Nov 29, 2022
A based PSR-15 microframework that also sets maximum flexibility with minimum complexity and easy replaceability of the individual components, but also of the framework.

chubbyphp-framework Description A based PSR-15 microframework that also sets maximum flexibility with minimum complexity and easy replaceability of th

chubbyphp 106 Dec 9, 2022
The efficient and elegant, PSR-7 compliant JSON:API 1.1 client library for PHP

Woohoo Labs. Yang Woohoo Labs. Yang is a PHP framework which helps you to communicate with JSON:API servers more easily. Table of Contents Introductio

Woohoo Labs. 160 Oct 16, 2022
OpenAPI(v3) Validators for Symfony http-foundation, using `league/openapi-psr7-validator` and `symfony/psr-http-message-bridge`.

openapi-http-foundation-validator OpenAPI(v3) Validators for Symfony http-foundation, using league/openapi-psr7-validator and symfony/psr-http-message

n1215 2 Nov 19, 2021
PSR-7 middleware foundation for building and dispatching middleware pipelines

laminas-stratigility From "Strata", Latin for "layer", and "agility". This package supersedes and replaces phly/conduit. Stratigility is a port of Sen

Laminas Project 47 Dec 22, 2022
Disable Google's FLoC with help of PSR-15 middleware

Disable Google's FLoC with PSR-15 middleware This package will help you disable Google's FLoC. Installation You can install the package via composer:

P7V 9 Dec 14, 2022
It validates PSR-7 messages (HTTP request/response) against OpenAPI specifications

OpenAPI PSR-7 Message (HTTP Request/Response) Validator This package can validate PSR-7 messages against OpenAPI (3.0.x) specifications expressed in Y

The League of Extraordinary Packages 421 Jan 3, 2023
PSR-15 middleware to geolocate the client using the ip address

middlewares/geolocation ![SensioLabs Insight][ico-sensiolabs] Middleware to geolocate the client using the ip address and Geocoder and save the result

Middlewares 10 Mar 22, 2022
A PSR-15 middleware adapter for react/http

A PSR-15 middleware adapter for react/http Wraps PSR-15 middleware into coroutines using RecoilPHP making them usable within react/http as middleware.

Friends of ReactPHP 22 Nov 12, 2022
PSR-15 middleware to use Whoops as error handler

middlewares/whoops Middleware to use Whoops as error handler. Requirements PHP >= 7.2 A PSR-7 http library A PSR-15 middleware dispatcher Installation

Middlewares 31 Jun 23, 2022
A PSR-15 middleware to handle content negotiation

Content negotiation middleware Motivation Packages like middlewares/negotiation do a very good job to detect the correct content type based on the Acc

Luís Cobucci 47 Nov 16, 2022
PSR-15 compatible middleware for Whoops, the pretty error handler

PSR-15 middleware for Whoops A PSR-15 compatible middleware for Whoops, the fantastic pretty error handler for PHP. Installation You can install the l

Franz Liedke 25 May 20, 2022
An internal redirect mechanism for PSR-15 middleware stacks

HTTP Request Forwarder The aim of this library is to make it possible to pass the HTTP request to another handler, creating a so-called internal redir

Zoltan Kovago 0 Jul 27, 2022
Simple, extensible and powerful enumeration implementation for Laravel.

About Laravel Enum Simple, extensible and powerful enumeration implementation for Laravel. Enum key value pairs as class constants Full featured suite

Ben Sampson 1.8k Dec 23, 2022