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.