Error vs Exception, Part 2

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, I’ll begin unfolding exceptions by expanding on the concept, as well as explain why they’re needed at all.

Definitions

Now that we’ve covered the basics of PHP error handling in part 1, let’s recap those definitions before we take a closer look at exceptions :

Error
When there is a mistake in the code, which one has to fix.
Example: to try and print the contents of a variable which doesn’t exist. The programmer can fix this error by declaring (and initializing) the variable.
Exception
When there is an irregular situation during the running of the program, which should have been expected as a possibility, and which the program should handle.
Example: when the database cannot be accessed because of some error on the server. The programmer cannot fix the problem as it doesn’t lie in his code, but the program should expect this exceptional situation as a possibility, and act accordingly.

Concept

Exceptions are a way for a function to return information up the call stack about why it could not perform the work that it was asked to perform (when the reason is not due to some error in the code!)

The concept is better explained with an example, so consider the following code :

function foo() {
	bar();
}
function bar() {
	baz();
}
function baz() {
}

How can each function return information to its caller (the function which called it), if it needs to? In other words, how could baz() tell bar() and bar() tell foo() if something went wrong? Generally, people use one of two ways — return values (also called status returns) or exceptions.

Return

The primary purpose of return is to return data to its caller:

function add ($num1, $num2) {
	return $num1 + $num2;
}

If return is also used to return more information than the actual data, it’s often referred to as an overloaded return function as you’re demanding more of the return than what it’s suited for. For instance, how would the add function in the previous example let its caller know that it couldn’t add the two arguments, for some or other reason, such as that one of the arguments passed to it wasn’t actually a number? (According to our definitions at the start of this article, if one of the arguments was a non-number it wouldn’t be an error in the code, but an exceptional circumstance which should be catered for.) You could let the function return null when there’s a problem, but some functions in your application might return null as an actual value, and would then have to return some other value to communicate a problem, such as false or -1.

Also, what if a function could encounter more than one type of problem — a return value of null (for example) still wouldn’t tell the caller the reason for not being able to add the two numbers, only that it couldn’t! So it would need to return different values to indicate different types of problems, so you’d always be forced to have to remember or check which return values are supposed to communicate which problematic situation in that particular function — there’s no agreed-upon standard.

Standardised return

You could perhaps let all your functions always return something more complex to communicate, with a standard structure, such as an array which always contains a boolean value indicating whether there was a problem, and then either the data if there wasn’t a problem, or a message if there was one :

function add ($num1, $num2) {
    if (!is_int($num1) || !is_int($num2))
        return array(false, "Both numbers have to be integers");
    return array(true, $num1 + $num2);
}

and then check for a problem situation in the caller :

function run () {
    $data = add (2,3);
    if (!$data[0]) {
        // there was a problem and $data[1] contains the reason
        echo $data[1];
        return;
    }
    // no problem, so $data[1] contains the answer
    echo "The answer is {$data[1]}";
}

However, each time the add function is called, one will have to check the returned value in that way, and it’s quite possible to forget to check it.

Silent errors

In fact, every single time the function is called, it provides an opportunity for the programmer to forget to check the return value and just assume the function performed its duty as instructed (as it should and will almost all the time). When this situation arises, the application will continue executing with incorrect data, without a hint of any problem, which could lead to all sorts of bad situations which often are very hard to debug. The user won’t even know why the application crashed, why some data is incorrect, or why the application is just not doing what was expected, and it’ll take time for you to figure it out as well. So using return for passing back information results in a program with a default behaviour of silently ignoring errors, which isn’t good at all!

As a very simple (and contrived!) example, consider the following function that uses the add function we created above, and which is supposed to print out :
“Let’s count: 1, 2, 3, … ” up to ($foo+$bar) :

function run ($foo, $bar) {
    $data = add ($foo, $bar);
    echo "Let's count: ";
    for ($i=1; $i<=$data[1]; $i++) {
        echo "{$i}, ";
    }
}

Now let’s say the user was asked to insert the values of $foo and $bar in a form, but makes a typo, which results in the following call once the form was submitted:

run(2, '3e');

This would result in output being only “Let’s count: “, and the user would have no idea why the application isn’t working correctly. So you can see that using an overloaded return isn’t the best solution.

A better way is needed

We don’t want the program above to try and print some incorrect result to screen if there was a problem with the input, but instead let the user know that their numbers weren’t valid, and give them the opportunity to try again. So what we need is a way for a function to be able to communicate with the rest of the program ‘higher up’ in the call chain and send as much information along as it would like to; to demand that a problem situation they’ve encountered be handled somewhere; and which stops the rest of the program’s code from executing otherwise!

Exceptions are it!

And that is one of the main reasons why exceptions are great: by immediately returning control up the stack once an exception is encountered, and by throwing a Fatal error if an exception isn’t dealt with anywhere, your program is automatically aware of all problems, and can respond in different ways depending on the context.

Not only that, but as they’re objects (more about this in Part 3), they allow you to return a great amount of information, and leave the return statement to be used in all its glory.

Also, PHP’s Exception class, which you use to create any type of exception in PHP, inherently carries the meaning of a problem caused by an exceptional circumstance, regardless of the function in which the exception was triggered, unlike the inconsistent meanings of return values when used to pass information.

The Exception class can also be extended, which allows for the communication of a rich hierarchy of different types of exceptional circumstances!

And lastly, some sections of code (such as a constructor) cannot even use return values, so a workaround has to be implemented in those situations. But if exceptions are used to communicate problems throughout your program, no workaround is ever needed as exceptions can be thrown even in those situations.

Conclusion

It’s obvious that using exceptions is many times better than using an overloaded return value to return status information up the stack.

Now that you understand the need for exceptions, as well as the difference between an error in the code and an exceptional circumstance which can occur in the running of the program, I’ll explain how exceptions work in part 3, and we’ll then begin implementing them in PHP.

That’s it for now, thanks for reading. Your feedback is always welcome.