Recipe 20.2. Creating Your Own Exception Classes


20.2.1. Problem

You want control over how (or if) error messages are displayed to users, even though you're using several third-party libraries that each have their own views on handling errors.

20.2.2. Solution

Take advantage of PHP 5's support for exceptions to create your own exception handler that will do your bidding when errors occur in third-party libraries:

class CustomException extends Exception {   public function __construct($message, $code = 0) {     // make sure everything is assigned properly     parent::__construct($message, $code);     // log what we know     $msg = "------------------------------------------------\n";     $msg .= __CLASS__ . ": [{$this->code}]: {$this->message}\n";     $msg .= $this->getTraceAsString() . "\n";     error_log($msg);   }   // overload the __toString() method to suppress any "normal" output   public function __toString() {     return $this->printMessage();   }   // map error codes to output messages or templates   public function printMessage() {     $usermsg = '';     $code = $this->getCode();     switch ($code) {       case SOME_DEFINED_ERROR_CODE:         $usermsg = 'Ooops! Sorry about that.';         break;       case OTHER_DEFINED_ERROR_CODE:         $usermsg = "Drat!";         break;       default:         $usermsg = file_get_contents('/templates/general_error.html');         break;     }     return $usermsg;   }   // static exception_handler for default exception handling   public static function exception_handler($exception) {     throw new CustomException($exception);   } } // make sure to catch every exception set_exception_handler('CustomException', 'exception_handler'); try {   $obj = new CoolThirdPartyPackage(); } catch (CustomException $e) {   echo $e; }

20.2.3. Discussion

PHP 5 introduced the concept of exceptions to PHP. Exceptions are a common construct in many other languages; they're used to deal gracefully with unforeseen error conditions. This is particularly useful when including third-party library code in your scripts when you're not 100 percent confident how that code will behave in unpredictable circumstances, such as loss of database connectivity, an unresponsive remote API server, or similar acts of randomness.

Exceptions provide your scripts with a TRy/catch structure you used to create a sandboxed section of your script where things can go horribly wrong without hurting anything else:

try {   // do something   $obj = new CoolThing(); } catch (CustomException $e) {   // at this point, the CoolThing wasn't cool   print $e; }

So why use a custom exception, when PHP 5 already provides a perfectly functional exception class? The default exception class doesn't exactly fulfill the graceful part of handling unpredictable results. It just prints out an error message not much different from regular errors. If you want truly flexible handling of these unfortunate events, a custom exception handler allows you to do what you have determined is the most appropriate given the condition.

In the CustomException above, there are two objectives. The first is to log everything you can about what happened; the second is to be as cool as possible from the user's perspective.

The __construct( ) method sets up the exception by calling the parent's constructor (the constructor of the default exception class) to ensure that all possible values are set for use by our custom exception's methods.

Then, you immediately log what you can, using an error_log( ) call that you can replace with a custom error logger of your choice. In keeping with the goal of handling this error gracefully, make sure that your error logger is capable of logging this error without causing another one. For example, if the error you're about to log is related to failed database connectivity, it's probably a good idea if you don't try to log this error to an error log table on that same database server.

From there, the CustomException class is written to expect the calling code to print out the error. However, that is not required behavior. You could just as easily have a try/catch block like this:

try {   // do something   $obj = new CoolThing(); } catch (CustomException $e) {   // at this point, the CoolThing wasn't cool   $e->redirectToOhNoPage(); }

The segment catch (CustomException $e) means that an instance of the CustomException class will be instantiated and assigned to the variable $e. From there, $e is just an object that has some predefined values and methods relating to the problem that caused the exception, but is otherwise a regular object that can be as simple or as complicated as you want it to be.

One primary difference between a standard error handler and exceptions is the concept of recovery. The use case shown in this recipe thus far has a good correlation with the set_error_handler( ) usage from PHP 4 you may already be familiar with. The idea is that your custom handler can contain a clean-up routine that checks the state of the application at the time that the custom exception is caught, cleans up as best as it can, and dies gracefully.

Exceptions can also be used to easily recover from an error in the midst of an application's flow. For example, a try block can have multiple catch blocks that are somewhat neater than a bunch of if/else/else/else blocks:

try {     // do something     $obj = new CoolThing(); } catch (PossibleException $e) {     // we thought this could possibly happen     print "<!-- caught exception $e! -->";     $obj = new PlanB(); } catch (AnotherPossibleException $e) {     // we knew about this possibility as well     print "<!-- aha! caught exception $e -->";     $obj = new PlanC(); } catch (CustomException $e) {     // if all else fails, go to clean-up     $e->cleanUp();     $e->bailOut(); }

In this example, we're able to use the try/catch structure to check for exception conditions without stepping out of the flow of this chunk of code, unless all else truly fails. If we were unable to recover in any of the ways we knew how to in line with the flow of the application, we still have the option of bailing out to a catchall custom exception. We can even throw a new exception inside the catch blocks in order to influence the order in which exceptions bubble up to a try/catch block that may be wrapping the chunk of code currently executing.

20.2.4. See Also

Recipe 8.9 for more on logging errors; documentation on exceptions at http://www.php.net/exceptions.




PHP Cookbook, 2nd Edition
PHP Cookbook: Solutions and Examples for PHP Programmers
ISBN: 0596101015
EAN: 2147483647
Year: 2006
Pages: 445

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net