Start · Sprachen · PHP · Referenz · SplSubject

SplSubject

Interface

Definiert den Vertrag für das <em>Subjekt</em> im Observer-Muster und wird zusammen mit <code>SplObserver</code> zur Umsetzung des Observer-Entwurfsmusters verwendet.

seit PHP 5.1.0 Kategorie: oop

Signatur

interface SplSubject

Beschreibung

SplSubject ist ein in der SPL (Standard PHP Library) definiertes Interface, das die Rolle des beobachteten Objekts im klassischen Observer-Entwurfsmuster beschreibt. Es legt drei Methoden fest: attach() zum Registrieren eines Beobachters, detach() zum Entfernen eines Beobachters sowie notify() zum Benachrichtigen aller registrierten Beobachter über eine Zustandsänderung.

Das Observer-Muster eignet sich überall dort, wo mehrere voneinander unabhängige Objekte automatisch über Änderungen eines gemeinsamen Zustands informiert werden sollen — etwa bei Event-Systemen, MVC-Architekturen oder reaktiven Datenflüssen. Durch die Implementierung von SplSubject signalisiert eine Klasse, dass sie beobachtet werden kann.

Eine eigene Klasse, die SplSubject implementiert, muss intern eine Liste von SplObserver-Instanzen verwalten und diese bei einem Aufruf von notify() der Reihe nach benachrichtigen. Für die Speicherung der Beobachter bietet sich SplObjectStorage an, da es doppelte Einträge automatisch verhindert und effizient mit Objekten arbeitet.

  • attach(SplObserver $observer) – fügt einen Beobachter hinzu.
  • detach(SplObserver $observer) – entfernt einen Beobachter.
  • notify() – informiert alle registrierten Beobachter.

Beispiele

Einfaches Observer-Muster mit SplSubject und SplObserver

<?php
class NewsAgency implements SplSubject
{
    private SplObjectStorage $observers;
    private string $latestNews = '';

    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 notify(): void
    {
        foreach ($this->observers as $observer) {
            $observer->update($this);
        }
    }

    public function publishNews(string $news): void
    {
        $this->latestNews = $news;
        $this->notify();
    }

    public function getLatestNews(): string
    {
        return $this->latestNews;
    }
}

class NewsChannel implements SplObserver
{
    private string $name;

    public function __construct(string $name)
    {
        $this->name = $name;
    }

    public function update(SplSubject $subject): void
    {
        echo "{$this->name} meldet: " . $subject->getLatestNews() . PHP_EOL;
    }
}

$agency  = new NewsAgency();
$channel1 = new NewsChannel('RTL');
$channel2 = new NewsChannel('ARD');

$agency->attach($channel1);
$agency->attach($channel2);

$agency->publishNews('PHP 9 wurde angekündigt!');

$agency->detach($channel1);

$agency->publishNews('SplSubject ist nützlich.');
RTL meldet: PHP 9 wurde angekündigt! ARD meldet: PHP 9 wurde angekündigt! ARD meldet: SplSubject ist nützlich.

Mehrere Beobachter mit unterschiedlicher Reaktion

<?php
class TemperatureSensor implements SplSubject
{
    private SplObjectStorage $observers;
    private float $temperature = 20.0;

    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 notify(): void
    {
        foreach ($this->observers as $observer) {
            $observer->update($this);
        }
    }

    public function setTemperature(float $temp): void
    {
        $this->temperature = $temp;
        $this->notify();
    }

    public function getTemperature(): float
    {
        return $this->temperature;
    }
}

class Logger implements SplObserver
{
    public function update(SplSubject $subject): void
    {
        echo '[LOG] Temperatur: ' . $subject->getTemperature() . '°C' . PHP_EOL;
    }
}

class Alarm implements SplObserver
{
    public function update(SplSubject $subject): void
    {
        if ($subject->getTemperature() > 80) {
            echo '[ALARM] Kritische Temperatur erreicht!' . PHP_EOL;
        }
    }
}

$sensor = new TemperatureSensor();
$sensor->attach(new Logger());
$sensor->attach(new Alarm());

$sensor->setTemperature(45.5);
$sensor->setTemperature(85.0);
[LOG] Temperatur: 45.5°C [LOG] Temperatur: 85°C [ALARM] Kritische Temperatur erreicht!

// Wichtig · Fallstricke

Da SplSubject lediglich einen Vertrag definiert, muss die eigene Klasse die interne Verwaltung der Observer-Liste vollständig selbst übernehmen. Die Verwendung von SplObjectStorage ist empfehlenswert, da es sicherstellt, dass kein Beobachter doppelt registriert wird. Alternativ kann ein einfaches Array verwendet werden, allerdings müssen Duplikate dann manuell verhindert werden.

Das Interface schreibt nicht vor, welche Daten an den Beobachter übergeben werden — notify() übergibt typischerweise $this an SplObserver::update(), sodass der Beobachter selbst entscheiden kann, welche Informationen er benötigt. Für komplexere Szenarien mit vielen Event-Typen sollte man ein dediziertes Event-System (z. B. Symfony EventDispatcher) in Betracht ziehen.