Event Bus and Observer Pattern in PHP
In modern PHP web application development, there is often a need to flexibly respond to various events: sending an email after registration, logging user actions, updating the cache when data changes. The Observer pattern and its extension — Event Bus — are ideal for these purposes.
Observer Pattern: Basics
The Observer pattern is a behavioral design pattern that creates a subscription mechanism allowing one object to watch and react to events occurring in other objects. Main components:
- Subject — the object that generates events
- Observer — the object that reacts to events
- Event — information about the action that occurred
Event Bus: Evolution of Observer
Event Bus is a centralized bus that manages all events in an application. Unlike the classic Observer, where subscription is tightly coupled to the subject, Event Bus provides:
- Loose coupling between senders and receivers
- Ability to add/remove handlers at runtime
- Global access to the bus from any part of the application
- Simplified testing and debugging
Example Implementation of Event Bus in PHP
Let's create a simple yet functional implementation of Event Bus, compatible with PSR-14 (Event Dispatcher).
<?php// Event interfaceinterface EventInterface{ public function getName(): string;}
// Base eventclass UserRegisteredEvent implements EventInterface{ public function __construct( private string $email, private string $username ) {}
public function getName(): string { return 'user.registered'; }
public function getEmail(): string { return $this->email; }
public function getUsername(): string { return $this->username; }}
// Handler interfaceinterface EventHandlerInterface{ public function handle(EventInterface $event): void;}
// Event Busclass EventBus{ private array $listeners = [];
public function addListener(string $eventName, EventHandlerInterface $handler): void { $this->listeners[$eventName][] = $handler; }
public function removeListener(string $eventName, EventHandlerInterface $handler): void { if (!isset($this->listeners[$eventName])) { return; }
$this->listeners[$eventName] = array_filter( $this->listeners[$eventName], fn($h) => $h !== $handler ); }
public function dispatch(EventInterface $event): void { $eventName = $event->getName();
if (!isset($this->listeners[$eventName])) { return; }
foreach ($this->listeners[$eventName] as $handler) { $handler->handle($event); } }}?>Practical Usage
Now let's create handlers and demonstrate the bus in action:
<?php// Email sending handlerclass SendWelcomeEmailHandler implements EventHandlerInterface{ public function handle(EventInterface $event): void { if (!$event instanceof UserRegisteredEvent) { return; }
echo "Sending welcome email to {$event->getEmail()}"; // mail($event->getEmail(), 'Welcome!', '...'); }}
// Logging handlerclass LogUserRegistrationHandler implements EventHandlerInterface{ public function handle(EventInterface $event): void { if (!$event instanceof UserRegisteredEvent) { return; }