Exception handling

Although solving all the bugs and potential errors in your code sounds like a nice idea, it's not really possible. 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 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:

<?php
    
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 way the catch block works might seem a little confusing first, but I'll show you an example in a moment where we catch multiple exceptions and rely on PHP to jump to the appropriate one. First, though, I want to explain why the code is catch(Exception $exception).

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. Yes, that sounds complicated, and yes, it sounds more complicated than it actually is.

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.

Here's an example that demonstrates how PHP handles multiple catch blocks:

<?php
    
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!

Of course, that makes sense: we throw an ExceptionFoo, so PHP jumps to the ExceptionFoo catch block. Now try this code:

<?php
    
class ExceptFoo extends Exception { }
    class
ExceptBar extends ExceptFoo { }

    try {
        
$foo = "bar";
        throw new
ExceptBar("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";
    }
?>

This is where the confusion sets in: it outputs the very same thing this time. Why? Because, as I said, 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:

<?php
    
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 ordered descending by their inheritance, so the script works as we would expect.

Here's a slightly more complicated example that should help clarify how other Exception the can be used:

<?php
    
class OutOfBoundsException extends Exception {
        
// nothing special here
    
}

    class
NotFooException extends Exception {
        
// nothing here either
    
}

    try {
        
$num = 310;

        if (
$num < 20) {
            throw new
OutOfBoundsException("Out of bounds");
        }
        
$foo = "bar";

        if (
$foo != "foo") {
            throw new
NotFooException("This variable is not set to 'foo'!");
        }
    } catch(
OutOfBoundsException $exception) {
        
var_dump($exception);
        print
"Out of bounds exception: $exception->file:$exception->line - $exception->message\n";
    } catch (
NotFooException $exception) {
        print
"Not foo exception: $exception->file:$exception->line - $exception->message\n";
    } catch (
Exception $exception) {
        print
"Some other exception: $exception->file:$exception->line - $exception->message\n";
    }
?>

There should be no surprises in there: there are two exception classes again (except not inherited this time), plus multiple catch blocks.

Note that if you want to you can throw an exception inside a catch block - either 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.

 

Next chapter: Backtracing your code >>

Previous chapter: Handling MySQL errors

Jump to:

 

Home: Table of Contents

Follow us on Identi.ca or Twitter

Username:   Password:
Create Account | About TuxRadar