Friday, November 26, 2010

Basic exception usage in php

Theory

If you have not already, check the official php documentation on exceptions at http://php.net/manual/en/language.exceptions.php. Besides some php specifics, here you can find basic example how to use try catch blocks and how to work with exceptions.

Example goes like this:
function inverse($x) {
 if (!$x) {
  throw new Exception('Division by zero.');
 }
 else return 1/$x;
}

try {
 echo inverse(5) . "\n";
 echo inverse(0) . "\n";
} catch (Exception $e) {
 echo 'Caught exception: ',  $e->getMessage(), "\n";
}

// Continue execution
echo 'Hello World';

Through this example you can see that if a process finds itself in a problematic situation, it can throw an exception. This exception can be caught, and that way we will prevent system to run into some php fatal error.

This are the theoretical basics, however, the real question is how and where to use exception handling in your  applications and scripts.



Front Controller
Front Controller is engine which runs your web application. If you are using a Zend Framework, then you are  probably using their MVC too. This MVC has in itself a front controller logic already built. On the other hand, if you have some of your engine that runs all requests through the index.php, then you probably have one class which carries the logic for including and executing different pages, or classes - that is front controller.
Theoretically, front controller is the only place where you should try to catch exceptions and probably log some info about it. In practice, you will catch them and even elsewhere, but I'll describe that issue some other time. This is quite enough for the beginning.
class Application
{
 public function init()
 {
  $page    =    $_GET['page'];
  try
  {
   require_once $page.'.php';
  }
  catch (Exception $e)
  {
   error_log( $e->getTraceAsString(), 3, 'error.log');             
   require_once 'error_page.php';              
  }
 }  
}

This is an example of a very simple application which will, depending on the passed GET parameter try to include (and execute) appropriate php script file.
It is important to note that this example shows that inside those included php scriptis, you can safely call any method that can throw the exception. You can even throw exceptions by yourself if you miss something for finishing requested process. When you have such a "rounded" application, exception will be logged and the user will be presented to an error page.
This is actually a minimum, bottom line, that your engine must comply.



Other classes
A simpler case is when you are developing "regular" classes, such as a daos for getting stored data. While building such classes, you do not have to think about what will hapen latter in other classes and processes if for example databes connection is broken, or user tries to hack the url parameters, or anything else goes wrong. You have to concentrate only on your "local" world, inside your calling method, and make it sure that the method executed well. In other words, either method will do all what is required from it, or otherwise if something is wrong, it will throw the appropriate exception.

class UserDao
{
 public $users    =    array( 1=>'Anny', 2=>'Sandra', 3=>'Monica');

 public function getUser( $userId)
 {
  if (!isset($this->users[$userId]))
   throw new Exception('User ['.$userId.'] not found in local array');

  return $this->users[$userId];
 }
}

This is a classic example where many web developers will return null and maybe try to define an error message that goes to the enduser. This is wrong because if you return null you are risking that an error passes  unnoticed. In that case your user can get the message "Hello Mrs null", and that is the better case. If you are working with the objects, you risk the fatal error after calling a method or property on the object which is null.

If, as in this example, you throw the exception, you can be sure that it will not go unnoticed. In addition, you have the opportunity to describe what happened in exception message, and help your self in latter bug tracing  sessions.
I would like to mention that in such cases it is better to throw some custom UserNotFoundExecption, but I'll explain the use of exception subclassing in another article.

No comments:

Post a Comment