Observer is a behavioral design pattern that allows some objects to notify other objects about changes in their state.
PHP has several built-in interfaces (SplSubject, SplObserver) that can be used to make your implementations of the Observer pattern compatible with the rest of the PHP code.
Sample Code:
(Source from https://refactoring.guru/design-patterns/observer/php/example)
<?php
namespace RefactoringGuru\Observer\Conceptual;
/**
* PHP has a couple of built-in interfaces related to the Observer pattern.
*
* Here's what the Subject interface looks like:
*
* @link http://php.net/manual/en/class.splsubject.php
*
* interface SplSubject
* {
* // Attach an observer to the subject.
* public function attach(SplObserver $observer);
*
* // Detach an observer from the subject.
* public function detach(SplObserver $observer);
*
* // Notify all observers about an event.
* public function notify();
* }
*
* There's also a built-in interface for Observers:
*
* @link http://php.net/manual/en/class.splobserver.php
*
* interface SplObserver
* {
* public function update(SplSubject $subject);
* }
*/
/**
* The Subject owns some important state and notifies observers when the state
* changes.
*/
class Subject implements \SplSubject
{
/**
* @var int For the sake of simplicity, the Subject's state, essential to
* all subscribers, is stored in this variable.
*/
public $state;
/**
* @var \SplObjectStorage List of subscribers. In real life, the list of
* subscribers can be stored more comprehensively (categorized by event
* type, etc.).
*/
private $observers;
public function __construct()
{
$this->observers = new \SplObjectStorage();
}
/**
* The subscription management methods.
*/
public function attach(\SplObserver $observer): void
{
echo "Subject: Attached an observer.\n";
$this->observers->attach($observer);
}
public function detach(\SplObserver $observer): void
{
$this->observers->detach($observer);
echo "Subject: Detached an observer.\n";
}
/**
* Trigger an update in each subscriber.
*/
public function notify(): void
{
echo "Subject: Notifying observers...\n";
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
/**
* Usually, the subscription logic is only a fraction of what a Subject can
* really do. Subjects commonly hold some important business logic, that
* triggers a notification method whenever something important is about to
* happen (or after it).
*/
public function someBusinessLogic(): void
{
echo "\nSubject: I'm doing something important.\n";
$this->state = rand(0, 10);
echo "Subject: My state has just changed to: {$this->state}\n";
$this->notify();
}
}
/**
* Concrete Observers react to the updates issued by the Subject they had been
* attached to.
*/
class ConcreteObserverA implements \SplObserver
{
public function update(\SplSubject $subject): void
{
if ($subject->state < 3) {
echo "ConcreteObserverA: Reacted to the event.\n";
}
}
}
class ConcreteObserverB implements \SplObserver
{
public function update(\SplSubject $subject): void
{
if ($subject->state == 0 || $subject->state >= 2) {
echo "ConcreteObserverB: Reacted to the event.\n";
}
}
}
/**
* The client code.
*/
$subject = new Subject();
$o1 = new ConcreteObserverA();
$subject->attach($o1);
$o2 = new ConcreteObserverB();
$subject->attach($o2);
$subject->someBusinessLogic();
$subject->someBusinessLogic();
$subject->detach($o2);
$subject->someBusinessLogic();
Output:
Subject: Attached an observer.
Subject: Attached an observer.
Subject: I'm doing something important.
Subject: My state has just changed to: 2
Subject: Notifying observers...
ConcreteObserverA: Reacted to the event.
ConcreteObserverB: Reacted to the event.
Subject: I'm doing something important.
Subject: My state has just changed to: 4
Subject: Notifying observers...
ConcreteObserverB: Reacted to the event.
Subject: Detached an observer.
Subject: I'm doing something important.
Subject: My state has just changed to: 1
Subject: Notifying observers...
ConcreteObserverA: Reacted to the event.