Observer
The Observer pattern defines a one-to-many dependency between objects: when one object — the subject — changes state, all of its dependents — the observers — are notified automatically, without the subject needing to know anything about what each observer actually does with that notification.
The Problem It Solves
Imagine placing an order needs to trigger several unrelated side effects: send a confirmation email, update inventory counts, and maybe log the sale for analytics. The naive approach hardcodes every one of those calls directly inside the order-placing method:
<?php
class Order {
public function place(): void {
// ...order placement logic...
// Every new side effect means editing this method directly
(new EmailNotifier())->send($this);
(new InventoryUpdater())->reduceStock($this);
}
}
Order now has to know the exact class names and method signatures of every single thing
that cares about a new order, and adding a new dependent (say, a Slack notification for the sales team)
means opening and editing Order again. The subject and its many unrelated dependents have
become tightly, needlessly coupled.
What Observer Actually Does
Observer decouples the subject from its dependents by routing through a shared interface:
- The subject keeps a list of observers and exposes
attach()/detach()to add or remove them. - When something noteworthy happens, the subject calls its own
notify()method. notify()simply loops over every attached observer and calls a shared interface method (commonlyupdate()) on each one.- The subject never knows or cares what each observer actually does inside
update().
A Realistic Worked Example: Order Notifications
First, the interface every observer must implement:
<?php
interface OrderObserverInterface {
public function update(Order $order): void;
}
The Order subject holds the list of observers and drives the notifications:
<?php
class Order {
public string $id;
public float $total;
private array $observers = [];
public function __construct(string $id, float $total) {
$this->id = $id;
$this->total = $total;
}
public function attach(OrderObserverInterface $observer): void {
$this->observers[] = $observer;
}
public function detach(OrderObserverInterface $observer): void {
$this->observers = array_filter(
$this->observers,
fn($o) => $o !== $observer
);
}
public function notify(): void {
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
public function place(): void {
// ...order placement logic...
$this->notify(); // Order has no idea who is listening, or how many
}
}
And two independent observers, each implementing the same interface, doing completely unrelated work:
<?php
class EmailNotifier implements OrderObserverInterface {
public function update(Order $order): void {
mail(
'customer@example.com',
"Order {$order->id} confirmed",
"Your order total was {$order->total}"
);
}
}
class InventoryUpdater implements OrderObserverInterface {
public function update(Order $order): void {
// Reduce stock levels for every item in $order
}
}
// Usage — Order doesn't reference either class by name
$order = new Order('ORD-1042', 79.99);
$order->attach(new EmailNotifier());
$order->attach(new InventoryUpdater());
$order->place(); // notify() fires update() on both observers
SlackNotifier class implementing
OrderObserverInterface and attach() it. Order,
EmailNotifier, and InventoryUpdater stay exactly as they are.
Real-World PHP: This Is What Event Systems Are Built On
You've almost certainly used this exact idea already, just with friendlier names. Laravel's
Events and Listeners system is a full-featured Observer: an OrderPlaced
event is the subject's notification, and every registered listener class is an observer that reacts
to it without the code that fired the event knowing any of them exist. Symfony's
EventDispatcher component works the same way — components dispatch named events, and
any number of independently-registered subscribers can react. Both frameworks essentially generalized
attach()/notify() into a registry keyed by event name, but the underlying
idea — decoupled one-to-many notification — is exactly the Observer pattern.
When to Reach for Observer
- An action needs to trigger several independent side effects that shouldn't know about each other.
- You want to add or remove reactions to an event without touching the class that triggers it.
- Different parts of an application need to stay in sync with a shared piece of state.