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.