PHP Observer Pattern
A short screencast: the Observer pattern — a publisher notifies subscribers about changes without knowing their concrete classes. We build a news broadcast over email and SMS.
Overview
The observer defines a one-to-many dependency: when the publisher's state changes, every subscriber is notified. The Observer interface describes the update() method that each subscriber must implement. The base Subject class keeps the list of observers: attach() adds a subscriber, detach() removes one, and notify() walks the list calling update() on each. The concrete publisher NewsAgency extends Subject and triggers the broadcast through publish(). The concrete subscribers EmailSubscriber and SmsSubscriber react to the news in their own way, but the publisher knows neither their types nor what they actually do.
UML diagram
Code
Observer.php
interface Observer
{
public function update(string $news): void;
}
Subject.php
abstract class Subject
{
/** @var Observer[] */
private array $observers = [];
public function attach(Observer $observer): void
{
$this->observers[] = $observer;
}
public function detach(Observer $observer): void
{
$this->observers = array_filter(
$this->observers,
fn (Observer $item): bool => $item !== $observer
);
}
protected function notify(string $news): void
{
foreach ($this->observers as $observer) {
$observer->update($news);
}
}
}
EmailSubscriber.php
class EmailSubscriber implements Observer
{
public function __construct(private readonly string $email) {}
public function update(string $news): void
{
echo "Email to {$this->email}: {$news}".PHP_EOL;
}
}
SmsSubscriber.php
class SmsSubscriber implements Observer
{
public function __construct(private readonly string $phone) {}
public function update(string $news): void
{
echo "SMS to {$this->phone}: {$news}".PHP_EOL;
}
}
NewsAgency.php
class NewsAgency extends Subject
{
public function publish(string $news): void
{
$this->notify($news);
}
}
demo.php
require_once 'Observer.php';
require_once 'Subject.php';
require_once 'EmailSubscriber.php';
require_once 'SmsSubscriber.php';
require_once 'NewsAgency.php';
$agency = new NewsAgency;
$email = new EmailSubscriber('user@example.com');
$sms = new SmsSubscriber('+1 555 0100');
$agency->attach($email);
$agency->attach($sms);
$agency->publish('New PHP release!');
$agency->detach($sms);
$agency->publish('Second news');
Example output
After php demo.php the first piece of news reaches both subscribers, and after detach — email only:
Email to user@example.com: New PHP release!
SMS to +1 555 0100: New PHP release!
Email to user@example.com: Second news
Advantages
- The publisher and subscribers are loosely coupled: the publisher does not know the concrete observer classes.
- New subscribers can be added without changing the publisher's code.
- A subscription can be enabled and disabled at runtime.
- It follows the open/closed principle: extend without editing existing classes.
When to use
- When a change in one object must update others and their number is not known in advance.
- When objects must notify others without knowing who they are.
- When part of the objects must watch the state of another object.
- When you need an event model with subscribe and unsubscribe.
Usage in Laravel
In Laravel the observer is everywhere. Events and listeners: Event::dispatch() broadcasts an event and listeners in app/Listeners react to it through their handle() method. Eloquent observers in app/Observers subscribe to model events (creating, saved, deleted) and fire automatically. The Illuminate\Support\Facades\Notification mechanism delivers notifications across several channels (mail, SMS, database) — the channel is chosen at send time, not in the sender's code.