Приглашаем посетить
Техника (find-info.ru)

Section 22.7.  Exception Handling

Previous
Table of Contents
Next

22.7. Exception Handling

Although solving all the bugs and potential errors in your code sounds like a nice idea, it's not likely for anything beyond "Hello, world" scripts. The main reason for this is because it's hard to predict how your code will operate in all scenarios, so you can't write code to handle it all.

The solution here is to write exception handlers , which allow you to explicitly state what PHP should do if there's a problem in a block of code. Exceptions are interesting because they all come from the root class Exception, but you can extend that with your own custom exceptions to trap specific errors.

As exceptions are new in PHP 5, they are primarily for userland code (PHP code you write) as opposed to internal PHP functions. As new versions of PHP get released, more and more internal code should be switched over to use exceptions so that you have a chance to handle errors smoothly, but this is a gradual process.

The basic exception handler uses try/catch blocks to encase blocks of code in a virtual safety barrier that you can break out of by throwing exceptions. Here's a full try/catch statement to give you an idea of how it works:

    try {
            $num = 10;
            if ($num < 20) {
                    throw new Exception("D'oh!");
            }
            $foo = "bar";
    } catch(Exception $exception) {
            print "Except!\n";
    }

In that example, PHP enters the try block and starts executing code. When it hits the line "throw new Exception", it will stop executing the try block and jump to the catch block. Here it checks each exception option against the list in catch and executes the appropriate code. Once PHP has left the try block, it will not return to it, which means that the line $foo = "bar" will never be executed.

The Exception class in there is necessary because PHP decides which catch block to execute by looking for the same class type as was thrown. Well, that's the "easy" way of looking at it: what actually happens is that PHP searches each catch block, using what is essentially an instanceof check on it. This means that if the exception thrown is of the same class as the exception in the class block, or if it is a descendant of that class, PHP will execute that catch block.

The $exception variable after the Exception class is there because PHP actually hands you an instance of that Exception class, set up with information about the exception you've just experienced. As all exceptions extend from the base class Exception, you get a basic level of functionality no matter what you do. What's more, most of the functions in the Exception class are marked final, meaning they can't be overridden in inherited classes, again guaranteeing a set level of functionality. For example, you can call $exception->getMessage( ) to see why the exception was thrown (the "D'oh!" part in the throw( ) statement), you can call getFile( ) to see where the exception was called, etc.

This example demonstrates how PHP handles multiple catch blocks:

    class ExceptFoo extends Exception { }
    class ExceptBar extends ExceptFoo { }

    try {
            $foo = "bar";
            throw new ExceptFoo("Baaaaad PHP!");
            $bar = "baz";
    } catch (ExceptFoo $exception) {
            echo "Caught ExceptFoo\n";
            echo "Message: {$exception->getMessage( )}\n";
    } catch (ExceptBar $exception) {
            echo "Caught ExceptBar\n";
            echo "Message: {$exception->getMessage( )}\n";
    } catch (Exception $exception) {
            echo "Caught Exception\n";
            echo "Message: {$exception->getMessage( )}\n";
    }

That will output the following:

    Caught ExceptFoo
    Message: Baaaaad PHP!

So we throw an ExceptionFoo, and PHP jumps to the ExceptionFoo catch block. However, the output remains the same even if we change the throw( ) line to this:

    throw new ExceptBar("Baaaaad PHP!");

Why? Because PHP matches the first catch block handling the exception's class or any parent class of it. Because ExceptionBar inherits from ExceptionFoo, and the ExceptionFoo catch block comes before the ExceptionBar catch block, the ExceptionFoo catch block gets called first.

You can rewrite the code to this:

    class ExceptFoo extends Exception { }
    class ExceptBar extends ExceptFoo { }

    try {
            $foo = "bar";
            throw new ExceptBar("Baaaaad PHP!");
            $bar = "baz";
    } catch (ExceptBar $exception) {
            echo "Caught ExceptBar\n";
            echo "Message: {$exception->getMessage( )}\n";
    } catch (ExceptFoo $exception) {
            echo "Caught ExceptFoo\n";
            echo "Message: {$exception->getMessage( )}\n";
    } catch (Exception $exception) {
            echo "Caught Exception\n";
            echo "Message: {$exception->getMessage( )}\n";
    }

This time, we have the exception classes in descending order by their inheritance, so the script works as we would expect.

If you want to, you can throw an exception inside a catch blockeither a new exception or just the old exception again. This is called rethrowing the exception, and is commonly used if you have ascertained that you cannot (or do not want to) handle the exception there.

Using this form of debugging allows you to have debugging code next to the code you think has a chance of breaking, which is much easier to understand than having one global error-handling function. Whenever you have code that you know might break and want to include code to handle the problem in a smooth manner, try/catch is the easiest and cleanest way of debugging.


Previous
Table of Contents
Next