Observer Pattern

Usage of this pattern allows class functionality to be dynamically extended in runtime, helping to prevent code rot and ensuring functionality is decoupled.


Purpose

A software pattern in which an object (subject) maintains a list of dependents (observers) and notifies them of state changes, usually by calling one of their methods. Usage of this pattern allows class functionality to be dynamically extended in runtime, helping to prevent code rot and ensuring functionality is decoupled.

Note: PHP already defines two interfaces that can help to implement this pattern: SplObserver and SplSubject.

UML


Code

User


namespace DesignPatterns\Behavioral\Observer;

use SplSubject;
use SplObjectStorage;
use SplObserver;

/**
* User implements the observed object (called Subject), it maintains a list of observers and sends notifications to
* them in case changes are made on the User object
*/
class User implements SplSubject
{
  private SplObjectStorage $observers;
  private $email;

  public function __construct()
  {
      $this->observers = new SplObjectStorage();
  }

  public function attach(SplObserver $observer): void
  {
      $this->observers->attach($observer);
  }

  public function detach(SplObserver $observer): void
  {
      $this->observers->detach($observer);
  }

  public function changeEmail(string $email): void
  {
      $this->email = $email;
      $this->notify();
  }

  public function notify(): void
  {
      /** @var SplObserver $observer */
      foreach ($this->observers as $observer) {
          $observer->update($this);
      }
  }
}

UserObserver


namespace DesignPatterns\Behavioral\Observer;

use SplObserver;
use SplSubject;

class UserObserver implements SplObserver
{
    /**
     * @var SplSubject[]
     */
    private array $changedUsers = [];

    /**
     * It is called by the Subject, usually by SplSubject::notify()
     */
    public function update(SplSubject $subject): void
    {
        $this->changedUsers[] = clone $subject;
    }

    /**
     * @return SplSubject[]
     */
    public function getChangedUsers(): array
    {
        return $this->changedUsers;
    }
}