ÄúµÄλÖãºÑ°ÃÎÍøÊ×Ò³£¾±à³ÌÀÖÔ°£¾PHP ±à³Ì£¾PHP 5 Power programming
Team LiB
Previous Section Next Section

3.18. Exception Handling

Exception handling tends to be one of the more problematic aspects in software development. Not only is it hard for the developer to decide what to do when an error occurs (such as database failure, network failure, or a software bug), but it is hard to spot all the places in the code to insert checks for failure and to call the correct function to handle it. An even more complicated task is that after you handle the failure, how do you fix your program's flow to continue at a certain point in your program?

Today, most modern languages support some variant of the popular try/catch/throw exception-handling paradigm. try/catch is an enclosing language construct that protects its enclosing source codeand basically tells the language, "I'm handling exceptions that occur in this code." Exceptions or errors are "thrown" when they are detected and the language run time searches its call stack to see if there is a relevant TRy/catch construct that is willing to handle the exception.

There are many advantages to this method. To begin with, you don't have to place if() statements in every place where an exception might occur; thus, you end up writing a lot less code. Instead, you can enclose the entire section of code with a TRy/catch construct and handle an error if one occurs. Also, after you detecte an error using the throw statement, you can easily return to a point in the code that is responsible for handling and continuing execution of the program, because tHRow unwinds the function call-stack until it detects an appropriate try/catch block.

The syntax of try/catch is as follows:

try {
    ... // Code which might throw an exception
} catch (FirstExceptionClass $exception) {
    ... // Code which handles this exception
} catch (SecondExceptionClass $exception) {
}

The TRy {} construct encloses the code that can throw an exception, which is followed by a series of catch statements, each declaring what exception class it handles and under what variable name the exception should be accessible inside the catch block.

When an exception is thrown, the first catch() is reached and an instance of comparison with the declared class is performed. If the result is true, the catch block is entered and the exception is made available under the declared variable name. If the result is false, the next catch statement is checked. Once a catch statement is entered, the following catch statements will not be entered, even if the instanceof check would result in true. If no catch statements are relevant, the language engine checks for additional enclosing try/catch statements in the same function. When none exist, it continues searching by unwinding the call stack to the calling functions.

The throw statement

throw <object>;

can only throw an object. You can't throw any basic types such as strings or integers. A pre-defined exception class exists called Exception, from which all your exception classes must inherit. Trying to throw an object which does not inherit from class Exception will result in a final runtime error.

The following code snippet shows the interface of this built-in exception class (the square brackets in the constructor declaration are used to represent optional parameters, which are not valid PHP syntax):

class Exception {
    function __construct([$message [,$code]]);

    final public getMessage();
    final public getCode();
    final public getFile();
    final public getLine();
    final public getTrace();
    final public getTraceAsString();

    protected $message;
    protected $code;
    protected $file;
    protected $line;
}

The following is a full-blown example of exception handling:

class NullHandleException extends Exception {
    function __construct($message)
    {
        parent::__construct($message);
    }
}

function printObject($obj)
{
    if ($obj == NULL) {
         throw new NullHandleException("printObject received NULL object");
    }
    print $obj . "\n";
}

class MyName {
    function __construct($name)
    {
        $this->name = $name;
    }

    function __toString()
    {
        return $this->name;
    }

    private $name;
}

try {
    printObject(new MyName("Bill"));
    printObject(NULL);
    printObject(new MyName("Jane"));
} catch (NullHandleException $exception) {
    print $exception->getMessage();
    print " in file " . $exception->getFile();
    print " on line " . $exception->getLine() . "\n";
} catch (Exception $exception) {
    // This won't be reached
}

Running this script prints
Bill
printObject received NULL object in file C:\projects\php5\tests\test.php on line
 12

Notice that the name Jane isn't printed, only Bill. This is because the printObject(NULL) line throws an exception inside the function, and therefore, Jane is skipped. In the catch handler, inherited methods such as getFile() are used to give additional information on where the exception occurred.

Tip

You might have noticed that the constructor of NullHandleException calls its parent constructor. If NullHandleException's constructor is left out, by default, new calls the parent constructor. However, it is good practice to add a constructor and call the parent constructor explicitly so that you don't forget to do so if you suddenly decide to add a constructor of your own.


Today, most internal methods don't throw exceptions to keep backward compatibility with PHP 4. This somewhat limits its use, but it does allow your own code to use them. Some new extensions in PHP 5mainly the object-oriented onesdo throw exceptions. Make sure you check the extension's documentation to be sure.

Tip

When using exceptions, follow these basic rules (both for performance and code-manageability reasons):

  1. Remember that exceptions are exceptions. You should only use them to handle problems, which brings us to the next rule᭮

  2. Never use exceptions for flow control. This makes the code hard to follow (similar to the goto statement found in some languages) and is slow.

  3. The exception should only contain the error information and shouldn't contain parameters (or additional information) that affect flow control and logic inside the catch handler.


    Team LiB
    Previous Section Next Section