Exception-handling with try-catch blocks

PHP 5 introduced something that programmers in other languages had come to rely on for exception handling. It’s called the try-catch block. In most languages that use the try-catch block, any time a run-time error occurs, it can be trapped inside a catch statement and dealt with in some manner appropriate for that particular situation. This is not the case in PHP 5.

Actual errors that the PHP engine generates aren’t considered exceptions the way they’re defined here, which is why it’s important to understand the difference between errors and exceptions. But if you want your code to generate and deal with procedural anomalies of any sort, or if you just want to halt execution without using a return statement, then exceptions are a fantastic tool to learn. PHP does provide a means for actual error-handling, but that topic is covered in another video.

So anyway, with the try-catch blocks, here is the basic syntax:

try {
//run some code
} catch(Exception $e) {
//do something
}

Basically, the code inside the try block runs, and if it causes an exception to be thrown, then execution of that block stops immediately, and the engine jumps into the catch block to begin execution. Exceptions are only thrown by actual PHP code. So your scripts and libraries may throw an exception, but if there’s an error within the PHP engine itself, the catch block won’t be triggered.

Take a look at the following example:

if($db->connect() === false) {
throw new Exception(‘could not connect to database’);
}

If this particular code is executed from within a try block, and the connect() method returns false, then the corresponding catch block will be called. It’s important to note that uncaught exceptions result in fatal errors, so don’t throw them unless you’re sure there’s a catch clause to deal with it. Alternately, if you want a last-resort catch clause, you can set a global exception handler. However, since the syntax for that is so similar to setting a custom error handler, I talk about that in the same video where I discuss setting custom error handlers.

The throw statement can act similar to a return in that it halts execution of a function (or at least, it will if it isn’t thrown within a try block), but instead of just returning to the point where that function was actually called, it continues up the stack until it finds a corresponding catch block.

Now, what I mean by corresponding catch block is one that matches the type of exception being thrown. The example above uses the basic class “Exception”, but that’s not the only option. PHP 5 provides several more specific Exception classes you can use, and it’s a simple matter to create your own. The catch block makes it clear what type of exception it can receive. Since all exceptions are based on the “Exception” class, a catch block like this:

catch(Exception $e)

will catch any type of exception thrown in its corresponding try block.

But supposing I wanted to handle different types of exceptions differently. A single try block can lead to multiple catch blocks:

try {
throw new UnexpectedValueException(‘unexpected!’);
} catch(InvalidArgumentException $i) {
echo ‘InvalidArgumentException: ‘ . $i->getMessage();
} catch(UnexpectedValueException $u) {
echo ‘UnexpectedValueException: ‘ . $u->getMessage();
} catch(Exception $e) {
echo ‘Exception: ‘ . $e->getMessage();
}

The code shown here will display “UnexpectedValueException: unexpected!” because the second catch block is the one that corresponds to the type of error thrown. However, since all exceptions are of the type “Exception” changing the code to the form shown below will change the output:

try {
throw new UnexpectedValueException(‘unexpected!’);
} catch(InvalidArgumentException $i) {
echo ‘InvalidArgumentException: ‘ . $i->getMessage();
} catch(Exception $e) {
echo ‘Exception: ‘ . $e->getMessage();
} catch(UnexpectedValueException $u) {
echo ‘UnexpectedValueException: ‘ . $u->getMessage();
}

This code will display “Exception: unexpected!”. That’s because PHP will go through each catch block in order until it finds one that matches, and then it will stop. Even though “catch(UnexpectedValueException $u)” is the BEST match, “catch(Exception $e)” is the FIRST match.

Alternately, the example below will simply cause a fatal error unless it’s called within the scope of a matching try-catch block higher up in the stack:

try {
throw new UnexpectedValueException(‘unexpected!’);
} catch(InvalidArgumentException $i) {
echo ‘InvalidArgumentException: ‘ . $i->getMessage();
}

Since the type of exception thrown doesn’t match any catch block.

In the above examples I used, InvalidArgumentException and UnexpectedValueException are actually built into PHP 5, along with several others. But inventing your own exception class is as simple as:

class MyNewException extends Exception { //no class code needed!
}

Include that class definition somewhere in your project space, and you can throw and catch MyNewException wherever you want. The name just helps differentiate exception types. This technique is useful for detecting when something unexpected has happened and dealing with it.

When catching an exception, the argument after the type of exception is a reference to the actual exception itself. In the case of:

try {
//do something
catch(Exception $e) {
//do something
}

“$e” is the specific object of the Exception class that was thrown and caught.

The exception class itself provides several methods you should know about. You can see more examples by searching php.net.

$e->getMessage();

Exceptions can be thrown with no arguments, but for debugging purposes, it’s usually helpful to include at least the first argument, a string, telling why the exception was thrown. For instance, if the script does this:
try {
throw new Exception(‘Go directly to the catch block’);
} catch(Exception $e) {
echo $e->getMessage();
}
the script will print “Go directly to the catch block”. Obviously, this isn’t a particularly helpful message, but you get the idea.

The second argument for an exception, if provided, is a code. Again, you can choose whatever you want, and calling “getCode()” will just return whatever you provided. Some programmers prefer to use numbers to classify their exceptions, since they can be a little less ambiguous than strings. And obviously, there’s no reason you can’t use both messages and codes in your exception. Here’s the syntax:

$e->getCode();

And an example:

define(‘EXAMPLE_EXCEPTION’, 55);
try {
throw new Exception(‘an example exception occurred’, EXAMPLE_EXCEPTION);
} catch(Exception $e) {
echo $e->getCode();
}

And this script would print “55”.

The final argument for creating an exception is another exception. The idea is that if an exception is caught in one catch block, and then a new one is thrown again, the newer exception can contain a reference to the old one. This isn’t something you see very often, but it’s easy enough to understand. Here’s another simple example:

try {
try {
throw new Exception(‘This is the way it is’);
} catch(Exception $e1) {
throw new Exception(‘Here is what I think’, null, $e1);
}
} catch(Exception $e2) {
$e1 = $e2->getPrevious();
echo $e2->getMessage() . ”
“;
echo $e1->getMessage();
}
And this code will print “Here is what I think” followed by “This is the way it is” on the line below.

In addition, the exception can return information about the stack at the time it was thrown. There are a couple different functions for this depending on whether you want the back trace as a string or an array, or whether you just need the specific line or file where the exception took place. Here they are very briefly:

$e->getTraceAsString();

returns the entire trace as a string

$e->getTrace();

returns an array where each line in the stack is a separate element

$e->getFile();

returns the path to the file where the exception was thrown

$e->getLine();

returns the line number where the exception was thrown

October 13 2009 11:19 pm | introduction and PHP tutorial scripts

Comments are closed.