PHP SplObserver and SplSubject Tutorial

This article explains how to implement the Observer design pattern in PHP using the built-in SplObserver and SplSubject interfaces. You will learn the purpose of these interfaces, how they facilitate decoupled communication between objects, and how to implement them in your PHP applications with a practical code example.

Understanding the Observer Pattern in PHP

The Observer pattern is a software design pattern where an object (known as the Subject) maintains a list of its dependents (known as Observers) and notifies them automatically of any state changes.

Instead of writing custom interfaces to implement this pattern, PHP provides the Standard PHP Library (SPL) which includes two built-in interfaces: SplSubject and SplObserver. These interfaces standardize how objects communicate state changes in PHP.


The SplSubject Interface

The SplSubject interface represents the object that is being observed. It is responsible for managing its observers and notifying them when its state changes. To implement SplSubject, a class must define three methods:


The SplObserver Interface

The SplObserver interface represents the object that wants to be notified when the subject changes state. To implement SplObserver, a class must define a single method:


Practical Code Implementation

Here is a practical example of a user registration system where a UserRegistry (Subject) notifies an EmailNotifier and a Logger (Observers) when a new user signs up.

<?php

// 1. Implement the Subject
class UserRegistry implements SplSubject {
    private array $observers = [];
    private string $username;

    // Standard attach method
    public function attach(SplObserver $observer): void {
        $oid = spl_object_hash($observer);
        $this->observers[$oid] = $observer;
    }

    // Standard detach method
    public function detach(SplObserver $observer): void {
        $oid = spl_object_hash($observer);
        unset($this->observers[$oid]);
    }

    // Notify all attached observers
    public function notify(): void {
        foreach ($this->observers as $observer) {
            $observer->update($this);
        }
    }

    // Application logic that triggers notification
    public function registerUser(string $username): void {
        $this->username = $username;
        echo "UserRegistry: Registering user '{$username}'.\n";
        $this->notify();
    }

    // Getter to allow observers to read state
    public function getUsername(): string {
        return $this->username;
    }
}

// 2. Implement the Observers
class EmailNotifier implements SplObserver {
    public function update(SplSubject $subject): void {
        if ($subject instanceof UserRegistry) {
            echo "EmailNotifier: Sending welcome email to " . $subject->getUsername() . ".\n";
        }
    }
}

class Logger implements SplObserver {
    public function update(SplSubject $subject): void {
        if ($subject instanceof UserRegistry) {
            echo "Logger: User " . $subject->getUsername() . " has been logged to database.\n";
        }
    }
}

// 3. Execution
$userRegistry = new UserRegistry();

$emailNotifier = new EmailNotifier();
$logger = new Logger();

// Attach observers to the subject
$userRegistry->attach($emailNotifier);
$userRegistry->attach($logger);

// Trigger a state change
$userRegistry->registerUser("JohnDoe");

Output:

UserRegistry: Registering user 'JohnDoe'.
EmailNotifier: Sending welcome email to JohnDoe.
Logger: User JohnDoe has been logged to database.

Key Benefits of Using SPL Interfaces