Tuesday, 24 July 2012

Throw your own - Nested exceptions

If you think that working with exceptions is hard to understand, exception nesting will look even harder (nested exceptions, chained exceptions, exception bubbling - it is all the same). Well, it is a step above, and it can be used if your application is structured in several layers, but at the end you'll see that it is a concept which is here to make your life easier.

The basic syntax

try
{
// do something that can go wrong
}
catch (Exception $e)
{
throw new Exception( 'Something really gone wrong', 0, $e);
}
Yes, you could see that kind of example at php.net, but it still does not make much sense. Why you caught it at first place, just to throw a new one? To be honest, it is a bad example (just like the examples on php.net). No wonder that you can not understand it.
But lets stick on it for one more moment.
First, you caught exception, like in any other try catch block, and than you passed it to an another one. It means that the second exception has reference to the first one, and who ever catch that second exception, will have access to the first exception too (the previous or the cause). It is hard to notice from this example, but you are probably guessing, it can be very helpful when debugging your application.

Adding useful information


function get_db_connection( )
{
throw new Exception( 'Could not connect to database');
}

function update_email( $username, $email)
{
try
{
$conn = get_db_connection();
$conn->update( "UPDATE user SET email = '$email' WHERE username = '$username'");
}
catch (Exception $e)
{
throw new Exception( 'Failed to save email ['.$email.'] for user ['.$username.']', 0, $e);
}
}

try
{
update_email( 'myusername', 'newmail@foo.com');
}
catch (Exception $e)
{
echo($e->getMessage().'
'.$e->getTraceAsString().'
'); while($e = $e->getPrevious()) echo('Caused by: '.$e->getMessage().'
'.$e->getTraceAsString().'
'); }
In this example we are outputting error information on screen, but in the real life situation, it should go to your log file. That way, when database error occurs, instead of only knowing that database connection was corrupted, you will know exactly which users encountered that problem too.

Pre Php 5.3 problem
Although passing cause exception as argument to a new one is a standard feature in languages which are supporting try/catch syntax, PHP architects included it in version 5.3. So if your application should be compatible with PHP versions before 5.3 you will have to do workaround to support nested exceptions. It can be done, it is not too hard, but I'll write about it in some other article.

Conclusion
The best motivation to use nested exceptions is when you are not satisfied with exceptions that your lower layers are throwing. During the development and debugging, when you encounter such situation, just catch it and add additional useful information. In most cases it will assume that you are using some external libraries (which original source you should not change) and that your application has multiple layers of functionality. Do not forget that your logging mechanism should be aware and capable of nested exceptions too.

No comments:

Post a Comment