Error vs Exception, Part 3

In this series, I’m going to take a look at both PHP’s error and exception models — what each is, how they work, and how and when to use the one or the other.

In this instalment, having covered the basics of PHP error handling in part 1 and the need for exceptions in part 2, I’ll continue unfolding exceptions by taking a look at how they work, and how they’ve been implemented in PHP.

PHP

PHP 5 introduced an Exception Handling model similar to that of other Object Oriented Programming languages, such as Java, C++ or Python. So if you’re already familiar with exceptions in other languages, it’ll be easy to begin using them in PHP.

What they look like

Exceptions come in the form of objects — instances of classes — so can carry as much information as you want. PHP comes with a base Exception class, and if you’d like to create your own exceptions, you’ll need to extend it (which we’ll do, later). The Exception class also comes with a number of useful methods, which we’ll look at shortly, but let me first explain:

How to work with them

Exceptions are “thrown” somewhere, and “caught” or “catched” somewhere else!

Throw

So what you do when you’d like to throw an exception, is create an instance of it, and then throw it as follows :

$exc = new Exception;
throw $exc;

though you usually do that in just one step:

throw new Exception();

Catch

And to catch an exception, you need two things – a try statement wrapped around the throw shown above, and a catch statement. The try is essentially saying “try to do the following” and the catch is then (only) triggered if it wasn’t successful (that means, when the try throws an exception). The catch then catches(obvious, isn’t it!) exceptions which were thrown.

This means that try and catch always go together :

try {
    // try doing this
}
catch (Exception $X) {
    // but do this if an exception was thrown in the try
}

The catch is completely ignored until the try actually throws an exception. Once a throw statement is reached when the program executes, control immediately passes back up the stack looking for a try-catch block which catches that type of exception.

That type?

The catch (Exception $X) in the example above means that this catch will only catch an exception of type Exception, and store it in the variable $X – yes, as I’ve mentioned, there are different types of exceptions, and you can have more than one catch block to catch the different types. But, as ALL exceptions extend PHP’s Exception base class, that catch statement in the example above would catch ANY kind of exception thrown in the try. To only catch certain types of exceptions, you would use something like:

catch (CustomException $E)

which would only catch exceptions of type CustomException; that is, CustomException exceptions, as well as any exceptions which extend the CustomException class – remember, exceptions are just classes! To create a custom exception such as this, you need do nothing more than:

class LevelTwoException extends Exception { 
    // not doing much extending!
}

// and let's create a third level in our hierarchy :
class LevelThreeException extends LevelTwoException { }

A catch statement like catch (LevelTwoException $E) would then catch any thrown LevelTwoException as well as any thrown LevelThreeException, but would not catch just a plain Exception. As I said, it is then possible to stack catch statements, but do it in such a way that you first catch more precise or specialized exceptions:

try {
    // try doing this
}
catch (LevelThreeException $X) {
    // catch exceptions of type LevelThreeException
}
catch (LevelTwoException $X) {
    // catch exceptions of type LevelTwoException.
    // This includes exceptions of type LevelThreeException
    // but as they've already been caught by the previous catch
    // a LevelThreeException will never get caught by this catch
}
// exceptions of type Exception would go uncaught at this point

If you were to exchange the two catch statements above such that the less specialized LevelTwoException catch was first, it would be a logic mistake, as all LevelThreeException exceptions would be caught by it, and the LevelThreeException catch below that would never catch anything at all, meaning you have code that never gets called.

(Note that in your code you should use more descriptive names than ‘LevelTwoException’, this is only for illustration purposes!)

If an exception isn’t caught anywhere in your code, it results in a PHP Fatal Error, so unless you like fatal errors, all exceptions have to be caught somewhere in your program, which is a fantastic thing, as I’ve explained in part 2.

Inner life

Let’s now throw an exception from within an internally-called function, and see what it looks like:

foo();
function foo () {
    try {
        bar();
    }
    catch (Exception $Name) {
        // what now?
    }
}
function bar() {
    throw new Exception("some explanatory message");
}

Exceptions, like the one here caught as $Name, contain a stack trace or backtrace of the program execution, very useful for debugging! To access that useful but protected information, the base Exception class provides a number of getter methods, which would return the following for the example above :

// 4 METHODS FOR INFORMATION ABOUT THE EXCEPTION ITSELF:
$name->getMessage();
    // some explanatory message
$name->getCode();
    // 0, as we didn't pass a code along as second parameter
$name->getFile();
    // location/of/file.php
$name->getLine();
    // 11

// TWO METHODS FOR MORE INFO ABOUT HOW WE GOT TO THIS POINT:
$name->getTrace();
/*
Array
(
    [0] => Array
        (
            [file] => location/of/file.php
            [line] => 4
            [function] => bar
            [args] => Array
                (
                )
        )
    [1] => Array
        (
            [file] => location/of/file.php
            [line] => 1
            [function] => foo
            [args] => Array
                (
                )
        )
)
*/
$name->getTraceAsString();
    // #0 location/of/file.php(4): bar()
    // #1 location/of/file.php(1): foo()
    // #2 {main}

The point of the methods are pretty self-explanatory, so I’m not going to run through them. You can find explanations for each in the PHP Manual.

There’s more

That’s it for now! In part 4, I’ll be talking about exception- & error-handling methodology – how errors and exceptions are used and handled especially in bigger applications, about the SPL, nesting and re-throwing exceptions, what to do with these criminal exceptions once they’ve been caught, error logging, ErrorExceptions and more..

Thanks for reading, your feedback is always welcome!