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.

Thursday, September 29, 2011

Throw your own - Losing the fear of Exceptions

The best way to lose the fear of exceptions is to start throwing them. Recipe is simple: whenever you come to case that something is wrong and your function or method can not accomplish its task, just throw an exception.

Example 1
My favorite case is when I'm using a configuration values in my applications. Ops, there is no value at all, not an numeric value? I'll just throw an exception and stop thinking about that. This case is especially important if your application will have several installations. This way you are ensuring that nobody (including you) can not forget to fill in configuration. Or, when such case happen anyway, problem will be immediately identified.
class MyIni
{

	private $_config;
	
	public function __construct( $config)
	{
		$this->_config	=	$config;
	}
	
	public function getProductId()
	{
		if (!isset($this->_config['productId']))
			throw new Exception( 'Required config parameter [productId] is not set');		
		if (!is_numeric( $this->_config['productId']))
			throw new Exception( 'Config parameter [productId] has not numeric value ['.$this->_config['productId'].']');		
		
		return $this->_config['productId'];
	}
}

//$test		=	new MyIni( array( 'productId' => 123)); // valid value
//$test		=	new MyIni( array( )); // not defined
$test		=	new MyIni( array( 'productId' => 'FooBar')); // not numeric

try {
	$product_id	=	$test->getProductId();
	echo 'Product Id is '.$product_id;	
} catch (Exception $e) {
	die($e->getMessage().'
'.$e->getTraceAsString().'
'); }

Example 2
Another favorite one is when switching between some predefined values. Someone passed not defined value? OK, here is an exception. This case is important when your component will be used by another developers too.
class MySwitch
{
	const GENDER_MALE		=	'MALE';
	const GENDER_FEMALE		=	'FEMALE';
	
	public static function getOpositeGender( $gender)
	{
		if (self::GENDER_FEMALE == $gender)
			return self::GENDER_MALE;
		if (self::GENDER_MALE == $gender)
			return self::GENDER_FEMALE;
		throw new Exception( 'Invalid gender value ['.$gender.']');
	}
}


try {
//	$gender	=	MySwitch::GENDER_MALE; // valid value
	$gender	=	'FooBar';	//	not defined value
	echo 'Oposite from '.$gender.' is '.MySwitch::getOpositeGender( $gender);	
} catch (Exception $e) {
	die($e->getMessage().'
'.$e->getTraceAsString().'
'); }

There is a good practice which can be applied all the time during the debug or test phase of your development project. When you encounter to an any problem, warning, notice, missing data or configuration, stop and ask your self do you know what exactly is wrong. First go to your source and add an exception which will describe what happened. Try it again and after you done that, fix what was wrong at the first place.

As the result, at the beginning you will more often see fatal error messages. But when you see it, you will exactly know what happened, where is the problem and what to do next. You will actually be glad that you wrote those throw exception statements.

Saturday, February 26, 2011

Returning the null value from a method

This example demonstrates the drawbacks of returning null from your methods. I allays preach to my colleagues that when something is wrong, your method should throw an exception instead of returning a null.
But sometimes, especially if I'm writing some tools for myself, because of the laziness, I use the return null too.

Few days ago I done that, and very soon I released that it was mistake which caused me unnecessary problems.

Story goes like this: There is an initial list of ids and result of a process is a array of fully populated objects. I use that procedure on two places in my tool: for generating RSS file and for generating a view script. The tool is written in Zend Framework, it uses a log file, when exception occurs displays a customized error page and sends a email to me.

Initially it was all flawless, but at some point, I released that I'm not able to read data for some of initial ids. So I modified a loading method in something like this:

public function loadItem( $id)
{
	$raw_data	=	$this->_getRawData( $id);
 	if ($this->_testRawData( $raw_data))
   		return $this->_parseData( $raw_data); 
 	return null;
}

Accordingly, I modified the controller part to:

public function rssAction() 
{   
	$ids	=	$this->getRssIds();   
 	$items 	= 	array();  
 	foreach ($ids as $id)   
 	{
  		$item = $this->manager->loadItem( $id);
  		if ($item)      
   			$items[] = $item;
  	}   
 	$this->view->items = $items;
}

I done it, tested the RSS feature and it was as expected, all OK. I've done few more fixes, and at last, after some time, I wanted to see the preview too.
Cold shower. PHP notices, warnings, fatal error. Undefined variable, call to a method on null object.... Why, how, what the hell ...?
Standard debugging procedure: I'm checking the log file. Nothing to find there. Next is to check the view script and associated action. Soon I found that the problem was that I had null values in array which supposed to be populated with objects. I simply forgot that I was using the same method on two places.

public function previewAction() 
{  
	$ids 	= 	$this->getPreviewIds();   
	$items 	= 	array();  
	foreach ($ids as $id)      
 		$items[] = $this->manager->loadItem( $id);
	$this->view->items = $items; 
}

At the end, as I'm experienced developer and this was my "in house" tool, I found and fixed it very quickly (still bad solution: again I put a test if the returned object is null), but it raised a really good question: When you are doing a bigger scale project, where you can not have all the things in your head, what is the right solution and what would be the consequences?

First the correct code variant:

// somewhwere in my manager class

public function loadItem( $id) 
{ 
 	$raw_data	=	$this->_getRawData( $id); 
 	if ($this->_testRawData( $raw_data))
   		return $this->_parseData( $raw_data); 
 	throw new ItemNotFoundException( 'Item ['.$id.'] could not be loaded');
} 

// somewhwere in my controller class 
public function rssAction() 
{ 
 	$ids 	= 	$this->getRssIds();  
 	$items 	= 	array();   
 	foreach ($ids as $id)   
 	{
  		try      
  		{       
   			$items[] = $this->manager->loadItem( $id);    
  		}      
  		catch (ItemNotFoundException $e)
  		{}   
  }   
  $this->view->items = $items; 
}
Let's assume that I still miss to upgrade the previewAction() method. Now, instead of bunch of warnings, I'll have Exception. This Exception is important because
1. Debugging: It will be stored to my log file, and in the development/testing process I'll quickly determine where is the problem and what I have to fix (Front controller handles it)
2. Application consistency: If I forgot to test any other part of my application which might use that manager method, and I put that code on live installation, users will encounter the customized error screen, error will be logged, and I'll receive the alarm email. The error will not be able to pass unnoticed!