symfony/event-dispatcher
Symfony’s EventDispatcher component lets application parts communicate by dispatching events to registered listeners and subscribers. Build decoupled, extensible workflows with a simple API for adding, removing, and prioritizing handlers.
Installation:
composer require symfony/event-dispatcher
Laravel already includes this package as a dependency, so no additional installation is typically required for basic usage.
First Use Case: Define an event class and dispatch it:
// app/Events/CustomEvent.php
namespace App\Events;
use Illuminate\Queue\SerializesModels;
use Illuminate\Foundation\Events\Dispatchable;
class CustomEvent
{
use Dispatchable, SerializesModels;
public function __construct(public string $message) {}
}
Dispatch the event:
use App\Events\CustomEvent;
event(new CustomEvent('Hello, EventDispatcher!'));
Register a Listener:
Create a listener class and register it in EventServiceProvider:
// app/Listeners/CustomEventListener.php
namespace App\Listeners;
use App\Events\CustomEvent;
class CustomEventListener
{
public function handle(CustomEvent $event)
{
info('Event received: ' . $event->message);
}
}
Register the listener in EventServiceProvider:
protected $listen = [
CustomEvent::class => [
CustomEventListener::class,
],
];
Verify Integration: Run a test or check logs to confirm the listener is triggered when the event is dispatched.
OrderPlaced event can trigger notifications, inventory updates, and analytics without direct service calls.// Dispatch event
event(new OrderPlaced($order));
// Listeners handle different concerns
class SendOrderConfirmationEmail {
public function handle(OrderPlaced $event) { /* ... */ }
}
class UpdateInventory {
public function handle(OrderPlaced $event) { /* ... */ }
}
protected $listen = [
OrderPlaced::class => [
['App\Listeners\ValidateOrder', 'handle'], // Priority 0 (default)
['App\Listeners\LogOrder', 'handle'], // Priority 10
['App\Listeners\SendNotification', 'handle'], // Priority -10
],
];
// app/Events/OrderEventSubscriber.php
namespace App\Events;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class OrderEventSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
OrderPlaced::class => 'onOrderPlaced',
OrderCancelled::class => 'onOrderCancelled',
];
}
public function onOrderPlaced(OrderPlaced $event) { /* ... */ }
public function onOrderCancelled(OrderCancelled $event) { /* ... */ }
}
Register the subscriber in EventServiceProvider:
protected $subscribe = [
OrderEventSubscriber::class,
];
Event::until() or Event::halt() to stop further listeners from executing.public function handle(OrderPlaced $event)
{
if ($event->order->isInvalid()) {
Event::halt($event); // Stops further listeners
}
}
$dispatcher = app(\Symfony\Component\EventDispatcher\EventDispatcherInterface::class);
$dispatcher->addListener(OrderPlaced::class, function ($event) {
// Dynamic logic
});
Leverage Laravel’s Event Facade:
use Illuminate\Support\Facades\Event;
Event::dispatch(new CustomEvent('Hello'));
Queue Listeners: Use Laravel’s queue system to process events asynchronously:
protected $dispatchesAfter = [
OrderPlaced::class => [
SendOrderConfirmationEmail::class,
],
];
Event Service Provider:
Centralize event-listener registration in EventServiceProvider for better maintainability.
Traceable Event Dispatcher:
Use TraceableEventDispatcher for debugging event flows (note the v8.0.8 memory leak fix for long-running processes).
$dispatcher = new \Symfony\Component\EventDispatcher\TraceableEventDispatcher($dispatcher);
Generic Event Objects:
For Symfony-style generic events, use GenericEvent:
use Symfony\Component\EventDispatcher\GenericEvent;
$event = new GenericEvent($data, ['key' => 'value']);
$dispatcher->dispatch($event, 'event.name');
Memory Leaks in Long-Running Processes:
TraceableEventDispatcher had a memory leak in versions before v8.0.8. If using it in CLI workers, queues, or long-running scripts, ensure you’re on v8.0.8+.TraceableEventDispatcher in production long-running processes.Listener Ordering:
spl_object_hash).$dispatcher->addListener(OrderPlaced::class, [$listener, 'handle'], 10);
Event Object Mutability:
$eventClone = clone $event;
Circular Dependencies:
Event::until() or Event::halt() to break cycles or refactor logic.Performance Overhead:
TraceableEventDispatcher and optimize critical paths.Enable Event Tracing:
Wrap the dispatcher in TraceableEventDispatcher for debugging:
$dispatcher = new \Symfony\Component\EventDispatcher\TraceableEventDispatcher(app('events'));
$dispatcher->dispatch(new CustomEvent('Debug'));
Log Listener Execution: Add logging to listeners to trace execution:
public function handle(CustomEvent $event)
{
\Log::debug('Listener executed for event: ' . $event->message);
}
Check for Unregistered Listeners:
Use php artisan event:list to list all registered listeners and verify coverage.
Test Event Propagation:
Verify that Event::halt() or Event::until() works as expected in edge cases.
Custom Event Dispatcher:
Extend EventDispatcherInterface to add custom logic (e.g., caching, retry mechanisms):
class CustomEventDispatcher implements EventDispatcherInterface
{
private $decorated;
public function __construct(EventDispatcherInterface $decorated)
{
$this->decorated = $decorated;
}
public function dispatch(object|string $event, string $eventName = null): object
{
// Custom logic (e.g., caching)
return $this->decorated->dispatch($event, $eventName);
}
// Delegate other methods to the decorated dispatcher
}
Event Subscriber Decorators: Decorate subscribers to add cross-cutting concerns (e.g., logging, metrics):
class LoggingSubscriberDecorator implements EventSubscriberInterface
{
private $decorated;
public function __construct(EventSubscriberInterface $decorated)
{
$this->decorated = $decorated;
}
public static function getSubscribedEvents()
{
return $this->decorated::getSubscribedEvents();
}
public function __call($name, $arguments)
{
\Log::info("Calling subscriber method: {$name}");
return $this->decorated->$name(...$arguments);
}
}
Dynamic Event Namespaces:
Use Symfony’s EventDispatcher with dynamic event namespaces for modular applications:
$
How can I help you explore Laravel packages today?