Dispatcher
The Dispatcher plugin provides low-level event dispatching using the League Event library. It serves as the foundation for the EventBus plugin.
Two ports are available:
- DispatcherInterface (PSR-14
EventDispatcherInterface) - dispatches events to registered listeners - ListenerRegistryInterface (PSR-14
ListenerProviderInterface) - registers and provides listeners
Adapters:
- LeagueDispatcher wraps League Event for event dispatching
- LeagueListenerRegistry wraps League Event for listener registration
PSR-14 Compatibility
The plugin fully implements PSR-14 through two separate interfaces:
| Phexium Port | PSR-14 Interface | Purpose |
|---|---|---|
DispatcherInterface | EventDispatcherInterface | Dispatch events |
ListenerRegistryInterface | ListenerProviderInterface | Register and provide listeners |
This separation follows PSR-14's design philosophy where dispatching and listener provision are distinct concerns. Code can type-hint either the Phexium ports or the PSR-14 interfaces directly.
Why Use It
The Dispatcher is a low-level abstraction used internally by the EventBus. Application code should typically use EventBus for domain events. The Dispatcher is useful for framework internals or when generic event dispatching is needed without domain event semantics.
Usage
The EventBus delegates to both ports internally:
// Dispatching events
$this->dispatcher->dispatch($event);
// Registering listeners
$this->listenerRegistry->subscribeTo(BookCreatedEvent::class, $listener);
// PSR-14: Getting listeners for an event
$listeners = $this->listenerRegistry->getListenersForEvent($event);
Listeners implement ListenerInterface:
final readonly class BookEventHandler implements ListenerInterface
{
public function __invoke(DomainEventInterface $event): void
{
// Handle event
}
}
Container Configuration
Both ports must share the same underlying League Event registry:
PrioritizedListenerRegistry::class => fn (): PrioritizedListenerRegistry => new PrioritizedListenerRegistry(),
LeagueEventDispatcher::class => fn (ContainerInterface $c): LeagueEventDispatcher => new LeagueEventDispatcher(
$c->get(PrioritizedListenerRegistry::class)
),
DispatcherInterface::class => fn (ContainerInterface $c): LeagueDispatcher => new LeagueDispatcher(
$c->get(LeagueEventDispatcher::class)
),
ListenerRegistryInterface::class => fn (ContainerInterface $c): LeagueListenerRegistry => new LeagueListenerRegistry(
$c->get(PrioritizedListenerRegistry::class)
),
Testing
Use FakeDispatcher and FakeListenerRegistry for testing:
$listenerRegistry = new FakeListenerRegistry();
$dispatcher = new FakeDispatcher($listenerRegistry);
// Register and dispatch
$listenerRegistry->subscribeTo(MyEvent::class, $listener);
$dispatcher->dispatch($event);
// Assertions
expect($dispatcher->wasDispatched(MyEvent::class))->toBeTrue();
expect($listenerRegistry->getSubscriberCount(MyEvent::class))->toBe(1);
See Also
- Event Bus - EventBus uses Dispatcher internally
- Event-Driven Architecture - Conceptual foundation
- PSR-14 Event Dispatcher - Interface specification