HomePHP Page 3 - An Introduction to the Observer Pattern in PHP
Adding a basic error logger - 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.
Adding a basic error logger to the message handling application that you learned before is in fact a straightforward process, which can be achieved by defining a simple error logging class. The code listed below illustrates how the application looks after including a new “ErrorLogger” class. Please, take a look:
// 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 $errorLogger; private $msgConv; public function __construct(MessageConverter $msgConv){ $this->msgConv=$msgConv; $this->errorLogger=new ErrorLogger(); } public function save(){ // check for message length if(strlen($this->msgConv->getMessage())>255){ // log message's length error by ErrorLogger object $this->errorLogger->logLengthError (self::LENGTH_ERROR); } // check for bad words if(in_array($this->msgConv->getMessage(),$this- >badWords)){ // log word error by ErrorLogger object $this->errorLogger->logLengthError(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); } } // define 'ErrorLogger' class class ErrorLogger{ private $mailAdmin='mailadmin@domain.com'; // log error to file public function logLengthError($errorMsg){ if(!error_log($errorMsg,0)){ throw new Exception('Could not log error to system.'); } } // email error to system administrator public function logWordError($errorMsg){ if(!mail($this->mailadmin,$errorMsg,'Message is not valid!')){ throw new Exception('Could not email error to system administrator.'); } } }
Now, the message handling application's functionality is increased, because I added to it a basic error logging class. As you can see, this class will log errors either to the system logger or by email to the corresponding system administrator, according to the type of error raised at runtime.
Based on the prior improved structure, the message handling application eventually might be used as follows:
try{ // instantiate 'MessageConverter' object $msgConv=new MessageConverter('This message will be converted to uppercase!'); // instantiate 'MessageSaver' object $msgSaver=new MessageSaver($msgConv); // save message to file $msgSaver->save(); } catch(Exception $e){ echo $e->getMessage(); exit(); }
At this point, I constructed a simple message saving system, which is comprised basically of three independent classes. It is capable of delegating program control to an error logger when things go wrong, in this way limiting the scope of each involved object.
Nevertheless, even when all the classes seem to fit each other quite well, there’s a problem that can easily break down the model of the whole application. Notice how the definition of the “MessageSaver” class has been convoluted with messy code, since the class now has to deal with the direct instantiation of an “ErrorLogger” object, in order to perform all the error login operations.
This is truly undesirable, particularly if I want to keep all the objects decoupled from the rest of the application. It gets even worse if I aggregate new classes to the application, while trying to maintain the correct scope of all the objects. Certainly, I would end up with a set of classes, which should perform the instantiation of other objects, quickly breaking down the model of highly independent classes.
Thus, the question is: can I restructure the message handling application in such a way that it can work with decoupled objects, while maintaining the same functionality? Fortunately, the answer is yes, and here’s where the Observer pattern comes in.
In the upcoming section, I’ll show you how to use this pattern to solve the issues that I discussed before. Therefore, click on the link below to learn more.