SplSubject
The SplObserver class, together with SplSubject, provides a interface for implementing the Observer pattern.
To quote wikipedia: The essence of this pattern is that one or more objects (called observers or listeners) are registered (or register themselves) to observe an event which may be raised by the observed object (the subject). (The object which may raise an event generally maintains a collection of the observers.)
First implementation
Let’s look at a very simple implementation where we have two objects, one that implements SplObserver and one that implements SplSubject and see how these fit together:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <?php class MySubject implements SplSubject { private $elements; public function __construct() { $this->elements = array(); } public function attach( SplObserver $observer ) { $this->elements[] = $observer; } public function detach( SplObserver $observer ) { // Not implemented here } public function notify() { foreach( $this->elements as $obj ) { $obj->update( $this ); } } } class MyObserver implements SplObserver { public function update( SplSubject $subject ) { echo "The subject is updating me\n"; } } $subject = new MySubject(); $observer = new MyObserver(); $subject->attach( $observer ); $subject->notify(); |
This will output: The subject is updating me
Here we can see that we attach a observer to the subject, and when we call notify() it iterates over all the observers and calls update() on the object, this way we can notify many objects that a content object, for example, has changed its properties.
Traffic data
To further illustrate the use of SplSubject lets say you have a program that looks at your apache log and generates a pie graph and a bar graph that you want to display on your web site. We can then create a TrafficData object that generates the actual data and two objects that implement the observer interface ( these will create the actual graph ):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | <?php class TrafficData implements SplSubject, Countable{ private $elements; private $count; public function __construct() { $this->elements = array(); $this->count = 0; } public function setData( $count ) { $this->count = $count; } public function attach( SplObserver $observer ) { $this->elements[] = $observer; } public function detach( SplObserver $observer ) { // Not implemented here } public function notify() { foreach( $this->elements as $obj ) { $obj->update( $this ); } } public function count() { return $this->count; } } class BarGraph implements SplObserver { public function update( SplSubject $subject ) { if( $subject instanceof TrafficData ) { echo "Generating bar graph with : " . $subject->count() . " elements\n"; } else { echo "Could not recognice subject\n"; } } } class PieGraph implements SplObserver { public function update( SplSubject $subject ) { if( $subject instanceof TrafficData ) { echo "Generating pie graph with : " . $subject->count() . " elements\n"; } else { echo "Could not recognize subject\n"; } } } $data = new TrafficData(); $bar = new BarGraph(); $pie = new PieGraph(); $data->attach( $bar ); $data->attach( $pie ); $data->notify(); $data->setData( 349 ); $data->notify(); |
When running this you will get the following output:
Generating bar graph with : 0 elements
Generating pie graph with : 0 elements
Generating bar graph with : 349 elements
Generating pie graph with : 349 elements
With this class structure we have a single class that maintains the data, and whenever we call notify() on it the observers attached to the class will create a new graph based on their implementation. ( we also make the subject implement Countable here to make sure we know that the data provider can tell us how much traffic we have received )
The SplSubject/SplObserver classes are a good pair of classes if you are creating a plugin-based system where you want the observers to act based on information they know about, not the other way around, this way you can keep a clean codbase and develop observers as you need them and just plug them into your subject.
Hi!
Thanks for spotting that issue, updated the code to test for correct SplSubject implementation.