symfony/event-dispatcher-contracts
Interfaces and base abstractions for Symfony’s event dispatching system. Use these contracts to standardize how events and listeners interact, and to build libraries compatible with Symfony components and their proven implementations.
Install the package via Composer:
composer require symfony/event-dispatcher-contracts
First Steps:
Symfony\Contracts\EventDispatcher\Event:
use Symfony\Contracts\EventDispatcher\Event;
class OrderPlaced extends Event
{
public function __construct(public int $orderId) {}
}
event(new OrderPlaced(123));
Listen trait or EventServiceProvider:
class OrderPlacedListener
{
public function handle(OrderPlaced $event): void
{
// Logic here
}
}
First Use Case: Migrate legacy events to PSR-14 for cross-framework reuse (e.g., shared events between a Laravel API and a Symfony CLI tool).
readonly properties in PHP 8.1+).getName() for explicit event identification:
class UserActivated extends Event
{
public const NAME = 'user.activated';
public function getName(): string
{
return self::NAME;
}
}
InvoiceGenerated instead of InvoiceCreated).Event class extends Symfony\Contracts\EventDispatcher\Event.league/event or symfony/event-dispatcher for non-Laravel contexts:
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\GenericEvent;
$dispatcher = new EventDispatcher();
$dispatcher->dispatch(new GenericEvent($event), 'user.registered');
event() helper by wrapping them in a Laravel-compatible event:
event(new LaravelEventWrapper($psr14Event));
EventDispatcherInterface::addListener() for runtime binding:
$dispatcher->addListener(OrderPlaced::NAME, fn(OrderPlaced $event) => /* ... */);
EventDispatcherInterface::addListener(..., 100) for low priority).Symfony\Component\EventDispatcher\EventDispatcher in tests:
$dispatcher = new EventDispatcher();
$dispatcher->addListener('user.registered', $this->createMock(Listener::class));
$dispatcher->dispatch(new UserRegistered($user));
assertHasListeners() (custom helper) or assertCount() on mocked listeners.No Concrete Implementation:
symfony/event-dispatcher-contracts to include a dispatcher.symfony/event-dispatcher or Laravel’s Illuminate\Events\Dispatcher.Missing getName():
getName() is required (it’s optional in PSR-14).public function getName(): string { return static::NAME; }
PHP Version Mismatch:
readonly properties).symfony/event-dispatcher:^5.4).Laravel-Specific Quirks:
Event methods (e.g., broadcastOn) in PSR-14 events.Circular Dependencies:
dd($event) to verify event properties and getName().$dispatcher = new class implements EventDispatcherInterface {
private $decorated;
public function __construct(EventDispatcherInterface $dispatcher) { $this->decorated = $dispatcher; }
public function dispatch(Event $event, ?string $eventName = null): Event {
\Log::debug("Dispatching {$eventName ?? get_class($event)}");
return $this->decorated->dispatch($event, $eventName);
}
// Delegate other methods to $this->decorated...
};
strace or Xdebug to trace listener execution order.#[Asynchronous]):
trait AsyncEvent {}
class PaymentProcessed extends Event implements AsyncEvent {}
EventDispatcherInterface to add middleware (e.g., logging, validation):
class LoggingDispatcher implements EventDispatcherInterface {
private $inner;
public function __construct(EventDispatcherInterface $dispatcher) { $this->inner = $dispatcher; }
public function dispatch(Event $event, ?string $eventName = null): Event {
\Log::info("Dispatching {$eventName ?? get_class($event)}");
return $this->inner->dispatch($event, $eventName);
}
}
JsonSerializable for events used in queues/APIs:
class OrderShipped extends Event implements JsonSerializable {
public function jsonSerialize(): array { return ['order_id' => $this->orderId]; }
}
AppServiceProvider:
$this->app->singleton(EventDispatcherInterface::class, function ($app) {
return new EventDispatcher();
});
ShouldQueue on listeners for async processing (works with PSR-14 events):
class SendWelcomeEmail implements ShouldQueue {
public function handle(OrderPlaced $event) { /* ... */ }
}
EventServiceProvider to bind PSR-14 listeners:
protected $listen = [
OrderPlaced::NAME => [
SendNotification::class,
'queue' => SendEmail::class,
],
];
AuthAttempted).
✅ Reserve for cross-cutting concerns or shared libraries.EventDispatcherInterface into domain models.
✅ Use dependency injection at the service layer.event() helper with PSR-14 dispatchers for no gain.
✅ Leverage Laravel’s built-in system unless interoperability is required.How can I help you explore Laravel packages today?