The `symfony/event-dispatcher` component is a powerful and standalone PHP library that provides a way to implement the Observer (or Publish-Subscribe) design pattern in your applications. It allows different parts of your application to communicate with each another without tight coupling, promoting a highly extensible and maintainable codebase.
Core Concepts:
1. Event: An event is an object that represents something that has happened in your application. It usually carries data related to that occurrence. In Symfony, events typically extend `Symfony\Contracts\EventDispatcher\Event` or implement `Psr\EventDispatcher\StoppableEventInterface` if you want to allow listeners to stop propagation.
2. Event Dispatcher: This is the central object responsible for dispatching events and notifying all registered listeners. It implements `Symfony\Contracts\EventDispatcher\EventDispatcherInterface`.
3. Listener (or Observer): A listener is a callable (a function, a static method, an object method) that executes when a specific event is dispatched. Listeners are registered with the dispatcher for one or more event names.
4. Subscriber: An event subscriber is a class that implements `Symfony\Component\EventDispatcher\EventSubscriberInterface`. Instead of registering multiple listeners individually, a subscriber defines which events it listens to and what methods should be called for each event within the class itself. This can help organize event handling logic.
How it Works:
* Defining Events: You define an event by creating a class, often extending `Symfony\Contracts\EventDispatcher\Event`. This class can hold any data relevant to the event.
* Registering Listeners/Subscribers: You register a listener (a callable) or a subscriber object with the event dispatcher, associating it with one or more specific event names. You can also specify a priority for listeners, determining the order in which they are executed.
* Dispatching Events: When something interesting happens in your application (e.g., a user registers, an order is placed, a file is uploaded), you create an instance of the relevant event class, populate it with data, and pass it to the `dispatch()` method of the event dispatcher along with an event name (a unique string identifier).
* Execution: The event dispatcher iterates through all registered listeners and subscribers for that specific event name and invokes their associated callables/methods. Listeners can modify the event object, potentially changing the behavior for subsequent listeners or the original dispatcher.
Benefits:
* Decoupling: Components don't need to know about each other. A component simply dispatches an event, and other components listen without direct dependencies.
* Extensibility: New features can be added by simply adding new listeners without modifying existing code. This is crucial for plugins and modules.
* Maintainability: Code becomes easier to understand and maintain as responsibilities are clearly separated.
* Reusability: Event listeners can be reused across different parts of an application or even different applications.
* Testability: Individual components (dispatchers, events, listeners) can be tested in isolation.
Example Code
<?php
require 'vendor/autoload.php';
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Contracts\EventDispatcher\Event;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
// 1. Define a custom Event class
class UserRegisteredEvent extends Event
{
public const NAME = 'user.registered';
private string $username;
private string $email;
public function __construct(string $username, string $email)
{
$this->username = $username;
$this->email = $email;
}
public function getUsername(): string
{
return $this->username;
}
public function getEmail(): string
{
return $this->email;
}
}
// 2. Create an Event Dispatcher instance
$dispatcher = new EventDispatcher();
// 3. Register Listeners
// Listener 1: Using a simple callable (function or static method)
$dispatcher->addListener(UserRegisteredEvent::NAME, function (UserRegisteredEvent $event) {
echo "[Listener 1] New user '{$event->getUsername()}' with email '{$event->getEmail()}' registered! Sending welcome email.\n";
});
// Listener 2: Using an object method (more common for complex logic)
class NotificationService
{
public function onUserRegistered(UserRegisteredEvent $event): void
{
echo "[Listener 2] NotificationService: Notifying admin about new user '{$event->getUsername()}'.\n";
}
}
$notificationService = new NotificationService();
$dispatcher->addListener(UserRegisteredEvent::NAME, [$notificationService, 'onUserRegistered']);
// Listener 3: With a priority (higher number means executed earlier)
$dispatcher->addListener(UserRegisteredEvent::NAME, function (UserRegisteredEvent $event) {
echo "[Listener 3] Performing some pre-registration check for '{$event->getUsername()}'.\n";
}, 100); // Priority 100
// 4. Register an Event Subscriber
class AnalyticsSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
UserRegisteredEvent::NAME => 'onUserRegistered',
// You can listen to multiple events with different methods
// 'another.event' => ['handleAnotherEvent', 50], // Method name and priority
];
}
public function onUserRegistered(UserRegisteredEvent $event): void
{
echo "[Subscriber] AnalyticsService: Recording user registration for '{$event->getUsername()}'.\n";
}
}
$analyticsSubscriber = new AnalyticsSubscriber();
$dispatcher->addSubscriber($analyticsSubscriber);
// 5. Dispatch the Event
echo "\n--- Dispatching UserRegisteredEvent ---\n";
$userRegisteredEvent = new UserRegisteredEvent('john_doe', 'john.doe@example.com');
$dispatcher->dispatch($userRegisteredEvent, UserRegisteredEvent::NAME);
echo "\n--- End of Dispatch ---\n";
// Example of another event (optional, to show extensibility)
class ProductViewedEvent extends Event
{
public const NAME = 'product.viewed';
private int $productId;
public function __construct(int $productId)
{
$this->productId = $productId;
}
public function getProductId(): int
{
return $this->productId;
}
}
class ProductLogListener
{
public function onProductViewed(ProductViewedEvent $event): void
{
echo "[Listener for ProductViewed] Logging product view for product ID: {$event->getProductId()}.\n";
}
}
$productLogListener = new ProductLogListener();
$dispatcher->addListener(ProductViewedEvent::NAME, [$productLogListener, 'onProductViewed']);
echo "\n--- Dispatching ProductViewedEvent ---\n";
$productViewedEvent = new ProductViewedEvent(123);
$dispatcher->dispatch($productViewedEvent, ProductViewedEvent::NAME);
/*
To run this code:
1. Make sure you have Composer installed.
2. Create a new directory, e.g., `event_dispatcher_example`.
3. Navigate into the directory and run `composer require symfony/event-dispatcher`.
4. Save the above code as `index.php` (or any other filename).
5. Run `php index.php` in your terminal.
Expected Output:
--- Dispatching UserRegisteredEvent ---
[Listener 3] Performing some pre-registration check for 'john_doe'.
[Listener 1] New user 'john_doe' with email 'john.doe@example.com' registered! Sending welcome email.
[Listener 2] NotificationService: Notifying admin about new user 'john_doe'.
[Subscriber] AnalyticsService: Recording user registration for 'john_doe'.
--- End of Dispatch ---
--- Dispatching ProductViewedEvent ---
[Listener for ProductViewed] Logging product view for product ID: 123.
*/








symfony/event-dispatcher