The SplSubject interface

(PHP 5 >= 5.1.0, PHP 7)

简介

The SplSubject interface is used alongside SplObserver to implement the Observer Design Pattern.

接口摘要

SplSubject {
/* 方法 */
abstract public attach ( SplObserver $observer ) : void
abstract public detach ( SplObserver $observer ) : void
abstract public notify ( void ) : void
}

Table of Contents

User Contributed Notes

xedin dot unknown at gmail dot com 01-Apr-2019 06:57
For a few years, I've made a few attempts to understand the architecture behind the Subject/Observer pair. Recently I tried again. I just could not imagine why someone would design this in the way it is documented.  I mean, "what were they thinking?!", I thought. The if things outside of the Subject could tell it when to notify observers, that would completely break encapsulation. Because only the subject can know when events occur inside of it. And therefore, exposing `notify()` would be a huge violation to many principles of good OOP design.

However, I have come up with one scenario where this would be valid. That is, if the Subject represents a Hook, like in WordPress. For example, in an event system where events are invoked with a name or code, a Hook could represent a named event in the system, and by calling `notify()` from outside the dispatcher could notify observers. If this is the intended scenario, then the problems are mostly with naming: the name "Subject" implies that it is what is being observed, and the Observer pattern is different.
aiddroid at example dot com 02-Jul-2013 07:01
/**
 * Subject,that who makes news
 */
class Newspaper implements \SplSubject{
    private $name;
    private $observers = array();
    private $content;
   
    public function __construct($name) {
        $this->name = $name;
    }

    //add observer
    public function attach(\SplObserver $observer) {
        $this->observers[] = $observer;
    }
   
    //remove observer
    public function detach(\SplObserver $observer) {
       
        $key = array_search($observer,$this->observers, true);
        if($key){
            unset($this->observers[$key]);
        }
    }
   
    //set breakouts news
    public function breakOutNews($content) {
        $this->content = $content;
        $this->notify();
    }
   
    public function getContent() {
        return $this->content." (by {$this->name})";
    }
   
    //notify observers(or some of them)
    public function notify() {
        foreach ($this->observers as $value) {
            $value->update($this);
        }
    }
}

/**
 * Observer,that who recieves news
 */
class Reader implements SplObserver{
    private $name;
   
    public function __construct($name) {
        $this->name = $name;
    }
   
    public function update(\SplSubject $subject) {
        echo $this->name.' is reading breakout news <b>'.$subject->getContent().'</b><br>';
    }
}

//classes test
$newspaper = new Newspaper('Newyork Times');

$allen = new Reader('Allen');
$jim = new Reader('Jim');
$linda = new Reader('Linda');

//add reader
$newspaper->attach($allen);
$newspaper->attach($jim);
$newspaper->attach($linda);

//remove reader
$newspaper->detach($linda);

//set break outs
$newspaper->breakOutNews('USA break down!');

//=========output==========
//Allen is reading breakout news USA break down! (by Newyork Times)
//Jim is reading breakout news USA break down! (by Newyork Times)
Anonymous 07-May-2013 10:37
<?php
class Observable implements SplSubject
{
    private
$storage;

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

    function
attach(SplObserver $observer)
    {
       
$this->storage->attach($observer);
    }

    function
detach(SplObserver $observer)
    {
       
$this->storage->detach($observer);
    }

    function
notify()
    {
        foreach (
$this->storage as $obj) {
           
$obj->update($this);
        }
    }
   
//...
}

abstract class
Observer implements SplObserver
{
    private
$observable;

    function
__construct(Observable $observable)
    {
       
$this->observable = $observable;
       
$observable->attach($this);
    }

    function
update(SplSubject $subject)
    {
        if (
$subject === $this->observable) {
           
$this->doUpdate($subject);
        }
    }

    abstract function
doUpdate(Observable $observable);
}

class
ConcreteObserver extends Observer
{
    function
doUpdate(Observable $observable)
    {
       
//...
   
}
}

$observable = new Observable();
new
ConcreteObserver($observable);
przemyslaw dot szpiler at gmail dot com 26-Feb-2012 09:54
<?php

// Example implementation of Observer design pattern:

class MyObserver1 implements SplObserver {
    public function
update(SplSubject $subject) {
        echo
__CLASS__ . ' - ' . $subject->getName();
    }
}

class
MyObserver2 implements SplObserver {
    public function
update(SplSubject $subject) {
        echo
__CLASS__ . ' - ' . $subject->getName();
    }
}

class
MySubject implements SplSubject {
    private
$_observers;
    private
$_name;

    public function
__construct($name) {
       
$this->_observers = new SplObjectStorage();
       
$this->_name = $name;
    }

    public function
attach(SplObserver $observer) {
       
$this->_observers->attach($observer);
    }

    public function
detach(SplObserver $observer) {
       
$this->_observers->detach($observer);
    }

    public function
notify() {
        foreach (
$this->_observers as $observer) {
           
$observer->update($this);
        }
    }

    public function
getName() {
        return
$this->_name;
    }
}

$observer1 = new MyObserver1();
$observer2 = new MyObserver2();

$subject = new MySubject("test");

$subject->attach($observer1);
$subject->attach($observer2);
$subject->notify();

/*
will output:

MyObserver1 - test
MyObserver2 - test
*/

$subject->detach($observer2);
$subject->notify();

/*
will output:

MyObserver1 - test
*/

?>