Login

Building A Generic Error Reporting Class In PHP

The traditional method of building dynamic, PHP-based Web sites – mixing HTML
elements with PHP code – can result in mangled Web pages (and much user angst)
if errors take place during script execution. But yes, you can avoid the ugliness
– plug in our handy error reporting class, which provides a simple way of trapping
script errors and generating consistent, user-friendly error screens.One of the most fundamental tasks during the development cycle for a software
application involves writing code to trap and gracefully recover from exceptions.

This might seem pretty obvious, but the reality – as anyone who’s ever spent
time developing a robust Web application will tell you – is completely different.
Most developers, especially those new to the Web paradigm, treat error handling
like an unwanted stepchild, preferring instead to concentrate their efforts on
implementing and delivering the application against aggressive deadlines. Error
handling is usually an after-thought…if it’s thought of at all.

This is a shame, because most programming languages – including PHP, which is
the subject of this article – come with a full-featured error handling API, which
provides you with a number of options when it comes to trapping and resolving
errors. Making use of such an error-handling API in a consistent manner throughout
the scripts that power your application bears rich dividends: your code is more
robust, application test time is shorter (since most errors have been thought
through and handled in your code), and users never get the opportunity to see
ugly, incomprehensible debug messages generated from the language’s internals.

Even if you develop and release a Web application without building in any error-handling
routines (either through ignorance or laziness), you can be sure that your customer’s
going to demand a fix for it in the next release of the software. And since it’s
one of the few things you’re likely to do over and over again when building Web
applications, it’s worthwhile spending a little time to make the process as painless
as possible.

That’s where this article comes in. Over the next few pages, I’ll be attempting
to build a reusable library of functions that allow you to handle script errors
in a generic manner, in an attempt to save myself (and, hopefully, you) some time
when the next project comes around. The end result of this experiment will be
a PHP class that can be easily included in your scripts, and that provides a simple
way to trap and display errors consistently. I should warn you at the outset that
it may not – in fact, probably will not – meet *all* your needs; however, the
process should be instructive, especially if you’re new to object programming
in PHP, and you’ll have very little difficulty customizing it to your own requirements.

Let’s get going!{mospagebreak title=Back To Class} Before we begin, let’s just
go over the basics quickly:

In PHP, a “class” is simply a set of program statements which perform a specific
task. A typical class definition contains both variables and functions, and serves
as the template from which to spawn specific instances of that class.

Once a class has been defined, PHP allows you to spawn as many instances of the
class as you like. These instances of a class are referred to as “objects”. Each
of these instances is a completely independent object, with its own properties
and methods, and can thus be manipulated independently of other objects.

This comes in handy in situations where you need to spawn more than one instance
of an object – for example, two simultaneous database links for two simultaneous
queries, or two shopping carts. Classes also help you to separate your code into
independent modules, and thereby simplify code maintenance and changes.

As you can see, this script dynamically builds an HTML page, using PHP to generate
some components of the data that appears on the page. The PHP business logic is
closely intertwined with the HTML interface code, and is executed sequentially,
line by line, as the PHP engine parses the document.

Now, consider what happens if an error occurs during execution of the PHP functions
near the middle of the script – say, for example, the database server does down.
Here’s what the resulting output might look like:

Ugly, huh?

Obviously, if you’re in the business of building professional Web applications,
an incomplete page like the one above opens the door to a minefield of problems
– possible further errors if the user attempts to invoke a command on the incomplete
page, support calls if the error message is particularly difficult to understand
(or absent), and so on. Therefore, it makes sense to ensure that such errors,
if they occur, are handled in a graceful manner, with a consistent error screen
displayed to the user and no potential to further compound the mistake.

Further, it is possible to classify the errors that occur within an application
into two basic categories: serious (“fatal”) errors that require the script to
terminate immediately, such as a broken database connection or a file I/O error,
and less serious (“non-fatal”) errors that do not require the script to terminate
immediately, such as missing or incorrectly-typed form data.

Keeping this in mind, it’s possible to create a simple class that handles error
reporting gracefully, producing consistent error messages so that users are protected
from the situation described a few pages back. This errorReporter class would
consist of the following three components:

Methods that allows the developer to raise, or trigger, errors whenever required,
and to add these errors to an “error stack”. This error stack is merely a PHP
structure (here, an associative array) that holds a list of all the errors (both
fatal and non-fatal) encountered.

Methods that may be called by the developer to clear the screen of previously-generated
data and re-draw it with an error message (this is necessary to avoid the display
of incomplete pages, such as the one above). Fatal errors are reported immediately;
reporting of non-fatal errors may be controlled by the developer.

Utility methods to manipulate the error stack.

As you will see, these three basic components make it possible to build a very
simple (and yet very useful) errorReporter object. {mospagebreak title=How Things Work} Now, before proceeding further, I need to decide how this class is going
to work. Here’s how I plan to use it:

As you can see, I would like to wrap each function call in my script with a call
to the errorReporter class. If a function call fails, an error will be generated
via a unique error code and added to the error stack. If the error is evaluated
as fatal, an appropriate error message will be immediately displayed to the user;
if the error is evaluated as non-fatal, script processing will continue and the
developer has the option to throw up an error screen at a later date containing
a list of non-fatal errors.

Once the basic functionality of the class is clear, it’s a good idea to spend
some time listing the important methods, together with their purpose. Here’s my
initial cut:

numErrors() – check the number of errors currently held in the error stack;

displayFatalError($code, $data) – display the fatal error screen;

displayNonFatalErrors() – display the non-fatal error screen;

flushErrors() – reset the error stack.

These are the essential methods; there may be more, which I will add as development
progresses.{mospagebreak title=The Number Game} Right. So I now know how the class
is supposed to work, plus I have a fairly good idea of what the methods encapsulated
within it will look like. This is a good time to start writing some code.

You might find it helpful to download the complete class code at this point,
so that you can refer to it over the next few pages.

Note the underscore (_) prefixed to this variable; I’ve done this in order to
provide a convenient way to visually distinguish between private and public class
variables and functions.

You’ll remember, from my explanation on the previous page, that the errorReporter
class allows developers to manually trigger two types of errors – fatal and non-fatal
– using a unique error code. Let’s define this list of error codes next:

As you can see, I’ve defined six main error categories, each with a unique identifying
code. I’ve also further classified these error categories into “fatal” and “non-fatal”
– that is, errors which should cause immediate script termination, such as a failed
database connection or a bad file operation, and errors which are not so serious,
such as missing form data (more on this later). Categories with error codes below
1000 are considered fatal, the remaining categories are considered non-fatal.

This list is not exhaustive – you should edit it as per your own needs. The categories
above have been created on the basis of my own past experience with Web application
errors.

With this broad categorization in mind, it’s possible to go down one level further,
and define more specific error messages within each category. Take a look:

This pre-defined list of error codes and messages covers the most common errors
developers experience when building Web applications. Since this list is defined
once, and indexed by a unique numeric code, it may be reused over and over, in
different scripts, to display consistent error messages. And in the event that
a particular error message is deemed confusing or difficult to understand, all
you need to do is update this master list, and the change will be immediately
reflected across all error screens thrown up by the application.{mospagebreak title=Running On Empty} Let’s now begin constructing the class methods. I’ll start
with the constructor:

Once that’s done, the constructor uses the ob_start() function to define a special
output buffer which stores all the output generated by the script during its lifetime.
When I do this, the output of the script is never seen by the user unless I explicitly
make the contents of this buffer visible via a call to PHP’s output control API.

Once a buffer has been defined, the script proceeds to execute as usual. When
you’ve decided that it’s time to display the contents of the buffer to the user,
you can simultaneously end output buffering and send the contents of the current
buffer to the browser. Alternative, you can also clear the contents of this buffer
at any time via a call to ob_clean() (as you will see on the next page, this is
the function I will be using to clear and re-draw the screen when an error occurs).{mospagebreak title=Raising An Alarm} Next, the real meat of this class – the method used to
raise errors and add them to the error stack:

The raiseError() method is invoked by the developer whenever a need arises to
capture an error in a script; it must be sent an error code identifying the error
type, together with an optional explanatory message or debug data. The error thus
raised is added to the $_errors array, for further processing as and when needed.

In the event that the error raised is a fatal error, the raiseError() class also
invokes the displayFatalError() method. The determination of whether or not an
error is fatal is handled by the _getErrorType() private method, which merely
checks the error code to see if it’s above or below 1000.

This method receives the error code and debug data as input, and has to perform
a number of important tasks.

First, it needs to clear the output buffer of all previously generated content,
so as to avoid the mangled screen I showed you near the beginning of this article.
In order to do this, it uses the ob_clean() function discussed previously.

Next, it needs to display an error template, and insert appropriate content into
it reflecting the nature of the error. Using the error code passed to it as argument,
this function looks up the $_errorsubTypes array defined at the top of the class,
and maps the error code to the corresponding human-readable error message. This
data is then inserted into the error template, which contains placeholders for
the error type, error message and debug data and takes care of providing the consistent
error handling earlier stated as one of the design goals for this class.

Finally, once the error screen has been displayed, it has to terminate further
script processing via a call to die().

The error template to be displayed here is contained within a separate file,
named “error-fatal.php”, and include()-d where needed by displayFatalError().
This template can be customized to your specific requirements – here’s my bare-bones
version:

and here’s an example of the error screen displayed if an error occurs during
script execution:

Note the manner in which the output generated by the script above is first cleared
from the output buffer by the errorReporter class, and is then completely replaced
with the template containing an appropriate error message. No more mangled screens,
no more confused users!{mospagebreak title=A Well-Formed Idea} Unlike fatal errors,
which immediately cause script termination when they are raised, non-fatal errors
merely get registered in the error stack – the developer is free to display these
non-fatal errors at any time (or even ignore them altogether).

The only difference between the internals of this method, and those of the displayFatalErrors()
method discussed on the previous page, is that this method builds and returns
an array of one or more non-fatal errors rather than a single error. The output
buffer is cleared of previously-generated data, and then replaced by a non-fatal
error template, which takes care of formatting and neatly displaying the list
of errors. Script execution is then terminated.

Pretty simple, this – this script accepts the $errorList array created by the
displayNonFatalErrors() method and iterates through it to create a bulleted list
of errors.

It should be noted that I added this type of error primarily to handle form validation
tasks – if you have a large or complex form to validate, it’s inconvenient to
terminate script processing every time a validation routine fails (as would happen
if I only used fatal errors). Non-fatal errors make it possible to perform a series
of validation routines on form data, store the errors that occur in an array,
and then display all the errors at one time for the user to view and correct.

Here’s an example of how you could use the errorReporter’s non-fatal errors while
performing form validation:

{mospagebreak title=Going To The Source} Of course, this is just my first stab
at a generic error reporting class. It’s designed for very simple requirements,
and may be way too primitive for your needs. If this is the case, you have two
basic options:

File the results of my efforts in the trash can and write your own, much-cooler,
does-everything-but-make-toast class;

Pick up a free, open-source PHP class which offers a more powerful feature set.

If you picked door one, you don’t really need my help any more. You can stop
reading right now and get to work. Have fun, and remember to send me a Christmas
card if you sell your software for a million bucks.

If, on the other hand, you’re lazy and figured that door two was more promising,
you’ll be happy to hear that the Web has a huge number of powerful error handling
classes floating around it, many of them extremely powerful. Here are two that
looked particularly interesting:

And that’s about all for the moment. In this article, you expanded your knowledge
of PHP’s OOP capabilities by actually using all that theory to build something
useful – an error reporting widget which can be wrapped around your PHP scripts
to provide consistent and reusable error messages without breaking your Web pages.

If you’re a stressed-out Web developer working on a Web site or application,
you might find this object a handy tool in your next development effort. If you’re
a novice programmer struggling to understand how OOP can make your life easier,
I hope this article offered some pointers, as well as some illustration of how
object-oriented programming works. And if you don’t fit into either of those categories
– well, I hope you found it interesting and informative, anyway.

See you soon!

Note: All examples in this article have been tested on Linux/i586 with PHP 4.2.3.
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!