PHP Observer Design Pattern Behavioral Pattern

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.

Observer Pattern — open the lesson page

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

classDiagram class Observer { <<interface>> +update(news: string): void } class Subject { -observers: array +attach(observer: Observer): void +detach(observer: Observer): void #notify(news: string): void } class NewsAgency { +publish(news: string): void } class EmailSubscriber { -email: string +update(news: string): void } class SmsSubscriber { -phone: string +update(news: string): void } Observer <|.. EmailSubscriber Observer <|.. SmsSubscriber Subject <|-- NewsAgency Subject o-- Observer : notifies

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.