Techblog Index

Better error handling

Baseball Catcher
Error handling and reporting was always a thing that I knew I wasn’t doing it right. Leaded by PHP’s on-the-fly type casting I used to make functions just return false or null but that was not right because I was hurting the return-type integrity of them.

Let’s assume we have a function that returns users IDs. What to expect for return type? Integers! Hell yeah! But what if the requested user ID is not found? Would you return false, null? Wrong!

In strict languages like C/C++ or Java, you can’t return a boolean in an integer function and so on. PHP gives the coder this “freedom” (that I interpret as “room for mistakes and laziness”).

You can go a little tidier when using type strict languages returning 0 or -1 (thus needing a signed int return type).

Exceptions are handled in a different way (will explain further) and you won’t need anymore to chunk your funky error codes/messages into your return codes.

NOTE: All the examples are in PHP and functions are merely illustrative.

Using PHP’s trigger_error

This function can help you to standardize your errors with PHP’s native errors like notices and fatal errors. I used this for long time attached to an error handler class. This worked out most for system errors and notices like “unable to open file” and such but they mostly were treated by issuing fatal errors and forcing user to a custom-built Error 500 – Internal Server Error page (in case of web code) or exiting to system with a custom error code.

Testing with if/else

As mentioned above, I started handling errors with false-ing and null’ing the return of functions and checking them for that. Example:

function find_id_by_name($name) {
 
	$query = "SELECT id FROM users WHERE name = '{$name}'";
 
	$result = mysql_query($query);
 
	if (mysql_num_rows($result) == 0)
		return false;
	else
		return mysql_result($result, 0);
 
} // end :: function :: find_id_by_name

And then checked when calling:

if ($id = find_id_by_name("John Doe")):
	# do stuff
else:
	# print some message on screen
endif;

NOTE: Returning zero (0) would trigger a false condition too.

Although this works pretty well it has several problems:

Obtrusion

If the checking code block prints messages on screen directly with print or echo it will be obtrusive for other functions calling it.

Multiple fail points

If a function can fail many ways, if you take the false/null approach you will know only that the function has failed but will have to insert debugging checkpoints on the function to know where it failed exactly.

Returning error codes may aid this point but cannot be used in integer functions because it would mix up with valid return entries and would still hurt our return type integrity.

Return Codes

As mentioned above, one could return integer error codes in order to identify the type of error that occurred. Although is a great leap in front of the null/false initiative.

define('ERROR_NO_USER',1);
define('ERROR_INVALID_PASSWORD',2);
 
function auth_user($username, $password) {
 
	$user = find_user($username);
 
	if (!$user)
		return ERROR_NO_USER;
 
	if ($user->password !== hash_function($password))
		return ERROR_INVALID_PASSWORD
 
	return $user;
 
} // end :: function :: auth_user

Note that this function returns an Object while it returns integers on failure.

To check if user has successfully authed, we would use something like:

if (is_numeric($user = auth_user("jdoe", "passw0rd")))
	# hmmm... error occurred!
else:
	$_SESSION['user'] = $user;
endif;

But this still doesn’t seems right, and you still have to cluch a switch statement inside the error treating area to take actions depending on error code.

Returning error codes in functions with integer return type

In functions with integer return type you can’t return positive error codes because it would be confused with valid non-error entries unless you used negatively signed integers for that.

function find_id_by_name($name) {
 
	# ... some code to get user data ...

	# User entry exists?
	if (!$user)
		return -1;
 
	# User account is inactive?
	if ($user->isInactive())
		return -2;
 
	return $user->id;
 
} // end :: function :: find_id_by_name

Signaling failures in a return-safe manner

You can always use a identifier of the same return type to illustrate failures. This technique can only be used to signalize entire function failure and does not gives you the fine grained error control that try gives.

Integer return types

Procedural languages like C has done this forever. In integer returned functions you can return -1 to signalize function failure.

function find_id_by_name($name) {
 
	# ... some code

	if ($ids)
		return $id;
	else
		return -1;
 
} // end :: function :: find_id_by_name

Returning empty lists

When dealing with function that returns lists (lists/arrays, dictionaries and such) you can also use an empty list in return signaling that no entries are found. This persists the function return-type and will flow naturally if parent function uses the returned value into a foreach or for statement.

function find_users_by_country($country_id) {
 
	# ... some code

	if ($users)
		return $users;
	else
		return array();
 
} // end :: function :: find_users_by_country

Using Try/Catch and Exceptions

One of the greatest practices I learned in my life as a developer was to introduce the power of try/catch into my code. This long-known practice by Java users was introduced in PHP version 5 and I think it’s present in almost every OOPw implementation like Java, PHP5, C++, Python and even in Flash AS3 (since its inherited from Java structure).

It gives you the ability to raise real-time errors during execution without the need to interfere within your returns. It helps you keeping your functions returning only data its expecting.

Try/Catch overview

The try block surrounds a set of statements that must “not throw an exception” to proceed. If any of the inner statements throws any exception, function execution proceeds but its routed to the catch block corresponding to the kind of exception that was raised.

But hell, what are exceptions?

As corrected by SeanJA from SquirrelHacker Exceptions are the Object-oriented form special errors that aren’t expected in the flow.

Exception are good to distinguish errors from each other from their class type. Exceptions can also have custom properties and methods for each type. You just have to define them in your exception class. They also extends a main Exception class with methods generally available to all classes such as the getMessage method in PHP.

In most implementation, exception classes often receives a custom message as first parameter that would be read later on by the corresponding getMessage method.

Example:

try {
	do_something();
	do_other_thing();
 
} catch (Exception $e) {
 
	print $e->getMessage();
 
} // end :: try

Throwing exceptions (errors)

Inside your function you can use the throw statement to generate exceptions that will be caught in the catch block of the caller function. Lets rewrite our function used in the early example with exceptions.

function find_id_by_name($name) {
 
	# ... some code to get user data ...

	# Connection is up?
	if (!$this->conn)
		throw new Exception("Connection was dropped.");
 
	return $user->id;
 
} // end :: function :: find_id_by_name

Custom-built exceptions

Working with generic exceptions is already very useful but it stills give you few control about the type of error generated. Generic exceptions are good if you will only catch and forward the error messages or if your error handling is independent of the error type.

You can easily generate custom exceptions by extending the main Exception class (varies according to the language). In PHP, this is the Exception class.

class FileLockedException extends Exception {
} // end :: class :: FileLockedException

That’s it, if you don’t need no additional methods or proprieties, you’re done! You may successful use your new exceptions while treating errors with catch. If you need it, just treat your Exception class as any normal OOP class.

class FileLockedException extends Exception {
 
	public $timestamp;
 
} // end :: class :: FileLockedException
 
some_function() {
 
	$ex = new FileLockedException();
	$ex->timestamp = date("Y-m-d H:i:s");
 
	throw $ex;
 
} // end :: function :: some_function

Catching exceptions

As seen on the example on try/catch overview, catching just requires a catch statement for the corresponding Exception class. All exceptions that doesn’t have an catch statement for its own class type will fall back to the main exception class.

try {
 
	do_something();
	do_other_thing();
 
} catch (FileLockedException $e) {
	print "Cannot open file for writing, locked!";
 
} catch (Exception $e) {
 
	print $e->getMessage();
 
} // end :: try

Rethrow, bubbling up exceptions

Exceptions can be bubbled up by rethrowing with a throw statement under a catch block. This is used to delegate the control of parsing the error to a parent function and to force triggering the catch blocks on them.

function do_something() {
 
	try {
 
		do_stuff();
 
	} catch (Exception $e) {
 
		throw $e;
 
	} // end :: try
 
} // end :: function :: do_something
 
function do_stuff() {
 
	if ($somethingWentWrong)
		throw new Exception("Something went wrong!");
 
} // end :: function :: do_stuff

Overhead & Overkill or When to use exceptions?

SeanJA wrote an excellent article about that clarifing some points (thanks SeanJA!).

To differ errors from exceptions, you need to point out if what you are returning is expected or not. You can achieve this by focusing on the purpose of your function.

To keep up with previous examples, lets assume our login function. Which is its purpose? The function is asked “This user credentials are valid?” and it expects “Yes” or “No”.

Credentials are a tuple of username and password so if one of these are wrong, your function should naturally return false because one of them are wrong, but that is expected.

Unexpected data is something that is beyond your control, like a locked file, a database timeout and such. Check out SeanJA if you need more clarification on this subject.

Once I got in love with Exceptions I can’t live without it. It gets you even more in depth within OOP, and I’m satisfied with it.

Treat your errors well and they will not betray you late in the nite!

 
 

Reader's thoughts on "Better error handling"

3
  1. The only problem is that you are now wrapping everything you do in try{} catch(SomeExceptionType $e){} so you can catch the errors… You should throw exceptions only in exceptional cases. For example: they gave you ‘asdf’ instead of an integer when they called find_user_by_id(). See: http://stackoverflow.com/questions/77127/when-to-throw-an-exception

  2. [...] I saw this post on Better Error Handling, while there is nothing horribly horribly wrong with it I couldn’t help thinking that he has [...]

Leave a Reply