Observer pattern in PHP
Tags: patterns, php, observer
Паттерн Наблюдатель (Observer) в PHP
Паттерн Спостерігач (Observer) в PHP
| ← Working with RAR, LZF and BZ2 Compression Formats in PHP | JavaScript defining and using custom events → |
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 write 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.
Hide code highlighting
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.
Hide code highlighting
So far so good. Now we make our email validator that implements IObservable interfaces and defines two types of events.
Hide code highlighting
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 notified whenever the validated email address is incorrect. It can write something to log or just alert the user.
Hide code highlighting
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.
Hide code highlighting
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.
Hide code highlighting
Running this example should result in something like this:
Hide code highlighting
This was just a very simple example demonstrating the power of observer pattern. I am sure you'll find many uses for it.
Original: Observer pattern in PHP
Hide code highlighting
1 2 3 4 5 6 | 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.
Hide code highlighting
1 2 3 4 5 | interface IObserver { public function notify( IObservable $objSource, $objArguments ); } |
So far so good. Now we make our email validator that implements IObservable interfaces and defines two types of events.
Hide code highlighting
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 | 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 notified whenever the validated email address is incorrect. It can write something to log or just alert the user.
Hide code highlighting
1 2 3 4 5 6 7 8 9 10 11 12 | 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.
Hide code highlighting
1 2 3 4 5 6 7 8 9 10 11 12 | 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.
Hide code highlighting
1 2 3 4 5 6 7 8 | $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:
Hide code highlighting
1 2 | 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.
Original: Observer pattern in PHP
Rating:




<< Please, rate this articleRelated articles:
A caching pattern for models
Working with RAR, LZF and BZ2 Compression Formats in PHP
MVC in smaller web applications
Fulltext searches with Xapian and PHP