Login

Error Handling In PHP (part 2)

The first part of this article demonstrated basic error
handling in PHP, explaining the various error types and illustrating the
process of building a custom error handler. But that’s just the tip of
the iceberg – this concluding part goes a step further, showing you to
trigger your own errors, and log error messages to a file, database or
email address.In the first part of this article, I introduced you to PHP’s error-handling
functions, demonstrating how they could be used to control the manner in which
errors are handled. I showed you how to use the error reporting functions to
filter out those error types you didn’t want to see, and how to use a custom
error handler to catch PHP’s notices and warnings. Finally, I wrapped things up
with a demonstration of how a custom error handler could be used to track errors
on a Web site, and write those errors to a log file for later
review.

There’s a lot more you can do with PHP’s error-handling API,
though; what you’ve seen so far is just the tip of the iceberg. In this
concluding section, I will demonstrate how you can piggyback your code on top of
PHP’s core engine by triggering your own errors and writing custom handlers to
deal with them. I will also show you a simpler method of logging errors, and
will (as usual) put the theory to the test with a couple of real-world examples.
Much mayhem ahead…so don’t go anywhere!{mospagebreak title=Raising Hell} Thus
far, we’ve been dealing with errors which are automatically generated by PHP
when it encounters a problem. However, in addition to these built-in errors, PHP
also allows you to raise errors of your own.

This is accomplished via a
little function named trigger_error(), which allows you to raise any of the
three errors in the E_USER family. These errors can then be caught and handled
either by PHP’s built-in error handler, or by a custom handler.

— validating empty string —
Warning: Empty password in /usr/local/apache/htdocs/x2.php on line 10
Warning: Password too short in /usr/local/apache/htdocs/x2.php on line
16
— validating 12345 —
Warning: Password cannot contain only numbers in
/usr/local/apache/htdocs/x2.php on line 22
— validating Gh3 —
Warning: Password too short in /usr/local/apache/htdocs/x2.php on line
16

In this case, every time the argument to validatePassword()
fails one of the tests within the function, an E_USER_WARNING error will be
raised; this error will be caught by PHP’s built-in handler and handled in the
same way as “regular” warnings – it will be displayed to the user, but script
processing will not be halted.

It’s also possible to raise fatal errors
in this fashion. Consider the next example, which updates the validatePassword()
function to raise a fatal error only if the password string is empty.

In this case, when the second password is evaluated, a fatal
error will be raised, PHP’s built-in handler will catch it, note that it is a
fatal error and terminate script execution immediately. Here’s what it looks
like:

Note that it is the responsibility of the custom
handler to die() in the event of user-generated fatal errors – PHP will not do
this automatically.{mospagebreak title=Rolling Back} PHP also comes with a
restore_error_handler() function, which allows you to restore the previous error
handler. This is useful if you need to switch between handlers in a single
script. Consider the following simple example, which demonstrates:

Warning: Something bad happened in /usr/local/apache/htdocs/x2.php on
line 12 This is the custom error handler speaking
Warning: Something bad happened in /usr/local/apache/htdocs/x2.php on
line 26

{mospagebreak title=Turning Up The Heat} In addition to
catching and displaying errors, PHP’s error-handling API also allows you to log
errors, either to a default error log, to any other file or as an email
message.

The error_log() function needs a minimum of two arguments: the
error message to be logged, and an integer indicating where the message should
be sent. There are three possible integer values in PHP 4.x:

0 – send the
message to the system log file (note that you must have logging enabled and a
log file specified in your PHP configuration for this to work);

1 – send
the message to the specified email address;

3 – send the message to the
specified file;

Here’s a trivial example which demonstrates how this
works:

<?php
// set a variable
$temp = 101.6;
// test it and log an error
// this will only work if
// you have “log_errors” and “error_log” set in your php.ini file if
($temp > 98.6) {
error_log(“Body temperature above normal.”, 0);
}
?>

Now, if you look at the system log file after running the
script, you’ll see something like this:

{mospagebreak title=Of Form And Function} Finally, how about
a real-life example to put all this in context? The following script sets up a
simple HTML form, and then uses the error-handling functions just explained to
catch and resolve form input errors once the form is submitted. Take a look:

This probably seems very complicated, so let me break it down
for you. The first part of the script merely checks for the presence of the
$submit variable and displays an HTML form if it is not present. Here’s what the
form looks like:

Now, once this form is
submitted, calls to error_reporting() and set_error_handler() are used to define
the error reporting level and the custom handler for the script.

you’ll see that it does two very simple things. First, it
uses the numeric code passed to it (and any additional information that may be
available, like the form field name) to construct a human-readable error message
(by mapping the error code to the global $errorCodes array). Next, it uses this
error message to raise an error of type E_USER_WARNING via trigger_error().

This error will ultimately be routed to the custom error handler e(),
which handles it by adding to an array named $warningList.

Obviously, if displayWarnings() returns true, it implies that
the form data is corrupt and so should not be used as is. And so, a fatal error
is raised, via my raiseError() function.

<?php
if (displayWarnings()) { raiseError(49); }
?>

This raiseError() function is identical to the
raiseWarnings() function, except that it raises an error of type E_USER_ERROR
rather than of type E_USER_WARNING. This error also gets routed to the custom
handler e(), which – since this is a fatal error – kills the script with an
appropriate message.

Assuming no errors occurred while validating the form data,
the next step is to do something with it – in this case, insert it into a
database. Since any difficulty in connecting to the database and executing the
query should be considered fatal, I’ve used the raiseError() function again to
catch and handle errors that may occur during this process.

{mospagebreak title=Buffer Zone}
You might remember, from the first part of this article, an example which
demonstrated how a custom error handler could be used to write error messages to
a file on a PHP-driven Web site. You might also remember that one of the
drawbacks of that script was the fact that warnings would get printed while the
page was being constructed.

It’s possible to bypass this problem – and
also simplify the error logging mechanism used in that example – via a series of
judicious calls to PHP’s output-buffering functions. Take a look at this revised
script, which sets things up just right:

In this case, the first thing I’ve done is initialized the
output buffer via a call to ob_start() – this ensures that all script output is
placed in a buffer, rather than being displayed to the user. This output may be
dumped to the standard output device at any time via a call to
ob_end_flush().

Now, whenever an error occurs, my custom error handler,
cleverly named e(), will first flush the output buffer, then send a custom error
template to the browser and terminate script execution. So, even if there was a
Web page being constructed on the fly when the error occurred, it will never see
the light of day, as it will be discarded in favour of the custom error
template. If, on the other hand, the script executes without any errors, the
final call to ob_end_flush will output the fully-generated HTML page to the
browser.

Note that, as before, fatal errors cannot be handled by the
custom handler. The only way to avoid the output of fatal error messages is by
telling PHP not to display them (take a look at the “display_errors”
configuration directive in the PHP configuration file).{mospagebreak title=Back To Class} If you’re not a big fan of rolling your own code, you might find it
instructive and useful to download the free, open-source ErrorHandler class from
http://www.phpclasses.org/browse.html/package/345.
Created by Gyozo Papp, this PHP class is a robust, full-featured error handler
that can easily be integrated into your application code.

The
ErrorHandler class comes with some very interesting features: the ability to
dump errors to a separate console window so that your primary interface is not
disrupted, to include in that error report the source code which caused the
error, and to append customized error messages or variable context to the error
report. Obviously, it also supports error logging (to a file, an email message,
the system logger, or all three) and can catch and replace PHP’s error messages
with a user-defined error template.

Here’s a small example of how it
works – take a look at the manual included with the class for more examples and
information.

{mospagebreak title=Endgame} And that’s about it from me. In
this two-part article, you learned how to use PHP’s error-handling API to exert
fine-grained control over the way the language handles errors. You learned how
to control the display of specific error types, how to customize the manner in
which they’re handled, and how to raise errors of your own. Next, you learnt how
to log errors, write them to a file and email them out to all and sundry. And,
as if all that wasn’t enough, the final section of this article demonstrated the
process of creating a robust, scalable error-handling system for a Web
application.

In case you’d like to know more about the material discussed
in this article, consider checking out the following links:

I
hope you enjoyed this article, and that you found it interesting and
informative. Till next time…be good!

Note: All examples in this article
have been tested on Linux/i586 with Apache 1.3.20 and PHP 4.1.1. Examples are
illustrative only, and are not meant for a production environment. Melonfire
provides no warranties or support for the source code described in this article.
YMMV!