PhpRiot
News Archive
PhpRiot Newsletter
Your Email Address:

More information

Merry Error Handling

Note: This article was originally published at Planet PHP on 19 April 6800.
Planet PHP

PHP does not enjoy the same consistency in error (and exception) handling as other languages, mostly due to historical reasons and the lack of a formal specification. But, there are things that you can do to make error handling saner and easier to maintain.

PHP has multiple categories of errors and exceptions, each with its own handling semantics within the PHP interpreter. For our purposes, we can classify these into the following categories:

  • Fatal errors, like out-of-memory and syntax errors, E_ERROR, E_CORE_ERROR, and E_PARSE.
  • User errors, including E_USER_ERROR, E_WARNING, E_PARSE, and all other non-fatal errors.
  • Exceptions (all subclasses of the Exception class).

Fatal errors are triggered from within the PHP interpreter and cannot be triggered by app code written in PHP. (It is possible, however, that some PHP extensions written in C may trigger fatal errors.)

User errors are largely deprecated in favor of exceptions, although they still get triggered by the PHP interpreter and some third-party libraries.

Exceptions behave the same way exceptions do in many other languages like Java, Python, and Ruby.

In an ideal world, all these categories would be condensed into one category, so you could apply the same rules and syntax to all of them. However, as you might have guessed, we do not live in an ideal world. Nonetheless, we will try to approximate it.

Fatal errors

There is a lot of literature out there about how to handle fatal errors using shutdown functions. Basically, set_error_handler() will not work for fatal errors. Instead, you can use register_shutdown_function() and pass it a function that performs some custom actions when fatal errors occur.

The problem is that, by the time the shutdown function is called, the call stack is emptied, and you have no useful backtrace to work with, except for where in the file the error has occurred. If you try to call debug_backtrace() in your shutdown function, it will return an empty array.

One way to work around this is to log a global variable in the shutdown function that can help debug the problem. This global variable could be the session identifier, user identifier, or anything that can help correlate log lines produced by the shutdown function with other log lines produced within the normal run of the app.

Furthermore, following some examples, calling error_get_last() means if any additional errors or warnings occur before the shutdown function is called, these will overwrite the error that actually caused the code to halt. Sadly, there is no way to avoid this, except to make sure these errors do not happen in the first place. These usually happen if PHP is incorrectly configured, or if you use poorly implemented third-party modules or C extensions that throw errors on shutdown.

Unifying user errors and exceptions

Much literature also exists about handling user errors, including in the PHP manual. However, most of the error handling solutions around suffer from two drawbacks:

  • You have to maintain two separate handlers for errors and exceptions.
  • You still cannot handle PHP errors like exceptions locally in the code. Instead, you have to rely on a global error handler.

The first drawback can be worked around by having both handlers simply call a central error handling facility instead of duplicating code. The following trick to wrap PHP errors as exceptions can be used to ameliorate the second drawback:

getMessage() . "\n"; } } function test_uncaught_error() { test_fake_error(); } test_

Truncated by Planet PHP, read more at the original (another 2548 bytes)