How do you manage errors in your application?

I’ve got a larger implementation that I’m about to do. So I’m thinking about this again.

Typically I’ve written the error handling for each function or class at that level of abstraction. Naturally that means all the error handling is embedded throughout the application. If I need to maintain it, I need to locate what code is generating the error. That makes sense in some ways, but I’m wondering if a utility class that handles ALL error messaging for the application makes more sense or is more formal for a larger app. Opinions?

Do you write your error handling into each function? An error manager in each module? Centralized in one class for the entire app?

What do you think about it? Easy to build? Hard to maintain?

Just a general note. Logging is an extremely helpful tool for not only troubleshooting problems, but also for gathering statistics, identifying broken code or potentially unstable SQL queries, and as means to audit access to your application. Personally, I log all errors and warnings (php error/warning, database errors/warnings, failed logons or other ‘application’ errors and warnings), as well as significant information-only events, such as successful logins, credit card transactions, password changes, etc.

I’ve always done error handling at a global level, only overriding that in exceptional circumstances which require special error handling (a credit card transaction processor might be an example).

+1 for mentioning Exceptions and the try/catch syntax.

Another thing I would like to add is that, it’s very important that you record all possible errors within an error log on your server. This way you will know when people are experiencing trouble rather then letting them see specific vulnerabilities.

A few specific ways it can be done are stated below, please note that this is just a basic system I use and is by no means ‘bulletproof’.

  • Create a “crash” table in your database and a set of wrapper classes for reporting errors. I’d recommend setting categories for the crashes (“blocking”, “security”, “PHP error/warning” (vs exception), etc).
  • In all of your error handling code, make sure to record the error. Doing this consistently depends on how well you built the API (above step) - it should be trivial to record crashes if done right.

Also don’t forget to write fail over logic incase your DB crashes =p. Such as sending emails to admin or simply just recording the DB activities in a plain text file.

One thing I would like to do is give an example about exceptions. Exceptions are a fairly modern way to treat errors in PHP. The easiest thing to do is probably set up an error handler that throws an exception. That way all errors are converted to exceptions, leaving you to deal with one error scheme only.

The following will convert errors to exceptions for you.

function exceptions_error_handler($severity, $message, $filename, $lineno) {
  if (error_reporting() == 0) {
    return;
  }
  if (error_reporting() & $severity) {
    throw new ErrorException($message, 0, $severity, $filename, $lineno);
  }
}
set_error_handler('exceptions_error_handler');
error_reporting(E_ALL ^ E_STRICT);

“There are a few cases though, where code is specifically designed to work with errors. For example, the schemaValidate method of DomDocument raises warnings, when validating a document. If you convert errors to exceptions, it will stop validating after the first failure. Some times this is what you want, but when validating a document, you might actually want all failures. In this case, you can temporarily install an error-handler, that collects the errors.”

class errorhandler_LoggingCaller {
  protected $errors = array();
  function call($callback, $arguments = array()) {
    set_error_handler(array($this, "onError"));
    $orig_error_reporting = error_reporting(E_ALL);
    try {
      $result = call_user_func_array($callback, $arguments);
    } catch (Exception $ex) {
      restore_error_handler();
      error_reporting($orig_error_reporting);
      throw $ex;
    }
    restore_error_handler();
    error_reporting($orig_error_reporting);
    return $result;
  }
  function onError($severity, $message, $file = null, $line = null) {
    $this->errors[] = $message;
  }
  function getErrors() {
    return $this->errors;
  }
  function hasErrors() {
    return count($this->errors) > 0;
  }
}

In action:

$doc = new DomDocument();
$doc->load($xml_filename);
$validation = new errorhandler_LoggingCaller();
$validation->call(
  array($doc, 'schemaValidate'),
  array($xsd_filename));
if ($validation->hasErrors()) {
  var_dump($validation->getErrors());
}

There is no ‘one right way’ to tackle error handling. You just have to ask yourself some basic questions:

  • How do you want errors handled on a dev environment (show on screen?) versus in a production environment (logged and emailed?)
  • Not all error types can be handled with a custom error handler
  • You can also look into Trigger_Error which is similar to throw new Exception in that they both escape out of the current execution immediately, only trigger_error() is not catchable

Depends on what you mean by error handling. Errors in application logic/state should be handled via Exceptions and the try/catch syntax.

Ideally, these exceptions should bubble up to the appropriate level: the function level is usually not a great place to handle thrown exceptions in your application, simply due to the fact that you couple your function to the exception handler you’re using. Scallio’s suggestion of a global session_handler() function has its merits, though I prefer a simpler try/catch syntax (As it allows more granular exception handling). Sadly, PHP doesn’t support the throws syntax in method declarations, which can make it more difficult to force the user to handle possible errors in your classes.

If you’re asking about things like bad data or users skipping steps in application logic leading to well-handled but inconsistent states, I think that’s more a question of what your application is doing in the first place.

Please ignore any inconsistencies in this post: I’m proofreading, but apparently my brain is already convinced it’s Saturday.

As far as I’m concerned each function is responsible for its own error handling. If a function gets into a state where error handling is no longer possible/feasable, an Exception is thrown that is caught by a function set through set_exception_handler, that terminates the app and shows an error (with a backtrace when not in production mode).

PS. Seeing as you’re about to get into a large project, have you considered/are you considering using a framework?