Observer pattern implements a publish/subscribe mechanism between objects. Some subscribing objects want to observe the state change of a specific object, and the observer pattern enables the publishing object(subject) to notify other objects about changes.
This article demonstrates Observer pattern implementations in PHP. Check the implementation details and examples.
Implementation
PHP has built-in interfaces – SplObserver and SplSubject for the observer implementation.
Here we are discussing 2 methods – one with the built-in interfaces, and another with our own interface so that we can add some flexibility to the implementation.
Method #1: Using Built-in Interfaces
PHP has predefined interfaces SplObserver and SplSubject for the implementation. Let’s check the predefined interfaces first-
// Interface for the subject class
interface SplSubject {
// Add a new observer to the observer list
public function attach(SplObserver $observer): void;
// Remove an existing observer from the list
public function detach(SplObserver $observer): void;
// Notify observers/subscribsers about state change
public function notify(): void;
}
// Interface for the obsers
interface SplObserver {
// Receive update/notification from the subject
public function update(SplSubject $subject): void;
}
Follow the steps below for the implementation-
- Define your subject class. Implement interface SplSubject for the class. Define a list of observer objects, the type of the list is SplObjectStorage (can be an array if required). Define all the states and utility methods for the class. When any state change in the class, call the update method, to notify the observers.
- Define observer classes. Implement SplObserver interface for the class.
- In the client create a new object of the subject class. Create new observer objects and attach those to the subject.
Check the following example of simple observer implementation using built-in observer interfaces-
Use of the SplObjectStorage class is not directly related to the observer partner. This is used to manage a list of observer objects easily.
<?php
// Observer pattern in PHP
// Using built in PHP observer interfaces
// Concrete subject class
class ConcreteSubject implements SplSubject {
public int $state;
private SplObjectStorage $observerList;
public function __construct() {
$this->observerList = new SplObjectStorage;
}
public function setState(int $state): void {
$this->state = $state;
$this->notify();
}
public function attach(SplObserver $observer): void {
$this->observerList->attach($observer);
}
public function detach(SplObserver $observer): void {
$this->observerList->detach($observer);
}
public function notify(): void {
foreach ($this->observerList as $observer) {
$observer->update($this);
}
}
}
// Concrete observer
class ObserverOne implements SplObserver {
public function update(SplSubject $subject): void {
echo "Received update in ObserverOne: " . $subject->state . "n";
}
}
// Concrete observer
class ObserverTwo implements SplObserver {
public function update(SplSubject $subject): void {
echo "Received update in ObserverTwo: " . $subject->state . "n";
}
}
// Demo
$subject = new ConcreteSubject();
$subject->attach(new ObserverOne());
$subject->attach(new ObserverTwo());
echo "Setting subject state value to: 10n";
$subject->setState(10);
echo "Setting subject state value to: 999n";
$subject->setState(999);
Output will be as follows-
Setting subject state value to: 10
Received update in ObserverOne: 10
Received update in ObserverTwo: 10
Setting subject state value to: 999
Received update in ObserverOne: 999
Received update in ObserverTwo: 999
Method #2: Custom Observer
Use this custom observer implementation if your observer and/or subscriber classes are complex and the built-in interfaces do not serve your requirements.
- Define a subject interface. Declare methods for adding new observer and notifying about updates.
- Create a concrete subject class and implement the subject interface. Maintain a list of observers, and whenever there is a change call the notification method. In the notification method loop through the list of the observers and notify those.
- Create interface (or abstract classes) for the observers. Declare abstract method for receiving the updates.
- Create observer class, and implement the interface.
<?php
// Simple Observer pattern in PHP
// Interface for the subject
interface Subject {
function getState(): int;
// Add new observer for the subject state
function attachObserver(Observer $observer): void;
// Notify all observers attached to the subject
function notifyObservers(): void;
}
// Concrete subject class
class ConcreteSubject implements Subject {
private int $state;
private array $observerList = [];
public function setState(int $state): void {
$this->state = $state;
$this->notifyObservers();
}
public function getState(): int {
return $this->state;
}
public function attachObserver(Observer $observer): void {
array_push($this->observerList, $observer);
}
public function notifyObservers(): void {
foreach ($this->observerList as $observer) {
$observer->update();
}
}
}
// Observer
abstract class Observer {
protected ?Subject $subject;
abstract function update(): void;
}
// Concrete observer
class ObserverOne extends Observer {
public function __construct(Subject $subject) {
$this->subject = $subject;
$this->subject->attachObserver($this);
}
public function update(): void {
echo "Received in ObserverOne: " . $this->subject->getState() . "n";
}
}
// Concrete observer
class ObserverTwo extends Observer {
public function __construct(Subject $subject) {
$this->subject = $subject;
$this->subject->attachObserver($this);
}
public function update(): void {
echo "Received in ObserverTwo: " . $this->subject?->getState() . "n";
}
}
// Demo
$subject = new ConcreteSubject();
new ObserverOne($subject);
new ObserverTwo($subject);
echo "Setting subject state value to: 10n";
$subject->setState(10);
echo "Setting subject state value to: 999n";
$subject->setState(999);
Output of this code-
Setting subject state value to: 10
Received in ObserverOne: 10
Received in ObserverTwo: 10
Setting subject state value to: 999
Received in ObserverOne: 999
Received in ObserverTwo: 999
Examples
Check the following examples of Observer pattern implementation.
Example #1: Player Observer
In this example we are consider a player score update notification. We have multiple observer classes for referee, commentator, and scoreboard.
Player [Subject]
- Create file “Player.php”.
- Define class “Player”.
- Implement SplSubject interface for the class.
- Define property “$observers” of type SplObjectStorage (this can be an array if you want). This is used to maintain a list of observers.
- Define property “$score”. also define a method for setting the score. In the method implementation, call the notify method.
<?php
// Player.php
namespace BigBoxCodeDesignPatternObserverPlayer;
class Player implements SplSubject {
public string $name;
public int $score;
private SplObjectStorage $observers;
public function __construct(string $name) {
$this->name = $name;
$this->observers = new SplObjectStorage;
}
public function setScore(int $score): void {
$this->score = $score;
$this->notify();
}
public function attach(SplObserver $observer): void {
$this->observers->attach($observer);
}
public function detach(SplObserver $observer): void {
$this->observers->detach($observer);
}
public function notify(): void {
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
}
Referee Class [Observer]
- Create file “Referee.php”.
- Define class “Referee”.
- Implement interface SplObserver for the class.
- In the method “update” perform any operation that we want to do on score change.
<?php
// Refaree.php
namespace BigBoxCodeDesignPatternObserverPlayer;
class Referee implements SplObserver {
public function __construct(private string $code) {
}
public function update(SplSubject $player): void {
echo "nPlayer update received in Referee : " . $this->code . "n";
echo "Player name: " . $player->name . "n";
echo "score: " . $player->score . "n";
}
}
Commentator Class [Observer]
- Create file “Commenentator.php”.
- Define class “Commentator”.
- Implement interface SplObserver for the class, in “update” method perform operations related to score change.
<?php
// Commentator.php
namespace BigBoxCodeDesignPatternObserverPlayer;
class Commentator implements SplObserver {
public function __construct(private string $name) {
}
public function update(SplSubject $player): void {
echo "nPlayer update received in Commentator : " . $this->name . "n";
echo "Player name: " . $player->name . "n";
echo "score: " . $player->score . "n";
}
}
Scoreboard Class [Observer]
- Create file “ScoreBoard.php”.
- Create class “ScoreBoard”.
- Implement interface SplObserver for the class, in “update” method perform operations related to score change.
<?php
// ScoreBoard.php
namespace BigBoxCodeDesignPatternObserverPlayer;
class ScoreBoard implements SplObserver {
public function update(SplSubject $player): void {
echo "nPlayer update received in Score Board -n";
echo "Player name: " . $player->name . "n";
echo "score: " . $player->score . "n";
}
}
Demo
Create player class. then create some observer classes of type “Referee”, “Commentator”, “ScoreBoard”. Then attach these observer classes to the player.
Now, whenever the player score changes, the subscribers will be notified.
<?php
// demo.php
require __DIR__ . '/../../vendor/autoload.php';
use BigBoxCodeDesignPatternObserverPlayerCommentator;
use BigBoxCodeDesignPatternObserverPlayerPlayer;
use BigBoxCodeDesignPatternObserverPlayerReferee;
use BigBoxCodeDesignPatternObserverPlayerScoreBoard;
// Referees
$referee1 = new Referee("REF #1");
$referee2 = new Referee("REF #2");
// Commentators
$commentator1 = new Commentator("John Doe");
// Score Boards
$scoreBoard = new ScoreBoard();
// Create players and attached observers
$playerA = new Player("Player A");
$playerA->attach($referee1);
$playerA->attach($referee2);
$playerA->attach($commentator1);
$playerA->attach($scoreBoard);
// Create another player and attach observers
$playerB = new Player("Player B");
$playerB->attach($referee1);
// Do not attach Referee #2 for demo purpose
// $playerA->attach($referee2);
$playerB->attach($commentator1);
$playerB->attach($scoreBoard);
// Change/set sccore for the players
echo "nSet/Change 'Player A' score to - 1n";
$playerA->setScore(1);
echo "nSet/Change 'Player A' score to - 5n";
$playerA->setScore(5);
echo "nSet/Change 'Player B' score to - 3n";
$playerB->setScore(3);
echo "nSet/Change 'Player A' score to - 9n";
$playerA->setScore(9);
Output
We will get the following output from the above implementation-
Set/Change 'Player A' score to - 1
Player update received in Referee : REF #1
Player name: Player A
score: 1
Player update received in Referee : REF #2
Player name: Player A
score: 1
Player update received in Commentator : John Doe
Player name: Player A
score: 1
Player update received in Score Board -
Player name: Player A
score: 1
---------------------------------------------------------------------
Set/Change 'Player A' score to - 5
Player update received in Referee : REF #1
Player name: Player A
score: 5
Player update received in Referee : REF #2
Player name: Player A
score: 5
Player update received in Commentator : John Doe
Player name: Player A
score: 5
Player update received in Score Board -
Player name: Player A
score: 5
---------------------------------------------------------------------
Set/Change 'Player B' score to - 3
Player update received in Referee : REF #1
Player name: Player B
score: 3
Player update received in Commentator : John Doe
Player name: Player B
score: 3
Player update received in Score Board -
Player name: Player B
score: 3
---------------------------------------------------------------------
Set/Change 'Player A' score to - 9
Player update received in Referee : REF #1
Player name: Player A
score: 9
Player update received in Referee : REF #2
Player name: Player A
score: 9
Player update received in Commentator : John Doe
Player name: Player A
score: 9
Player update received in Score Board -
Player name: Player A
score: 9
Source Code
Use the following link to get the source code:
Example | Source Code Link |
---|---|
Example #1: Player Observer | GitHub |
Other Code Implementations
Use the following links to check Observer pattern implementation in other programming languages.