waffle-commons / event-dispatcher
Event Dispatcher component for Waffle framework.
Package info
github.com/waffle-commons/event-dispatcher
pkg:composer/waffle-commons/event-dispatcher
Requires
- php: ^8.5
- psr/event-dispatcher: ^1.0
- waffle-commons/contracts: 0.1.0-beta0
Requires (Dev)
- carthage-software/mago: ^1.0.0-rc
- cyclonedx/cyclonedx-php-composer: ^6.1
- php-mock/php-mock-phpunit: ^2.13
- phpunit/phpunit: ^12.0
- vimeo/psalm: ^6.13
This package is auto-updated.
Last update: 2026-05-16 15:49:50 UTC
README
Waffle Event Dispatcher Component
Release:
v0.1.0-beta0PSR Compliance: PSR-14 (Psr\EventDispatcher\EventDispatcherInterface,ListenerProviderInterface,StoppableEventInterface)
A minimal, attribute-driven PSR-14 dispatcher. The dispatcher itself is final readonly and stateless; the listener provider stores the listener map and supports priority ordering and #[AsEventListener] attribute discovery.
๐ฆ Installation
composer require waffle-commons/event-dispatcher
๐งฑ Surface
| Class | Role |
|---|---|
Waffle\Commons\EventDispatcher\Dispatcher\EventDispatcher |
final readonly PSR-14 dispatcher. Walks listeners, respects StoppableEventInterface. |
Waffle\Commons\EventDispatcher\Provider\ListenerProvider |
Listener registry. Manual registration via addListener(), or attribute scanning via register($object). |
Waffle\Commons\EventDispatcher\Attribute\AsEventListener |
PHP 8 attribute marking a class or method as a listener. |
Waffle\Commons\EventDispatcher\Event\AbstractStoppableEvent |
Convenience base implementing StoppableEventInterface. |
๐ Manual registration
use Waffle\Commons\EventDispatcher\Dispatcher\EventDispatcher; use Waffle\Commons\EventDispatcher\Provider\ListenerProvider; $provider = new ListenerProvider(); $provider->addListener(UserRegistered::class, function (UserRegistered $event): void { // โฆ }, priority: 100); // higher priority = earlier $dispatcher = new EventDispatcher($provider); $event = $dispatcher->dispatch(new UserRegistered($userId));
๐ท๏ธ Attribute-driven registration (#[AsEventListener])
The attribute is declared as:
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_METHOD)] final readonly class AsEventListener { public function __construct( public ?string $event = null, public int $priority = 0, ) {} }
Method-level โ event class resolved from the parameter type-hint
final class AuditListener { #[AsEventListener(priority: 50)] public function onUserRegistered(UserRegistered $event): void { // resolved automatically from the parameter type } } $provider->register(new AuditListener());
Class-level โ first public non-constructor method is the handler
#[AsEventListener(event: UserRegistered::class, priority: 50)] final class WelcomeMailer { public function send(UserRegistered $event): void { /* โฆ */ } } $provider->register(new WelcomeMailer());
๐ Stoppable events
use Waffle\Commons\EventDispatcher\Event\AbstractStoppableEvent; final class CancellableJob extends AbstractStoppableEvent { public function __construct(public readonly string $jobId) {} } $provider->addListener(CancellableJob::class, function (CancellableJob $e): void { if ($shouldCancel) { $e->stopPropagation(); } });
The dispatcher honours isPropagationStopped() and breaks out of the listener loop.
๐ PHP 8.5 features used
final readonly class EventDispatcherโ the dispatcher itself is immutable.- Constructor property promotion with explicit visibility on listeners.
- Typed properties + parameters throughout.
- Inheritance walks via native
get_parent_class()(not reflection caches), so listener resolution against parent event types isO(depth)without warm-up cost.
๐งช Testing
docker exec -w /waffle-commons/event-dispatcher waffle-dev composer tests
๐ License
MIT โ see LICENSE.md.