Using Exception Objects to Handle Errors
Although functions like trigger_error() and set_error_handler() give us a lot of flexibility with raising and handling errors, they do have limitations. For example, if a piece of code calls a class method and an error occurs in that method, it would be nice if the method could simply tell the calling code about the error, rather than having to raise an error with trigger_error() and go through a central error handler. That way the calling code could take action to correct the problem, making the application more robust.
One simple, common way to achieve this is to get a function or method to return a special error value, such as -1 or false. The calling code can then inspect the return value and, if it equals the error value, it knows there was a problem. However, this can get unwieldy when you start working with deeply nested function or method calls, as the following code shows:
class WarpDrive {
public function setWarpFactor( $factor ) {
if ( $factor >=1 && $factor <= 9 ) {
echo "Warp factor $factor<br />";
return true;
} else {
return false;
}
}
}
class ChiefEngineer {
public function doWarp( $factor ) {
$wd = new WarpDrive;
return $wd->setWarpFactor( $factor );
}
}
class Captain {
public function newWarpOrder( $factor ) {
$ce = new ChiefEngineer;
return $ce->doWarp( $factor );
}
}
$c = new Captain;
if ( !$c->newWarpOrder( 10 ) ) echo "She cannot go any faster!<br />";
The WarpDrive::setWarpFactor() function returns true if the function succeeded, and false otherwise (if the warp factor was less than 1 or greater than 9). This return value then needs to be passed through both the ChiefEngineer::doWarp() method and the Captain::newWarpOrder() method to reach the calling code, which can then identify and report on the error. It's not uncommon to find at least this level of nested method calls in complex applications.
Another problem is that simply returning false doesn't tell the calling code much about what went wrong. What's more, when a method has to return an error value, it can't then easily return anything else (because methods and functions can return only one thing at a time).
Fortunately, PHP gives us exceptions, which are a much more elegant way of triggering and handling error conditions. Rather than returning a single error value, our method or function can create a rich Exception object that includes detailed information about the problem, then throw the object up to the calling code to handle, or catch.
Another nice feature of exceptions is that the calling code doesn't have to catch an exception if it doesn't want to; if it ignores it, the exception is re-thrown up the calling chain until it is caught. If no code catches the exception, the script halts with a fatal error and the exception is logged or displayed to the user (depending on our log_errors and display_errors settings). So by using exceptions, any problem can either be handled automatically by another part of the application or, if all else fails, reported to the developer or user. This allows applications to be much more flexible and robust in their handling of error scenarios.
Note: If we don't want uncaught exceptions to raise fatal errors, we can create our own exception handler to deal with the exceptions (much like creating our own error handler).
See http://www.php.net/manual/en/function.set-exception-handler.php for details.
Throwing Exceptions
Here's how to create and throw an exception when an error occurs in our code:
throw new Exception;
We can also pass an optional error message to the Exception object when it's created (this is generally a good idea):
throw new Exception( "Oops, something went wrong" );
If we have a lot of different error messages in our application, it can help to give each exception a numeric error code to distinguish it. To add an error code to our thrown exception, pass it as the second argument when creating the Exception object:
If we don't catch our thrown exception at some other point in our code, eventually it bubbles up to the top level of our script, displaying an error message similar to the following:
PHP Fatal error: Uncaught exception 'Exception' with message 'Oops, something went wrong' in script.php:4
Stack trace:
#0 {main}
thrown in script.php on line 4
This tells us that an exception occurred that wasn't handled by the script itself, gives us the error message, and informs us that the exception was thrown in the main (top-level) part of the script.
Although functions like trigger_error() and set_error_handler() give us a lot of flexibility with raising and handling errors, they do have limitations. For example, if a piece of code calls a class method and an error occurs in that method, it would be nice if the method could simply tell the calling code about the error, rather than having to raise an error with trigger_error() and go through a central error handler. That way the calling code could take action to correct the problem, making the application more robust.
One simple, common way to achieve this is to get a function or method to return a special error value, such as -1 or false. The calling code can then inspect the return value and, if it equals the error value, it knows there was a problem. However, this can get unwieldy when you start working with deeply nested function or method calls, as the following code shows:
class WarpDrive {
public function setWarpFactor( $factor ) {
if ( $factor >=1 && $factor <= 9 ) {
echo "Warp factor $factor<br />";
return true;
} else {
return false;
}
}
}
class ChiefEngineer {
public function doWarp( $factor ) {
$wd = new WarpDrive;
return $wd->setWarpFactor( $factor );
}
}
class Captain {
public function newWarpOrder( $factor ) {
$ce = new ChiefEngineer;
return $ce->doWarp( $factor );
}
}
$c = new Captain;
if ( !$c->newWarpOrder( 10 ) ) echo "She cannot go any faster!<br />";
The WarpDrive::setWarpFactor() function returns true if the function succeeded, and false otherwise (if the warp factor was less than 1 or greater than 9). This return value then needs to be passed through both the ChiefEngineer::doWarp() method and the Captain::newWarpOrder() method to reach the calling code, which can then identify and report on the error. It's not uncommon to find at least this level of nested method calls in complex applications.
Another problem is that simply returning false doesn't tell the calling code much about what went wrong. What's more, when a method has to return an error value, it can't then easily return anything else (because methods and functions can return only one thing at a time).
Fortunately, PHP gives us exceptions, which are a much more elegant way of triggering and handling error conditions. Rather than returning a single error value, our method or function can create a rich Exception object that includes detailed information about the problem, then throw the object up to the calling code to handle, or catch.
Another nice feature of exceptions is that the calling code doesn't have to catch an exception if it doesn't want to; if it ignores it, the exception is re-thrown up the calling chain until it is caught. If no code catches the exception, the script halts with a fatal error and the exception is logged or displayed to the user (depending on our log_errors and display_errors settings). So by using exceptions, any problem can either be handled automatically by another part of the application or, if all else fails, reported to the developer or user. This allows applications to be much more flexible and robust in their handling of error scenarios.
Note: If we don't want uncaught exceptions to raise fatal errors, we can create our own exception handler to deal with the exceptions (much like creating our own error handler).
See http://www.php.net/manual/en/function.set-exception-handler.php for details.
Throwing Exceptions
Here's how to create and throw an exception when an error occurs in our code:
throw new Exception;
We can also pass an optional error message to the Exception object when it's created (this is generally a good idea):
throw new Exception( "Oops, something went wrong" );
If we have a lot of different error messages in our application, it can help to give each exception a numeric error code to distinguish it. To add an error code to our thrown exception, pass it as the second argument when creating the Exception object:
If we don't catch our thrown exception at some other point in our code, eventually it bubbles up to the top level of our script, displaying an error message similar to the following:
PHP Fatal error: Uncaught exception 'Exception' with message 'Oops, something went wrong' in script.php:4
Stack trace:
#0 {main}
thrown in script.php on line 4
This tells us that an exception occurred that wasn't handled by the script itself, gives us the error message, and informs us that the exception was thrown in the main (top-level) part of the script.
No comments:
Post a Comment