Share this question

Welcome to Teachnovice Q&A, where you can ask questions and receive answers from other members of the community.

This is a collaboratively edited question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

Observer pattern in PHP?

0 like 0 dislike

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( '' );$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 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.

asked Oct 21, 2015 by pak786 (2,100 points)  

Your answer

Your name to display (optional):
Privacy: Your email address will only be used for sending these notifications.
Anti-spam verification:
To avoid this verification in future, please log in or register.