Previous Next
RSDTable RSIO

RSErrorManager

An Error Management System

Table of Contents

Introduction

RSErrorManager was written to handle the occurence of errors in an application that seperates application from presentation logic. For example the validation of user input should be part of the application logic. This makes an application more secure because the developer using the application class does not have to check the input values every time he/she calls a method of the application class. But notifying the user about one or more invalid input values is part of the presentation logic.

Normally - even if the validation is implemented in the application class - you are forced to check every value individually:


1 <?php
2 if(!$app->isValidUsername($_REQUEST['username'])){
3 echo "invalid username<br>";
4 }
5 if(!$app->isValidPassword($_REQUEST['password'])){
6 echo "invalid password<br>";
7 }
8
9 if($app->isValidUsername($_REQUEST['username']) && $app->isValidPassword($_REQUEST['password'])){
10 $app->createUser($_REQUEST['username'],$_REQUEST['password']);
11 }
12 ...
13 ?>
This is not a good idea! It makes your application class easyly vulnerable.

A more sophisticated application makes it impossible (or at least very hard) to to use it in an unintended way. So in our example this would mean to validate username and password inside the method createUser. But how should the developer using this method know if the values passed were valid or not? One approach is to return a PEAR_Error from this method if something goes wrong. But how to let the programmer know that both values were invalid? Returning two PEAR_Error instances (in an array)? - not a very clean implementation! But this is where RSErrorManager can help out!

Usage

You have to register RSErrorManager as the callback for the occurrence PEAR errors.


1 <?php
2 $errorManager =& new RSErrorManager();
3 PEAR::setErrorHandling(PEAR_ERROR_CALLBACK,array(&$errorManager,'catch'));
4 ?>
Having done so enables this instance of RSErrorManager to catch all PEAR errors. These errors are saved on a stack that can be queried at a later time. The class PEAR_Error itself does not really provide means for classifying an error as an indicator for an invalid username (you could for sure use error codes or the error message to handle this, but that would not be a very clean implementation). Due to the clean implementation of PEAR_Error it is easy to extend it - and this is where RSError comes in: RSError is capable of handling the parameter $userinfo as an associative array with additional information about the error: the object, property and operation the error is related to. Use the method RSErrorManager::raiseRSError() to raise an RSError.

The method createUser from the above example should validate its arguments and raise errors for invalid values:


1 <?php
2 //method
3 function createUser($username, $password){
4 $error = true;
5 if(!$this->isValidUsername($username)){
6 $error = RSErrorManager::raiseRSError('t_user', 'f_username', 'validation', 'The provided username is invalid.', RSERROR_INVALID);
7 }
8 if(!$this->isValidPassword($password)){
9 $error = RSErrorManager::raiseRSError('t_user', 'f_password', 'validation', 'The provided password is invalid.', RSERROR_INVALID);
10 }
11 //...
12 //perform an sql-insert query
13 //...
14 return $error;
15 }
16 ?>
Its return value is true on success or an instance of RSError on failure. Note that if both values are invalid just one error is returned but both are saved on the error stack of RSErrorManager.

If RSErrorManager has been registered as callback we can write code like the following:


1 <?php
2 $app->createUser($_REQUEST['username'],$_REQUEST['password']);
3
4 if($errorManager->errorsOccurredOnProperty('username')){
5 echo "invalid username<br>";
6 }
7 if($errorManager->errorsOccurredOnProperty('password')){
8 echo "invalid password<br>";
9 }
10 ...
11 ?>
This code is much more secure because the worst thing that can happen, is that the developer forgets to query the occurrence of certain errors which just means that the user would not get informed about invalid input. But it does not make our application class vulnerable!

Advanced Error Querying

The following methods are implemented for querying errors from the error stack.

getErrors

RSErrorManager::getErrors() is in fact the only entry point to the error stack. All other methods call this one. getErrors accepts just one (optional) argument: Array $conditions. If the method is called without an argument all errors are returned from the stack. Conditions can be specified by passing an associative array with key-value-pairs. The following returns an array containing all errors that occurred on the object 't_user' and the property 'f_username' while performing the operation 'insert'.


1 <?php
2 $rsErrorManager->getErrors(array(
3 'object' => 't_users',
4 'property' => 'f_username',
5 'operation' => 'insert'
6 )
7 );
8 ?>

countErrors

RSErrorManager::countErrors() counts the errors on the error stack. With the optional argument $conditions you can count just certain errrors.

This method in fact just calls getErrors with whatever you pass as arugment. Note that the argument is optional.

errorsOccurred

RSErrorManager::errorsOccurred() calls countErrors with whatever you pass as argument. This method returns true if the error count is zero. Otherwise false is returned.

countErrorsByProperty

RSErrorManager::countErrorsByProperty() returns the number of occured errors that were related to the property passed as argument.

errorsOccurredOnProperty

RSErrorManager::errorsOccurredOnProperty() returns true if any occurred errors were related to the property passed as argument.

Handling Specific Errors

RSErrorManager provides the following methods for handling specific types of errors:

The default for onFatalError and onPermissionDenied is to call die() with the error message as argument. By inheriting from RSErrorManager and overwriting these methods it is easy to handle database connection errors at a single point - in onFatalError method. You could for example display an error page or send an email about the incident to the programmer. But the most important thing is that you do not have to deal with these errors anywhere else in your application.

Reporting Errors

The method RSErrorManager::reportError() is called by onFatalError and onPermissionDenied. Inherit RSErrorManager and overwrite reoportError to implement an error reporting. Recommendable would be to send an e-mail to the programmer if a fatal error occures (let's say the database is unavailable).

Error Codes

The package RSErrorManager provides as well some useful error codes which are defined in RSErrorCodes.php.

Previous Next
RSDTable RSIO

Documentation generated on Mon, 8 Dec 2003 13:10:24 +0100 by phpDocumentor 1.2.3