Exception Handling in PHP

Notices:  
These are trivial, non-critical errors that PHP encounters while executing a script
Example:  Accessing a variable that has not yet been defined. By default, such errors are not displayed to the user at all - although you can change this default behavior.

Warnings:
These are more serious errors
Example: attempting to include() a file which does not exist. By default, these errors are displayed to the user, but they do not result in script termination.

Fatal errors:
These are critical errors
Example: Instantiating an object of a non-existent class, or calling a non-existent function. These errors cause the immediate termination of the script, and PHP’s default behavior is to display them to the user when they take place.

Parse Error:
When we make mistake in PHP code like, missing semicolon or any unexpected symbol in code

What are Exceptions?
Exceptions are the PHP 5 way of flagging up an unexpected event. They contain an information message, and can be extended just like any other object. Exceptions are thrown rather than warnings in newer PHP extensions, although you do still see extensions returning errors.

When an exception is thrown, we either catch it where it happens, or it causes the code to return to its calling context. If it isn’t caught there, it returns again, and so on. As usual it is easier to explain with examples than words! 
Here is an example in which DateTimeZone objects are created, first with a valid timezone string of ‘Europe/Amsterdam’, then another with an invalid timezone ‘nonsense’:

$timezone = new DateTimeZone('Europe/Amsterdam');
var_dump($timezone);

$timezone2 = new DateTimeZone('nonsense');

As the documentation explains, DateTimeZone throws an exception when called with unexpected data. This indeed happens if you run the above script, and the exception appears as an error message from PHP, looking something like this:

Exception: DateTimeZone::__construct(): Unknown or bad timezone (nonsense) 
in /...../exception1.php on line 6

We don’t want our entire application to stop and spit out errors over a nonsense timezone, and working with exceptions means we can do so much more with these situations than we can with errors. We “catch” exceptions and can use the information in the exception itself and in the context of the current scope to decide how best to react. This is a great improvement on the previous situation of having a few different error levels and only being able to choose whether to display, log or ignore each level as was the situation before PHP 5.

Handling Exceptions
We handle exceptions with a try/catch block like this:

try {
    $timezone2 = new DateTimeZone('nonsense');
    echo "time!";
} catch(Exception $e) {
    echo "Oops!  Something bad happened!";
    print_r($e);
}

The “try” bit goes around the code that you would normally write, but which contains operations which can cause an exception. The “catch” bit states what kind of exception to catch, and contains the code to execute if we do catch anything.
If you ran this script with a valid timezone as the argument to the DateTimeZone constructor, you would see the “time!” output and never enter the code inside the “catch” block. However in our example, an exception will be caused and so we jump straight into this block, without echoing “time!”. This is a fairly key concept – the code in the try block actually stops and we jump into the catch block and start executing from there. Any code after the point at which the exception is throw simply isn’t run.
Inside an Exception
Exceptions can hold all sorts of information. If we inspect our Exception thrown by DateTimeZone’s constructor using print_r, we’d see something like this:

Exception Object
(
    [message:protected] => DateTimeZone::__construct(): Unknown or bad timezone (nonsense)
    [string:private] =>
    [code:protected] => 0
    [file:protected] => /home/lorna/data/personal/publish/thinkvitamin/exceptions/exceptions2.php
    [line:protected] => 4
    [trace:private] => Array
        (
            [0] => Array
                (
                    [file] => /home/lorna/data/personal/publish/thinkvitamin/exceptions/exceptions2.php
                    [line] => 4
                    [function] => __construct
                    [class] => DateTimeZone
                    [type] => ->
                    [args] => Array
                        (
                            [0] => nonsense
                        )

                )

        )

    [severity] => 2
)

Walking through the object, there are many useful elements here:

message This contains a useful and relevant information message
code By default this is zero but we can use this to include a numeric error code with our exception
file Path to the file where this exception was thrown
line Line number where the exception was thrown
trace This is the stack trace, so you can see the stack of calls which were made before this exception was thrown
This is all valuable information and we can use it to inform how we handle errors in the catch block in our code. In addition we’ll take a look at how we can set these ourselves by making our own exceptions a little later on.

Throwing Exceptions
It isn’t only PHP itself that can throw exceptions, we can use exactly the same functionality in our own code. Here is a really simple example, taken directly from the PHP manual itself, of how to throw an exception manually.

function inverse($x) {
    if (!$x) {
        throw new Exception('Division by zero.');
    }
    else return 1/$x;
}

We simply throw a new Exception object that we create as we need it. The string passed into the constructor becomes the object’s message property, and we can also pass a second argument to the constructor if we want to set the code property as well. It is usual to throw exceptions in functions or methods, and then catch them from the location they are called – this lets the calling code decide what to do in the context of what it intended and is much more flexible than simple errors or having your methods return false and set an error string somewhere, which I’m sure is how I solved this before exceptions were introduced in PHP 5.

Extending Exceptions
The Exception object by itself is great, but we can extend it so that we can return additional information and different types of exceptions – so if one of a number of different scenarios actually happens, we can respond differently. Consider this script and in particular the takeTwoNumbers function:

class BigNumberException extends Exception { }
class FavouriteNumberException extends Exception { }

function takeTwoNumbers($a, $b) {
    if($a > 15 || $b > 15 || ($a + $b) > 20) {
        throw new BigNumberException('Keep it simple with smaller numbers');
    } elseif($a != 3 && $b != 3) {
        throw new FavouriteNumberException('But three is my favourite number!');
    } else {
        return $a + $b;
    }
}

try {
    echo takeTwoNumbers(3,9) . "\n";
    echo takeTwoNumbers(4,16) . "\n";
    echo takeTwoNumbers(7,5) . "\n";
} catch(Exception $e) {
    echo $e->getMessage() . "\n";
}

The function accepts two numbers, checks the values, and if there are any problems, throws a relevant exception. If the values are OK (and yes, this is a totally contrived example, in an effort to illustrate the concept without any complicating factors like the real world getting involved!) then the function simply adds them together and returns them. Our calling script calls the function 3 times, with different number sets. The first one executes fine and the numbers get added together. However the second includes a large number and so an error is thrown, causing our code to jump to the “catch” block, and so the third function call is never made. 
Although we declared empty classes to extend Exception here and so only the class name changed, you can do all sorts of things to help record the information you need. We would set custom error messages here (remember the helpful error message returned by DateTimeZone?), set error codes, or even set new properties of our own. It depends on your application which of these is helpful but having the custom exception classes makes it very nice and tidy to do so. The differing class names let us detect what *kind* of a problem occured and what we want to do in response. When we catch an exception, we can state what kind of an exception we wanted to catch – so we can narrow ourselves down to only dealing with a known situation. We can also use multiple catch blocks, like this:

try {
    echo takeTwoNumbers(3,9) . "\n";
    echo takeTwoNumbers(4,16) . "\n";
    echo takeTwoNumbers(7,5) . "\n";
} catch(BigNumberException $e) {
    echo "Big Number: " . $e->getMessage() . "\n";
} catch(FavouriteNumberException $e) {
    echo "Favourite Number: " . $e->getMessage() . "\n";
}

In this example, we can handle the two types of exception differently. If any other type of exception were to occur, it wouldn’t be caught here, but instead would “bubble” up the stack, getting returned from the end of the second catch block to wherever this code was called from – in this very simple example we are in a simple PHP file but if we were nested in functions, the code would keep on returning until PHP caught the exception, or until it reached the top at which point it would show an error like the one we saw at the start of this post.

Uncaught Exceptions
So if an exception is thrown in a method, and not caught, it returns from the function to where that function is called from. If it isn’t caught here, it returns to where this code was called from, and so on until it reaches the top of the stack. If it gets to the top of the stack, uncaught, we’ll see the error we saw at the beginning of the post. We can also give PHP instructions about how to handle any exceptions that get this far, using set_exception_handler. Many frameworks will do this and return nicely formatted messages, often logging the detail of the error so that users do not see it but developers can do so.
Here’s an example of a catch-all exception handling function:

function my_exception_handler($exception) {
  error_log("Uncaught " . get_class($exception) . " exception: " . $exception->getMessage() . "\n");
}
set_exception_handler('my_exception_handler'); 
throw new Exception('Oooops!');

All we need to do is declare our exception handling function and then tell PHP to use it for uncaught exceptions. This simple example simply passes a string to error_log function, which you can configure to behave however you need it to for your system. In a real-world application you might want to notify administrators of the problem or perhaps provide some nice feedback to the user in addition to logging the error.

Exceptions in Your Applications
Hopefully this has given some insight into what you can do with exceptions and how they interact with the rest of your object-oriented application. Even functional code needs awareness of exceptions since some core language elements now throw them, so it is worth making sure you have the exception handler declared alongside your existing error handler even if you don’t need to be extending or throwing exceptions yourself.

Popular posts from this blog

Simple Animation Using C

Personal Diary - A Mini Project Written in Turbo C With Graphical Interface

2D Reflection