Home arrow PHP arrow Page 4 - An Introduction to the Observer Pattern in PHP

Applying the Observer design pattern - PHP

If you have reached the point in your programming life where you are using design patterns, you will want to read this article. The first of a three-part series, it covers the Observer pattern, which can be just the thing for situations where objects need to send information to a centralized mechanism.

TABLE OF CONTENTS:
  1. An Introduction to the Observer Pattern in PHP
  2. Building a practical example
  3. Adding a basic error logger
  4. Applying the Observer design pattern
By: Alejandro Gervasio
Rating: starstarstarstarstar / 15
July 17, 2006

print this article
SEARCH DEV SHED

TOOLS YOU CAN USE

advertisement

As I explained in the previous section, when a basic error login class was added to the message handling application, the independence of their objects was compromised; therefore, an alternative approach should be implemented to avoid this issue. For this reason, I’ll apply the Observer pattern, thus the application can retain the objects’ independence without sacrificing functionality.

Here’s how he application now looks, after implementing the Observer pattern:

  // define the abstract 'MessageObserver' interface
  interface MessageObserver{
    function displayStatus(MessageSaver $obj,$notifMsg);
  }
  // define 'ErrorLogger' class
  class ErrorLogger implements MessageObserver{
    function displayStatus(MessageSaver $obj,$notifMsg){
        echo __CLASS__.' notified the following error message:
'.$notifMsg;
    }
  }
  // define 'MessageConverter' class
  class MessageConverter{
    private $message;
    public function __construct($message){
        if(!is_string($message)){
            throw new Exception('Input message must be a
string!');
        }
        $this->message=strtoupper($message);
    }
    // return uppercased message
    public function getMessage(){
        return $this->message;
    }
  }
  // define 'MessageSaver' class
  class MessageSaver{
    const LENGTH_ERROR='Invalid length of message.';
    const WORD_ERROR='Message contains invalid words.';
    private $msgPath='defaultPath/messages.txt';
    private $badWords=array('BAD WORD1','BAD WORD2','BAD WORD3');
    private $observers=array();
    private $msgConv;
    public function __construct(MessageConverter $msgConv){
        $this->msgConv=$msgConv;
    }
    // add new observer
    public function addObserver(MessageObserver $obj){
        $this->observers["$obj"]=$obj;
    }
    // remove existent observer
    public function removeObserver(MessageObserver $obj){
        delete($this->observers["$obj"]);
    }
    // notify to all the observers that an error occurred
    protected function notifyObservers($notifMsg){
        foreach($this->observers as $observer){
            $observer->displayStatus($this,$notifMsg);
        }
    }
    public function save(){
        // check for message length
        if(strlen($this->msgConv->getMessage())>255){
            // notify length error to all the observers
            $this->notifyObservers(self::LENGTH_ERROR);
        }
        // check for bad words
        if(in_array($this->msgConv->getMessage(),$this-
>badWords)){
            // notify word error to all the observers
            $this->notifyObservers(self::WORD_ERROR);
        }
        // checking was passed, then save message to file
        if(!$fp=fopen($this->msgPath,'a+')){
            throw new Exception('Error opening messages file.');
        }
        fwrite($fp,$this->msgConv->getMessage()."n");
        fclose($fp);
    }
  }

As you can see, the above application looks slightly different when compared to the previous incarnation. All the changes that I introduced are minor yet significant, therefore pay close attention to the following explanation.

In order to apply the Observer pattern, first I defined a “MessageObserver” interface, which exposes only an abstract “displayStatus()” method. Next, I created a basic “ErrorLogger” class, which implements this interface and also inherits the previous method.

Here’s where you can spot the first difference. This class actually won’t log any errors, but instead will display notices from the respective “MessageSaver” objects. So, where’s the advantage in defining the logger class like this? Well, by doing so, all the objects of type “MessageSaver” will be responsible only for saving messages to a text file, period. Any eventual errors will be sent to the observer object (in this case the “ErrorLogger” class), and only this object will decide the course of action to take. Do you now see how all the involved objects are decoupled from the whole application? I hope you do.

Once the observer class was created, I redefined the “MessageSaver” class, in order to add the “addObserver()”,“removeObserver()” and “notifyObserver()” methods to it. As you may have guessed, the first two methods simply add and remove observer objects from the class in question, assuming that the application will implement eventually other observers.

The “notifyObserver()” method loops over all the observers (for this example, I have only one) and calls their “displayStatus()” method, in this way showing all the notices that were sent by the “MessageSaver” object when trying to save an offending message. This condition is demonstrated by the “save()” method, which is listed below:

  public function save(){
    // check for message length
    if(strlen($this->msgConv->getMessage())>255){
        // notify length error to all the observers
        $this->notifyObservers(self::LENGTH_ERROR);
    }
    // check for bad words
    if(in_array($this->msgConv->getMessage(),$this->badWords)){
        // notify word error to all the observers
        $this->notifyObservers(self::WORD_ERROR);
    }
    // checking was passed, then save message to file
    if(!$fp=fopen($this->msgPath,'a+')){
        throw new Exception('Error opening messages file.');
    }
    fwrite($fp,$this->msgConv->getMessage()."n");
    fclose($fp);
  }

At this point, the previous example allowed me to apply the Observer pattern, which results in the decoupling of all the pertinent objects. It also results in the implementation of a handy mechanism for reflecting all the objects' changes at the application’s core level, and without having to write convoluted code!

Want to see how the application works, after coding the respective observer class? Right, here’s an example:

  try{
    // instantiate 'MessageConverter' object
    $msgConv=new MessageConverter('bad word1');
    // instantiate 'MessageSaver' object
    $msgSaver=new MessageSaver($msgConv);
    // add 'MessageObserver'
    $msgSaver->addObserver(new ErrorLogger());
    // save message to file
    $msgSaver->save();
  }
  catch(Exception $e){
    echo $e->getMessage();
    exit();
  }

The above script displays the following message:

ErrorLogger notified the following error message: Message
contains invalid words.

As you can see, the “MessageObserver” class displays the above notification, since I passed an invalid message to the “MessageConverter” class. This demonstrates the power of the Observer pattern.

As homework, try adding more observers to the “ErrorLogger” class, and see what happens in each case, so you gain a solid grasp of this pattern. It’s really fun!

To wrap up

In this first article, you learned how to apply the Observer pattern inside a simple PHP application. Of course, feel free to treat the source code like a starting example and introduce some improvements to it. It’s the best way to grasp the concepts for how this pattern works.

In the next tutorial, I’ll show you how to apply the Observer pattern in a real-world case: server-side data validation. See you there! 



 
 
>>> More PHP Articles          >>> More By Alejandro Gervasio
 

blog comments powered by Disqus
escort Bursa Bursa escort Antalya eskort
   

PHP ARTICLES

- Hackers Compromise PHP Sites to Launch Attac...
- Red Hat, Zend Form OpenShift PaaS Alliance
- PHP IDE News
- BCD, Zend Extend PHP Partnership
- PHP FAQ Highlight
- PHP Creator Didn't Set Out to Create a Langu...
- PHP Trends Revealed in Zend Study
- PHP: Best Methods for Running Scheduled Jobs
- PHP Array Functions: array_change_key_case
- PHP array_combine Function
- PHP array_chunk Function
- PHP Closures as View Helpers: Lazy-Loading F...
- Using PHP Closures as View Helpers
- PHP File and Operating System Program Execut...
- PHP: Effects of Wrapping Code in Class Const...

Developer Shed Affiliates

 


Dev Shed Tutorial Topics: