7 Error and Exception Handling

IN THIS CHAPTER, WE EXPLAIN THE CONCEPT OF exception handling and the way it is implemented in PHP. Exceptions provide a unified mechanism for handling errors in an extensible, maintainable, and object-oriented way.

Key topics covered in this chapter include

Image  Exception handling concepts

Image  Exception control structures: try…throw…catch

Image  The Exception class

Image  User-defined exceptions

Image  Exceptions in Bob’s Auto Parts

Image  Exceptions and PHP’s other error handling mechanisms

Exception Handling Concepts

The basic idea of exception handling is that code is executed inside what is called a try block. That’s a section of code that looks like this:

try
{
  // code goes here
}

If something goes wrong inside the try block, you can do what is called throwing an exception. Some languages, such as Java, throw exceptions automatically for you in certain cases. In PHP, exceptions must be thrown manually. You throw an exception as follows:

throw new Exception('message', code);

The keyword throw triggers the exception handling mechanism. It is a language construct rather than a function, but you need to pass it a value. It expects to receive an object. In the simplest case, you can instantiate the built-in Exception class, as done in this example.

The constructor for this class takes two parameters: a message and a code. They are intended to represent an error message and an error code number. Both of these parameters are optional.

Finally, underneath your try block, you need at least one catch block. A catch block looks like this:

catch (typehint exception)
{
  // handle exception
}

You can have more than one catch block associated with a single try block. Using more than one would make sense if each catch block is waiting to catch a different type of exception. For example, if you want to catch exceptions of the Exception class, your catch block might look like this:

catch (Exception $e)
{
  // handle exception
}

The object passed into (and caught by) the catch block is the one passed to (and thrown by) the throw statement that raised the exception. The exception can be of any type, but it is good form to use either instances of the Exception class or instances of your own user-defined exceptions that inherit from the Exception class. (You see how to define your own exceptions later in the chapter.)

When an exception is raised, PHP looks for a matching catch block. If you have more than one catch block, the objects passed in to each should be of different types so that PHP can work out which catch block to fall through to.

One other point to note is that you can raise further exceptions within a catch block.

To make this discussion a bit clearer, let’s look at an example. A simple exception handling example is shown in Listing 7.1.

Listing 7.1  basic_exception.php—Throwing and Catching an Exception


<?php
try {
  throw new Exception("A terrible error has occurred", 42);
}
catch (Exception $e) {
  echo "Exception ". $e->getCode(). ": ". $e->getMessage()."<br />".
  " in ". $e->getFile(). " on line ". $e->getLine(). "<br />";
}
?>


In Listing 7.1, you can see that we used a number of methods of the Exception class, which we discuss shortly. The result of running this code is shown inFigure 7.1.

Figure 7.1  This catch block reports the exception error message and notes where it occurred.

Image

In the sample code, you can see that we raise an exception of class Exception. This built-in class has methods you can use in the catch block to report a useful error message.

The Exception Class

PHP has a built-in class called Exception. The constructor takes two parameters, as we discussed previously: an error message and an error code.

In addition to the constructor, this class comes with the following built-in methods:

Image  getCode()—Returns the code as passed to the constructor

Image  getMessage()—Returns the message as passed to the constructor

Image  getFile()—Returns the full path to the code file where the exception was raised

Image  getLine()—Returns the line number in the code file where the exception was raised

Image  getTrace()—Returns an array containing a backtrace where the exception was raised

Image  getTraceAsString()—Returns the same information as getTrace, formatted as a string

Image  __toString()—Allows you to simply echo an Exception object, giving all the information from the above methods

You can see that we used the first four of these methods in Listing 7.1. You could obtain the same information (plus the backtrace) by executing

echo $e;

A backtrace shows which functions were executing at the time the exception was raised.

User-Defined Exceptions

Instead of instantiating and passing an instance of the base Exception class, you can pass any other object you like. In most cases, you will extend the Exception class to create your own exception classes.

You can pass any other object with your throw clause. You may occasionally want to do this if you are having problems with one particular object and want to pass it through for debugging purposes.

Most of the time, however, you will extend the base Exception class. The PHP manual provides code that shows the skeleton of the Exception class. This code, taken from http://us.php.net/manual/en/language.oop5.php, is reproduced in Listing 7.2. Note that this is not the actual code but represents what you can expect to inherit.

Listing 7.2  Exception class—This Is What You Can Expect to Inherit


<?php
class Exception {
   function __construct(string $message=NULL, int $code=0) {
       if (func_num_args()) {
           $this->message = $message;
       }
       $this->code = $code;
       $this->file = __FILE__; // of throw clause
       $this->line = __LINE__; // of throw clause
       $this->trace = debug_backtrace();
       $this->string = StringFormat($this);
   }
   protected $message = "Unknown exception";  // exception message
   protected $code = 0; // user defined exception code
   protected $file;   //  source filename of exception
   protected $line;   //  source line of exception
   private $trace;  // backtrace of exception
   private $string; // internal only!!
   final function getMessage(){
       return $this->message;
   }
   final function getCode() {
       return $this->code;
   }
   final function getFile() {
       return $this->file;
   }
   final function getTrace() {
       return $this->trace;
   }
   final function getTraceAsString() {
       return self::TraceFormat($this);
   }
   function _toString() {
       return $this->string;
   }
   static private function StringFormat(Exception $exception) {
       // … a function not available in PHP scripts
       // that returns all relevant information as a string
   }
   static private function TraceFormat(Exception $exception) {
       // … a function not available in PHP scripts
       // that returns the backtrace as a string
   }
}
? >


The main reason we are looking at this class definition is to note that most of the public methods are final: That means you cannot override them. You can create your own subclass Exceptions, but you cannot change the behavior of the basic methods. Note that you can override the __toString() function, so you can change the way the exception is displayed. You can also add your own methods.

An example of a user-defined Exception class is shown in Listing 7.3.

Listing 7.3  user_defined_exception.php—An Example of a User-Defined Exception Class


<?php
class myException extends Exception
{
  function __toString()
  {
       return "<table border="1">
       <tr>
       <td><strong>Exception ".$this->getCode()."
       </strong>: ".$this->getMessage()."<br />"."
       in ".$this->getFile()." on line ".$this->getLine()."
       </td>
       </tr>
       </table><br />";
  }
}
try
{
  throw new myException("A terrible error has occurred", 42);
}
catch (myException $m)
{
   echo $m;
}

?>


In this code, you declare a new exception class, called myException, that extends the basic Exception class. The difference between this class and the Exception class is that you override the __toString() method to provide a “pretty” way of printing the exception. The output from executing this code is shown in Figure 7.2.

Figure 7.2  The myException class provides exceptions with “pretty printing.”

Image

This example is fairly simple. In the next section, we look at ways to create different exceptions to deal with different categories of error.

Exceptions in Bob’s Auto Parts

Chapter 2, “Storing and Retrieving Data,” described how you could store Bob’s order data in a flat file. You know that file I/O (in fact, any kind of I/O) is one area in programs where errors often occur. This makes it a good place to apply exception handling.

Looking back at the original code, you can see that three things are likely to go wrong with writing to the file: the file cannot be opened, a lock cannot be obtained, or the file cannot be written to. We created an exception class for each of these possibilities. The code for these exceptions is shown in Listing 7.4.

Listing 7.4  file_exceptions.php—File I/O-Related Exceptions


<?php
class fileOpenException extends Exception
{
  function __toString()
  {
       return "fileOpenException ". $this->getCode()
              . ": ". $this->getMessage()."<br />"." in "
              . $this->getFile(). " on line ". $this->getLine()
              . "<br />";
   }
}
class fileWriteException extends Exception
{
  function __toString()
  {
       return "fileWriteException ". $this->getCode()
              . ": ". $this->getMessage()."<br />"." in "
              . $this->getFile(). " on line ". $this->getLine()
              . "<br />";
   }
}
class fileLockException extends Exception
{
  function __toString()
  {
       return "fileLockException ". $this->getCode()
              . ": ". $this->getMessage()."<br />"." in "
              . $this->getFile(). " on line ". $this->getLine()
              . "<br />";
   }
}

?>


These Exception subclasses do not do anything particularly interesting. In fact, for the purpose of this application, you could leave them as empty subclasses or use the provided Exception class. We have, however, provided a __toString() method for each of the subclasses that explains what type of exception has occurred.

We rewrote the processorder.php file from Chapter 2 to incorporate the use of exceptions. The new version is shown in Listing 7.5.

Listing 7.5  processorder.php—Bob’s Order-Processing Script with Exception Handling Included


<?php
  require_once("file_exceptions.php");
  // create short variable names
  $tireqty = $_POST['tireqty'];
  $oilqty = $_POST['oilqty'];
  $sparkqty = $_POST['sparkqty'];
  $address = $_POST['address'];
  $DOCUMENT_ROOT = $_SERVER['DOCUMENT_ROOT'];
?>
<html>
<head>
  <title>Bob’s Auto Parts - Order Results</title>
</head>
<body>
<h1>Bob’s Auto Parts</h1>
<h2>Order Results</h2>
<?php
$date = date('H:i, jS F'),
echo "<p>Order processed at ".$date."</p>";
echo '<p>Your order is as follows: </p>';
$totalqty = 0;
$totalqty = $tireqty + $oilqty + $sparkqty;
echo "Items ordered: ".$totalqty."<br />";
if( $totalqty == 0) {
  echo "You did not order anything on the previous page!<br />";
} else {
  if ( $tireqty > 0 ) {
    echo $tireqty." tires<br />";
  }
  if ( $oilqty > 0 ) {
    echo $oilqty." bottles of oil<br />";
  }
  if ( $sparkqty > 0 ) {
    echo $sparkqty." spark plugs<br />";
  }
}
$totalamount = 0.00;
define('TIREPRICE', 100);
define('OILPRICE', 10);
define('SPARKPRICE', 4);
$totalamount = $tireqty * TIREPRICE
             + $oilqty * OILPRICE
             + $sparkqty * SPARKPRICE;
$totalamount=number_format($totalamount, 2, '.', ' '),
echo "<p>Total of order is ".$totalamount."</p>";
echo "<p>Address to ship to is ".$address."</p>";
$outputstring = $date." ".$tireqty." tires ".$oilqty." oil "
                  .$sparkqty." spark plugs $".$totalamount
                  ." ". $address." ";
// open file for appending
try
{
  if (!($fp = @fopen("$DOCUMENT_ROOT/../orders/orders.txt", 'ab')))
      throw new fileOpenException();
  if (!flock($fp, LOCK_EX))
      throw new fileLockException();
  if (!fwrite($fp, $outputstring, strlen($outputstring)))
      throw new fileWriteException();
  flock($fp, LOCK_UN);
  fclose($fp);
  echo "<p>Order written.</p>";
}
catch (fileOpenException $foe)
{
   echo "<p><strong>Orders file could not be opened.
         Please contact our webmaster for help.</strong></p>";
}
catch (Exception $e)
{
   echo "<p><strong>Your order could not be processed at this time.
         Please try again later.</strong></p>";
}
?>
</body>
</html>


You can see that the file I/O section of the script is wrapped in a try block. It is generally considered good coding practice to have small try blocks and catch the relevant exceptions at the end of each. This makes your exception handling code easier to write and maintain because you can see what you are dealing with.

If you cannot open the file, you throw a fileOpenException; if you cannot lock the file, you throw a fileLockException; and if you cannot write to the file, you throw a fileWriteException.

Look at the catch blocks. To illustrate a point, we have included only two: one to handle fileOpenExceptions and one to handle Exceptions. Because the other exceptions inherit from Exception, they will be caught by the second catch block. Catch blocks are matched on the same basis as the instanceof operator. This is a good reason for extending your own exception classes from a single class.

One important warning: If you raise an exception for which you have not written a matching catch block, PHP will report a fatal error.

Exceptions and PHP’s Other Error Handling Mechanisms

In addition to the exception handling mechanism discussed in this chapter, PHP has complex error handling support, which we consider in Chapter 26, “Debugging.” Note that the process of raising and handling exceptions does not interfere or prevent this error handling mechanism from operating.

In Listing 7.5, notice how the call to fopen() is still prefaced with the @ error suppression operator. If it fails, PHP will issue a warning that may or may not be reported or logged depending on the error reporting settings in php.ini. These settings are discussed at length in Chapter 26, but you need to know that this warning will still be issued regardless of whether you raise an exception.

Further Reading

Because exception handling is new to PHP, not much has been written on the subject. However, basic information about exception handling is plentiful. Sun has a good tutorial about what exceptions are and why you might want to use them (written from a Java perspective, of course) at http://java.sun.com/docs/books/tutorial/essential/exceptions/handling.html.

Next

The next part of the book deals with MySQL. We explain how to create and populate a MySQL database and then link what you’ve learned to PHP so that you can access your database from the Web.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset