<<

. 58
( 132 .)



>>

.™<li>query=™.$query
;

// just in case we were in a transaction
@mysql_query(˜rollback™);

// send a generic error message to the user

user_error(˜There was an error executing a query. Please
contact the system administrator.™, E_USER_ERROR);
}

You use the $private_error variable, which your error-handling function
checks for, to store the query that failed and the error message. Then the error-
causing query can be written out to the server error log, or right to the screen if
you™re debugging the application. Meanwhile, the user gets a less revealing (and
less frightening) error message.

Error-handling and debugging functions
Unlikely as it seems, sometimes you make mistakes in your code. Or the database
server won™t be running because someone forgot to restart it. Errors happen. When
they do, it™s useful to have a uniform way of responding to them. PHP enables you
to set up a function that gets all the errors in your application (those that come
from PHP itself ” most of them, at least) and errors you raise in your own code
when something doesn™t seem right. In this section we cover a set of functions that
we use throughout the rest of the book to handle errors. The same functions also
prepare your code for debugging in a number of ways, without interfering with its
normal use.
PHP has its own error-handling functions. And obviously, doing work that
someone else is perfectly willing to do for you is not the simplest path you can take.
However, setting up your own error handler is worthwhile for a couple of reasons.

— One, it enables you to exercise a little more control over the user™s experi-
ence. When something breaks, if you choose, the user sees only a calm
report that there seems to be a little problem, while MySQL still writes the
scary original error message out to a log file where you can use it to fix
whatever™s wrong.
Chapter 10: Threaded Discussion 333

— Two, it can make debugging an application a lot easier, especially with
some of the functions that now exist in PHP.

For sure, what you see in the following pages is no Hello_World() function. A
lot is going on in here. And debugging errors when devising your error-handling
code is almost more fun than a person should be allowed to have. But once you
have it set up, you should find that getting on with the rest of your work is easier
because it™s there.

error_debugging() and error_logging()
The PHP function error_reporting() enables you to modify the level of errors
displayed by the default error handler. Here you use one of the oldest tricks in pro-
gramming ” stealing ” to set up the same kind of levels for the types of errors (or
other things) that you want to write out to the server™s error log, or as debugging
information:

function error_debugging($newlevel=NULL)
{
static $debug_error_level = 0;
$output = $debug_error_level;
if ($newlevel !== NULL)
{
$debug_error_level = $newlevel;
}
return $output;
}
function error_logging($newlevel=NULL)
{
static $log_error_level = E_ALL;
$output = $log_error_level;
if ($newlevel !== NULL)
{
$log_error_level = $newlevel;
}
return $output;
}

Each function works just like error_reporting() ” you can change the level
by passing in a new value, or just get the current value by calling the function with
no arguments. Also, when a new value is set, the previous value is returned.

set_handler()
Now that you have three functions that track how you want to respond to various
types of errors ” the built-in error_reporting() function, and our own error_
debugging() and error_logging() functions ” managing all of them could get
334 Part IV: Not So Simple Applications

unwieldy. So you create an interface to them that makes setting up just the condi-
tions you want more straightforward.

// create constants to represent normal error reporting,
// error logging, and debugging
define(˜H_ERROR™,1);
define(˜H_LOG™,2);
define(˜H_DEBUG™,4);
define(˜H_ALL™, (H_ERROR | H_LOG | H_DEBUG));

function set_handler($newvalue=NULL, $where=NULL, $direction=NULL)
{
// store the names of the handling functions

static $functions = array(
H_ERROR => ˜error_reporting™
, H_LOG => ˜error_logging™
, H_DEBUG => ˜error_debugging™
);

// this will hold the last error level that we turned on,
// so we can easily turn it off (see below)
static $last_args = array();

if ($direction === FALSE && $newvalue === NULL && $where ===
NULL)
{
// if we just get an argument to turn something off, but
// not what or where, use the last error level that we
// turned on
list($where,$newvalue) = array_pop($last_args);
}

if (empty($where))
{
// if we don™t get a request for a specific kind of
// handler, pick a default one

if (error_levels($newvalue))
{
// if the error level we™re dealing with
// is one of the standard PHP values, assume that
// we want to change error handling
Chapter 10: Threaded Discussion 335

$where = H_ERROR | H_LOG;
}
else
{
// if we™re dealing with some made-up error level,
// it™s probably for debugging, so use that as
// the default

$where = H_DEBUG;
}
}



if ($direction !== FALSE)
{
// if we™re turning on handling for something, store
// it for turning off later

array_push($last_args, array($where,$newvalue));
}

$output = 0;
foreach ($functions as $handler => $handler_function_name)
{
if ($where & $handler)
{
// if this type of handler is one of the ones we want
// to change, get its current value

$handler_level = call_user_func($handler_function_name);

// either set handling directly to new level,
// or turn it on or off
if ($direction === FALSE)
{
$handler_level = $handler_level ^ $newvalue;
}
elseif ($direction === TRUE)
{
$handler_level = $handler_level | $newvalue;
}
else
{
$handler_level = $newvalue;
336 Part IV: Not So Simple Applications

}

// add the new level to our result
$output = $output | $handler_level;

// call the handler function to set it to the new level
call_user_func($handler_function_name,$handler_level);
}
}

// return an OR™d sum of the changed handling values
return $output;

}

In this code you set up three constants corresponding to your three ways of
responding to an error: H_ERROR for catching errors and displaying them on the
screen, set with error_reporting(); H_LOG for writing errors to the server error-
log file, set with error_logging(); and H_DEBUG for taking extra steps to debug an
error, set with error_debugging(). You use these constants in your code much as
you would use the PHP error-level constants (E_ERROR, E_USER_NOTICE, and the
like). You also define an H_ALL constant to indicate that you want to affect all of
them, just as E_ALL means “all error levels.”
The set_handler() function gets called, normally, with three arguments: one to
specify whether you want to turn handling of a given error on (TRUE) or off
(FALSE); one to specify what error level or other kind of value, such as a debugging
constant, you are interested in; and one to specify which of the handlers you want
to affect: H_ERROR, H_DEBUG, H_LOG, H_ALL for all of them, or a bitmask combining
any two of them, such as (H_ERROR|H_LOG) to affect error reporting and logging.
If you are turning handling on for a given error level, you store the error handler
specifications in a static array that functions like a stack. That enables you to call
set_handler() without any argument beyond OFF; in that case, you just turn off
the last thing that you turned on.
The other interesting thing in this function is the use of the PHP
call_user_func() function. For all three handler functions, you make exactly the
same kind of calls ” once with no arguments at all, to get the current state, and once
passing in a new state. Since the steps are the same, you don™t need to hard-code
them in three separate times. Note that in call_user_func(), the arguments after
the function name are passed to the called function just as they are. So this code

call_user_func(˜my_function™,3,™hello™,$some_variable);

is effectively the same as this code:

my_function(3,™hello™,$some_variable);
Chapter 10: Threaded Discussion 337

push_handler() and pop_handler()
These functions use the stack features of set_handler() to make changing error-
handling levels even simpler.

function push_handler($newvalue=0, $where=NULL)
{
return set_handler($newvalue,$where,TRUE);
}
function pop_handler($newvalue=NULL, $where=NULL)
{
return set_handler($newvalue,$where,FALSE);
}

The push_handler() and pop_handler() functions don™t do much; they™re just
saving you the trouble of writing the first argument to set_handler(). But in your
application code they™re much simpler to follow. If you want to turn on logging of
user notices ” the PHP E_USER_NOTICE error level ” you can write this:

push_error_handler(E_USER_NOTICE, H_LOG);

And to turn it off again:

pop_error_handler(E_USER_NOTICE, H_LOG);

Or just this:

pop_error_handler();

It™s not quite English, but it™s relatively clear.
You™re making the same kind of decision here that you made when deciding how
to pass arguments to your functions. It would be perfectly valid to skip the
set_handler(), push_handler(), and pop_handler() functions and just use the
handler functions directly:

error_reporting(error_reporting() | E_USER_NOTICE);
error_logging(error_logging() | E_USER_NOTICE);

The function calls get a little more obscure, though not terribly so. It™s up to you
to find the balance. Do you put a pretty complicated chunk of code in one place, or
slightly complicated code in lots of places? There™s no universal answer to that
question.

error_handler()
Speaking of pretty complicated chunks of code in one place, now you are ready to
look at your actual error-handling function. You use the PHP function

<<

. 58
( 132 .)



>>