Showing posts with label advanced. Show all posts
Showing posts with label advanced. Show all posts

Wednesday, July 25, 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.

Tuesday, October 4, 2011

Throw your own - Custom Exceptions

The key to understanding custom exception concept lies in knowing how to catch them. You have to know syntax, and you have to know how to use it.

First, the syntax itself
try 
{}
catch (CustomException $e)
{}
catch (OtherException $e)
{}
catch (Exception $e)
{}
Basicaly it looks like an switch/case statement. The above code could be written as the next one, and all will be valid and will work perfectly. The try catch syntax is actually automatically handled multiple if statement!
try 
{}
catch (Exception $e)
{
	if ($e instanceof CustomException)
	{}
	else if ($e instanceof OtherException)
	{}
	else
	{}
}


Throw your own
Note that by distinguishing exceptions you can decide which are the fatal errors and which exceptions are only passing you an information that something is wrong and it is a handled case. This means that you have to use Exceptions not only on critical situations. Throw them for your handled situations too.

class MyAuthException extends Exception
{}

class MyTest
{
	private $_connected;
	
	public function __construct( $connected)
	{
		$this->_connected	=	$connected;
	}
	
	
	public function printData( $auth)
	{
		try 
		{
			if ($auth != 'foo')
				throw new MyAuthException( 'Not an [foo] exception');
			if (!$this->_connected);
				throw new Exception( 'System is not connected');
				
			echo 'Authorized!';
		}
		catch (Exception $e)
		{
			die($e->getMessage().'
'.$e->getTraceAsString().'
'); } } } //$test = new MyTest( true); $test = new MyTest( false); try { $test->printData( 'foo'); // $test->printData( 'bar'); } catch (MyAuthException $e) { echo 'Not authorized'; } catch (Exception $e) { die($e->getMessage().'
'.$e->getTraceAsString().'
'); }


Be wise, work with exceptions
// BAD PRACTICE
$action->fooBar();
if ($action->isError())
{
	if ($action->getErrorCode() == 0)
	{}
	else if ($action->getErrorCode() == 1)
	{}
	else
	{}
}
else
{}
One could think that handling multiple types of exceptions can be replaced by simple if then else. The greatest disadvantage of this solution is not that you have to preserve your error state in your objects. The main problem is that you have to perform that check every time you have to handle that object. That leads you to situations where you will forget to check the error state.

Look at the same example written with exceptions. Less code and no additional error based methods you have to add to objects.
// GOOD PRACTICE
try 
{
	$action->fooBar();
}
catch (CustomException $e)
{}
catch (Exception $e)
{}
If you, in some case, forget to wrap fooBar() method in the try/catch block, your upper, application layers will catch that renegade exception and handle it.