Error Handling in PHP: Introducing Exceptions in PHP 5

Welcome to the last part of the series “Error Handling in PHP.” In two parts, this series introduces the basics of error handling in PHP. It demonstrates some of the most common methods for manipulating errors in PHP 4, and explains the implementation of exceptions in PHP 5, particularly in object-oriented environments.

Introduction

If you spent some time reading the first tutorial of the series, you’ve probably grasped the basic methodologies for handling failures within PHP 4 applications. In that first part, I walked through the use of abrupt “die()” statements, then covered more efficient approaches, such as using “trigger_error()/set_error_handler()” combinations, and finally explained the usage of the popular PEAR_Error object. I tried to illustrate –- at least at a glance — how different methods can be used to handle potentially conflictive conditions, during the execution of a PHP program.

If you’ve been developing large PHP 4 applications, where very often a centralized error handling mechanism is required, then you may have realized that error manipulating classes can do decent work. However, in PHP 5, things are even better when it comes to handling errors. As you probably know, the latest version of PHP introduces the power and functionality of exceptions, a built-in mechanism for handling program failures through centralized points. This mechanism offers a nice level of flexibility, particularly for delegating error handling to client code and getting detailed information on the errors that occurred and the context within which they happened.

Specifically, this last tutorial will focus on explaining the implementation of exceptions in PHP 5, ranging in from the use of “try-catch” blocks, in addition to subtly more complex structures, such as custom exceptions and exception subclassing. Whether you’re a PHP developer taking the first steps in PHP 5, or a seasoned programmer wanting to fill some gaps in the area of error handling, hopefully this article will be an instructive experience.

Now that you know the topics that will be treated in this article, let’s dive into explaining how PHP 5 exceptions can work for you.

{mospagebreak title=The basics of exceptions: using the “throw” statement and “try-catch” blocks}

At a basic level, PHP 5 exposes the classic “throw” statement and the “try-catch” blocks for implementing exceptions. As expected, any exception can be “thrown” in sections of an application, and trapped within the corresponding “catch” block. While this combination may seem trivial, it really provides a robust error management mechanism. First of all, it allows you to delegate error handling to client code; second, it provides useful and detailed information about the program failure; and finally it supports handling multiple errors conditions, by allowing you to separate program flow from error managing routines.

In order to illustrate a basic implementation of exceptions, let’s see the first example, which uses the “FileReader” class that you saw in the first article:

class FileReader{
  private $file;
  private $fileDir=’fileDir/’;
  public function __construct($file){
    if(!file_exists(“{$this->fileDir}{$file}.php”)){
      throw new Exception(‘File ‘.$file.’ not found’);
    }
    $this->file=$file;
  }
  public function getContent(){
    if(!$content=file_get_contents(“{$this->fileDir}{$this-
>file}.php”)){
      throw new Exception(‘Unable to read file contents’);
    }
    return $content;
  }
}

In this case, the above class throws a new exception when something goes wrong. As you can see, I’ve coded the class in a defensive way, so whether an error occurs when the sample file isn’t found or when reading file contents, an exception is properly thrown. At first glance, you can appreciate the benefits of using exceptions, since the flow of class operations is kept under normal conditions, and its methods aren’t responsible for handling failures that might eventually arise. Now, let’s see how a “try-catch” block can be used, in order to trap the exceptions thrown by a “FileReader” object. The example is as follows:

try{
  $fr=new FileReader(‘sample_file’); // potential error condition
  // echo file content
  echo $fr->getContent(); // potential error condition
}
catch(Exception $e){
  echo $e->getMessage();
  exit();
}

As shown above, object instantiation, together with method calls, are wrapped up into a single “try” block, so eventual exceptions thrown by the class will be caught within the appropriate “catch” block. In this particular example, I’ve instructed the “catch” block to stop program execution after displaying the error message passed as an argument to the constructor of the exception object. The line below:

echo $e->getMessage();

uses the “getMessage()” method to show the corresponding error message. By default, the PHP 5 interpreter will trigger a fatal error if the file to be read isn’t found, but this condition can be handled in a different way, by altering only the logic implemented within the pertinent “catch” block.

As you can see, the given example doesn’t provide much information on the error itself or its context. Fortunately, the built-in PHP Exception class exposes a few useful methods, which can be used to obtain detailed data when a program failure happens. Keeping that in mind, I’ll build up another example, so you can see how other exception methods are used, in order to obtain more specific information on raised errors.

{mospagebreak title=Obtaining detailed error information: using built-in Exception class’ additional methods}

As I mentioned earlier, it’s possible to get detailed information about the error that occurred. It’s precisely for this reason that the Exception class provides developers with some other methods, which can offer additional details about the failure and its environment. Here is the sample “FileReader” class, which now uses these complementary methods:

class FileReader{
  private $file;
  private $fileDir=’fileDir/’;
  public function __construct($file){
    if(!file_exists(“{$this->fileDir}{$file}.php”)){
      throw new Exception(‘File ‘.$file.’ not found’);
    }
    $this->file=$file;
  }
  public function getContent(){
    if(!$content=file_get_contents(“{$this->fileDir}{$this-
>file}.php”)){
      throw new Exception(‘Unable to read file contents’);
    }
    return $content;
  }
}

// instantiate FileReader object and implement try-catch
statement providing more information on the error
try{
  $fr=new FileReader(‘sample_file’); // potential error condition
  // echo file content
  echo $fr->getContent(); // potential error condition
}
catch(Exception $e){
  echo ‘Error :’.$e->getMessage().’<br />’;
  echo ‘Code :’.$e->getCode().’<br />’;
  echo ‘File :’.$e->getFile().’<br />’;
  echo ‘Line :’.$e->getLine().’<br />’;
  exit();
}

In addition to the methods illustrated above, the Exception class exposes the “getTrace()” and “getTraceAsString()” methods. The first one returns an array providing information on the progress of an exception, while the second one shows the same data, but in string format.

Returning to the example above, it’s nearly the same as the previous one. However, the “catch” block uses the additional methods of the exception object, helpful for obtaining more specific details on the error that occurred. With reference to this, the set of “echo” statements displays the error message passed in when the exception was originally thrown, as well as the error code (if any), the file containing the error, and finally the line at which the failure happened.

Now, the benefits of exceptions should be pretty clear. Notice how easy it is to set up an error management mechanism that can be developed through specific code, while maintaining program flow completely free from dealing with potential failures. In addition, as you’ve seen in the above example, a greater level of detail on the error that occurred can be easily obtained by calling up some of the complementary methods.

What else can we ask for? Well, sometimes it’s highly advantageous to have an error handling system that is capable of determining what course of action to take, according to the type of error generated. While this may sound like common sense stuff, in practical terms, it isn’t so intuitive to code. Thus, let’s address this issue and set up an example, which takes different actions according to the type of exception generated.

{mospagebreak title=Working with error types: developing an improved error handling mechanism}

In order to build an improved error handler, I’ll use a slightly modified version of the sample class, which passes the number of errors generated to the constructor of the Exception class. Take a look at its definition:

class FileReader{
  private $file;
  private $fileDir=’fileDir/’;
  const FILE_DATA_ERROR=1;
  const EMAIL_DATA_ERROR=2;
  public function __construct($file){
    if(!file_exists(“{$this->fileDir}{$file}.php”)){
      throw new Exception(‘File ‘.$file.’ not found’,self::FILE_DATA_ERROR);
    }
    $this->file=$file;
  }
  public function getContent(){
    if(!$content=file_get_contents(“{$this->fileDir}{$this-
>file}.php”)){
      throw new Exception(‘Unable to read file
contents’,self::FILE_DATA_ERROR);
    }
    return $content;
  }
  public function mailContent(){
    if(!@mail(‘Recipient<user@domain.com>’,'HTML Page’,$this-
>getContent())){
      throw new Exception(‘Unable to send by email file contents’,self::EMAIL_DATA_ERROR);
    }
  }
}

As you can see in the above example, I’ve rewritten the sample class, so it can throw different exceptions when things go wrong. I’ve added a new class method for sending the contents of the sample file by email. This means that each time an exception is thrown, the class will pass an error message to the Exception class, along with the type of error generated.

This is possible because the built-in Exception class accepts two parameters: the error message and the error code. So, returning to the example, I’ve defined two constants, in order to identify when the error is caused, when manipulating file contents or when emailing file data. According to the new definition for the sample class, the improved error handler might be implemented as follows:

try{
  $fr=new FileReader(‘sample_file’); // potential error condition
  // email file content
  $fr->mailContent(); // potential error condition
  // echo file content
  echo $fr->getContent(); // potential error condition
}
catch(Exception $e){
  // if error occurred when reading file data, stop program execution
  if($e->getCode()==1){
    die($e->getMessage());
  }
  // if error occurred when emailing file data, send error message to system logger
  else if($e->getCode()==2){
    echo $e->getMessage().’<br />’;
    error_log($e->getMessage(),0);
  }
}

If you study the above snippet, then you’ll probably understand what’s going on. Within the “catch” block, the error code is checked, in order to determine the type of raised error. According to this value, different courses of action will be taken. If a failure occurred when dealing with file contents, a message error is displayed and the program is simply halted. On the other hand, if the error was triggered when emailing file contents, the script will show the corresponding error message, and attempt to store the message in the system logger (if possible).

Now, with the introduction of a few modifications to the original sample class, I’ve set up a more efficient error handler, which can react differently, based on the second error parameter passed to client code. Certainly, it’s possible to use all the methods of the Exception class shown above, and develop an error handing mechanism capable of giving detailed information on the failure and its context.

Having demonstrated how to develop a more efficient error handler, the next step consists of writing a new example, this time using custom exceptions (also known as exception subclassing). These can be extremely helpful, particularly when it’s necessary to extend the functionality of the PHP built-in Exception class. Want to find out how this is done? All right, keep on reading.

{mospagebreak title=Exception subclassing: extending the functionality of the built-in Exception class}

There are situations where the default functionality provided by the built-in Exception class is simply not enough. In these cases, the quickest way to resolve this issue is to extend the class itself, as done regularly with user-defined classes, and derive one or more child classes that handle specific errors. Considering this particular situation, I’ll show you how to implement exception subclassing, by defining a basic “CustomException” class, which can handle all the errors raised by the already familiar “FileReader” class. Have a look at the example below:

class FileReader{
  private $file;
  private $fileDir=’fileDir/’;
  const FILE_DATA_ERROR=1;
  const EMAIL_DATA_ERROR=2;
  public function __construct($file){
    if(!file_exists(“{$this->fileDir}{$file}.php”)){
      throw new CustomException(‘File ‘.$file.’ not
found’,self::FILE_DATA_ERROR);
    }
    $this->file=$file;
  }
  public function getContent(){
    if(!$content=file_get_contents(“{$this->fileDir}{$this-
>file}.php”)){
      throw new CustomException(‘Unable to read file
contents’,self::FILE_DATA_ERROR);
    }
    return $content;
  }
  public function mailContent(){
    if(!@mail(‘Recipient<user@domain.com>’,'HTML Page’,$this-
>getContent())){
      throw new CustomException(‘Unable to send by email file contents’,self::EMAIL_DATA_ERROR);
    }
  }
}

As you can see, the sample class remains nearly the same. The only differences worth noticing here point to how exceptions are thrown by the class. Note that each time a failure occurs, a “CustomException” is triggered. This exception accepts an error message together with the appropriate error number as parameters. Of course, the instantiation of “CustomException” objects imply the existence of a class. Thus, here is the pertinent definition:

class CustomException extends Exception{
  public function logError(){
    // Send notification through the server log
    if($this->getCode()==FileReader::FILE_DATA_ERROR){
      error_log($this->getMessage(),0);
    }
    // Notify administrator by email
    else if($this->getCode()==FileReader::EMAIL_DATA_ERROR){
      error_log($this->getMessage(),1,’sysadmin@domain.com’);
    }
  }
}

In simple terms, what I’ve done above is derive a child class from the original Exception class, so it’s possible to handle errors directly by the internals of the class, rather than within the “catch” block. Notice how the child class determines the course of action to be taken, based on the error number passed to the class constructor (in this case, the constructor of the Exception class hasn’t been overridden).

With reference to the above example, if a custom exception was thrown when reading file contents, the child class will store the error message in the system logger. Otherwise, if the error was raised when trying to send file data by email, then the error message is sent by email to the system administrator.

Since now all the processing of errors is handled behind the scenes by the “CustomException” class, the snippet to instantiate a “FileReader” object would be reduced to this:

try{
  $fr=new FileReader(‘sample_file’);
  // email file content
  $fr->mailContent(); // potential error condition
  // echo file content
  echo $fr->getContent(); // potential error condition
}
catch(CustomException $e){
  $e->logError();
}

Definitely, the above script is now much simpler to code and read, due to the fact that all the exceptions are handled by the “logError()” method, defined earlier. Although the customized exception class is basic in its conception, it’s a useful example aimed at showing in a friendly way how to use Inheritance for deriving child exception handling classes.

Having developed a basic child class which is useful for illustrating exception subclassing in PHP 5, the last hands-on example of this tutorial will be focused on the development of two independent child classes, which break the whole exception handling process into two separated structures. For this reason, I’ll rewrite the sample “FileReader” class, like this:

class FileReader{
  private $file;
  private $fileDir=’fileDir/’;
  public function __construct($file){
    if(!file_exists(“{$this->fileDir}{$file}.php”)){
      throw new FileException(‘File ‘.$file.’ not found’);
    }
    $this->file=$file;
  }
  public function getContent(){
    if(!$content=file_get_contents(“{$this->fileDir}{$this-
>file}.php”)){
      throw new FileException(‘Unable to read file contents’);
    }
    return $content;
  }
  public function mailContent(){
    if(!mail(‘Recipient<user@domain.com>’,'HTML Page’,$this-
>getContent())){
      throw new MailException(‘Unable to send by email file
contents’);
    }
  }
}

As shown above, the “FileReader” class now throws two different exceptions. The first one, “FileException,” is triggered when an error occurs when working with the sample file, while the second one, “MailException” is thrown when trying to send file data by email. Even when this level of detail can sometimes be unnecessary, it allows you to implement exception subclassing as deep as needed.

Returning to the previous example, below is the definition for the two corresponding child classes:

class FileException extends Exception{
  public function showError(){
    die($this->getMessage());
  }
}
// MailException extends Exception
class MailException extends Exception{
  public function logError(){
    error_log($this->getMessage(),1,’sysadmin@domain.com’);
  }
}

As one would usually do with regular classes, I’ve derived both child classes from the parent Exception class, and provided specific functionality for each of them. The first one simply stops program execution after displaying the appropriate error message by using the “showError()” method, while the second one attempts to notify the system administrator by email, by utilizing the “logError()” method. Now, turn your attention to the following snippet of code, which demonstrates how to use these custom exception classes:

try{
  $fr=new FileReader(‘sample_file’);
  // email file content
  $fr->mailContent(); // potential error condition
  // echo file content
  echo $fr->getContent(); // potential error condition
}
// catch FileException
catch(FileException $e){
  $e->showError();
}
// catch MailException
catch(MailException $e){
  $e->logError();
}

In this case, I’ve used two different “catch” blocks for trapping each custom exception and moving program flow to the corresponding block. As I said before, it’s possible to implement the required logic within each child class, in order to handle specific errors according to their nature and context.

As you can see, there is a lot of room for developing robust error handlers through exceptions, and certainly their level of complexity will be mostly dictated by the specific requirements of the web application that you’re going to develop. Once you’ve grasped the core concepts, learning to use exceptions within your PHP 5 applications can be a didactical experience.

Closing time

Finally we’re done. Over this two-part series, I’ve explored the basics of error handling in PHP. I covered rudimentary methods, mostly based on the usage of “die()” statements, and more powerful combinations, such as “trigger_error()/set_error_handler().” Also, I’ve taken a quick look at the popular PEAR_Error object, in order to implement error handling mechanisms in object-based environments. Of course, the last tutorial of the series has been focused entirely on explaining exceptions in PHP 5, and hopefully demonstrating their vast benefits to object-oriented programming.

Now that you know the basics of error handling in PHP, you can leap forward and start coding your applications defensively. Certainly, it’s something that your customers and your development team are going to thank you for in the future. See you in the next tutorial!

Google+ Comments

Google+ Comments