Observer pattern in PHP
I've recently pretty much fell in love with an observer design pattern. You can basically build the whole application on it using awesome power of event driven programming. What exactly is an observer pattern ? All the theory is accessible on Wikipedia. Basically we have an object you want to monitor ( observe ) for any changes. Most of the time this object just fires out events and we want to listen to them. We can have more objects observing one or more other objects so basically this can be M:N relationship. Classic example in database world are triggers. When the table is updated for example the listening trigger is fired. In window applications when the user clicks a button the event is fired and listeners are notified to handle the event. In the following example we have a validator object checking for a valid email address that fires two types of events we would like to listen to. Those types are obviously valid / invalid email address. We also implement two listeners, one for each event type, listening to the validator. The first one would just be some kind of an error logger, logging the invalid inputs and the second one would wite the correct email address into database or something. We start with definition of our Observable interface. We basically just want to add observers and fire events. We also want to be able to listen just to certain types of events so we do not need to notify observers that do not listen to the currently fired event.
interface IObservable{
public function addObserver( IObserver $objObserver, $strEventType );
public function fireEvent( $strEventType );
}
So any object that we want to observe must implement the IObservable interface. Let's define how the observer should look like. Basically the observer just needs to know what object fired the event and what is the type of that event so it could be processed.
interface IObserver{
public function notify( IObservable $objSource, IEventArguments $objArguments );
}
So far so good. Now we make our email validator that implements IObservable interfaces and defines two types of events.
class EmailValidator implements IObservable{
const EVENT_EMAIL_VALID = 1; const EVENT_EMAIL_INVALID = 2;
protected $strEmailAddress;
protected $aryObserversArray;
public function __construct( $strEmailAddress ) { $this->strEmailAddress = $strEmailAddress; $this->aryObserversArray = array( array() ); }
public function setEmailAddress( $strEmailAddress ) { $this->strEmailAddress = $strEmailAddress; }
public function getEmailAddress() { return $this->strEmailAddress; }
public function validate() { if( preg_match( '/^[a-zA-Z][\w\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$/', $this->strEmailAddress ) ) { $this->fireEvent( EmailValidator::EVENT_EMAIL_VALID ); } else { $this->fireEvent( EmailValidator::EVENT_EMAIL_INVALID ); } }
public function addObserver( IObserver $objObserver, $strEventType ) { $this->aryObserversArray[$strEventType][] = $objObserver; }
public function fireEvent( $strEventType ) { if( is_array( $this->aryObserversArray[$strEventType] ) ) { foreach ( $this->aryObserversArray[$strEventType] as $objObserver ) { $objObserver->notify( $this, $strEventType ); } } }
}
The method that does all the work is validate() method. It just validates the give email address and fires the valid event or invalid event. That's all, the observers just takes care of the rest. Let's make two observers each listening to different type of the event. There's the ErrorLogger observer that wants to be norified whenever the validated email address is incorrect. It can write something to log or just alert the user.
class ErrorLogger implements IObserver{
public function notify( IObservable $objSource, $strEventType ) { if( $strEventType == EmailValidator::EVENT_EMAIL_INVALID && $objSource instanceof EmailValidator ) { printf( 'Error: %s is not a valid email address.', $objSource->getEmailAddress() ); } }
}
Simple, isn't it. ErrorLogger just implements the notify() method and checks whether the source of the event is type of EmailValidator and the event type is EVENT_EMAIL_INVALID and then prints the error message. The obsever for EVENT_EMAIL_VALID looks very similiar.
class DatabaseWriter implements IObserver{
public function notify( IObservable $objSource, $strEventType ) { if( $strEventType == EmailValidator::EVENT_EMAIL_VALID && $objSource instanceof EmailValidator ) { printf( 'Email address %s is valid and was stored in database.', $objSource->getEmailAddress() ); } }
}
And that's it. Now we just need to create our EmailValidator object, add some observers to it and let it validate a couple of email addresses.
$objValidator = new EmailValidator( 'valid@email.com' );$objValidator->addObserver( new ErrorLogger(), EmailValidator::EVENT_EMAIL_INVALID );$objValidator->addObserver( new DatabaseWriter(), EmailValidator::EVENT_EMAIL_VALID );$objValidator->validate(); $objValidator->setEmailAddress( 'not_a_valid_address' );$objValidator->validate();
Running this example should result in something like this:
Email address valid@email.com is valid and was stored in database.
Error: not_a_valid_address is not a valid email address.
This was just a very simple example demonstrating the power of observer pattern. I am sure you'll find many uses for it. If you have any ideas or anything just leave me some
comments here.


8 comments to “Observer pattern in PHP”
February 23rd, 2009 at 9:46 pm
I understand the Observer pattern, but I don’t know how it can be used on a website. It can be an helper that return a constant, but an observer is realy strange.
Thanks.
February 23rd, 2009 at 11:45 pm
It’s useful as a plugin system, for extensible apps
February 24th, 2009 at 9:05 am
SPL has Observer & Subject functionality already built in. Why not implement those?
interface SplObserver {
public function update(SplSubject $subject) {}
}
interface SplSubject {
public function attach(SplObserver $observer) {}
public function detach(SplObserver $observer) {}
public function notify() {}
}
February 24th, 2009 at 10:31 am
Agree with the last comment, why not do it following the Observer Pattern defined in the (Standard) SPL Library?
February 24th, 2009 at 8:38 pm
well .. yeah SPL provides the needed interfaces, I just wanted to create all from scratch to give a better idea. In fact I realy do think observer pattern is amazing and you can build whole applications on it. The plugin system is an ideal example.
February 25th, 2009 at 9:02 am
The observer patterns is one of the most natural feeling patterns there is. This is because of the way it interacts with other objects.
OOP states that you have to encapsulate your functionality in your classes and that no object must serve more than one purpose. With the observer pattern "Observable A" just notifies "Observer A", "Observer B", etc that it has finished a sertain task and the Observers will act accordingly.
This is a clean way to couple objects together when they do not share the same purpose i.e. a logging observer that records your errors.
August 31st, 2009 at 2:11 pm
Why you use IEventArguments type as 2nd argument in notify() method of observer class?
This produces error when notify() is implemented in ErrorLogger and DatabaseWritter! If i implement this method exactly as in interface then error will appear in fireEvent() : "Catchable fatal error: Argument 2 passed to DatabaseWriter::notify() must be an instance of IEventArguments, integer given"
Did you tested your code?
August 9th, 2010 at 4:33 am
@oreales & @Pelle
Maybe he didn’t use the SPL interfaces because you have to implicitly use SplSubject and SplObserver in the class to implement the interface, which is pretty confining if you want you attach special observers to special subjects and vice versa.