<<

. 59
( 132 .)



>>

338 Part IV: Not So Simple Applications

set_error_handler() to tell PHP to call your own function whenever an error
occurs in your code. That function is then responsible for doing all the work that
PHP™s normal error handler does by default. It checks the current error-reporting
level to see if it™s supposed to react to the particular error, it controls what kind of
error message the user sees, and so forth.


Not every PHP error will be sent to the error-handling function. Calls to
an undefined function, for example, will not. To quote the manual, “The
following error types cannot be handled with a user defined function:
E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_
ERROR, and E_COMPILE_WARNING.” For more information, check out
the manual page: http://www.php.net/manual/en/function.set-
error-handler.php.



function error_handler($error_level,$error,$file,$line,$context)
{
// $context is an array of all the variables defined at the
// time of the error. so we can check it to see if the
// variables $public_error, $private_error, or $debug were
// defined.

if (array_key_exists(˜public_error™, $context))
{
$public_error = $context[˜public_error™];
}
else
{
$public_error = $error;
}
if (array_key_exists(˜private_error™, $context))
{
$private_error = $context[˜private_error™];
}
else
{
$private_error = ˜™;
}

// the value for $debug that we™ll use is a combination of the
// value of the $debug variable in the scope of the line where
Chapter 10: Threaded Discussion 339

// the error occurred (if defined), the setting of the debugging
// level for the file from debug_file() (if there is one), and
// the value of the constant DEBUG (if defined).

if (array_key_exists(˜debug™, $context))
{
$debug_scope = $context[˜debug™];
}
else
{
$debug_scope = 0;
}
$debug_file = debug_file($file);
if (defined(˜DEBUG™))
{
$debug_constant = constant(˜DEBUG™);
}
else
{
$debug_constant = 0;
}

$debug = $debug_scope | $debug_file | $debug_constant;

// get the current error handling levels
$error_reporting = error_reporting();
$error_logging = error_logging();
$error_debugging = error_debugging();

// get the name of the constant that matches the error
// (if there is one)
$error_name = error_levels($error_level, “_Error
#$error_level”);
$public_name = substr($error_name,strrpos($error_name,™_™)+1);

// write the error to the server error log if it™s of a level
// that we™re interested in

if ($error_logging & $error_level)
{
$logerror = “$error_name file: $file line: $line\n”
.” error: $error\n”
;
340 Part IV: Not So Simple Applications

if ($public_error && $public_error != $error)
{
$logerror .= “ public_error: $public_error\n”;
}

if ($private_error && $private_error != $error
&& $private_error != $public_error
)
{
$logerror .= “ private_error: $private_error\n”;
}

error_log($logerror);
}

// if $debug is set to something that we™re debugging at the
// moment, add some stuff to the error message and make sure
// it gets displayed, no matter what the error_reporting level
is

if ($error_debugging & $debug)
{
$debug_error = “ <li>error: $error\n”;
if ($public_error && $public_error != $error)
{
$debug_error .= “ <li>public_error: $public_error\n”;
}
if ($private_error && $private_error != $error
&& $private_error != $public_error
)
{
$debug_error .= “ <li>private_error: $private_error\n”;
}

$debug_error .= “<li>backtrace:<ul>\n”;

$backtrace = debug_backtrace();

foreach ($backtrace as $skip)
{
$class = ˜NoClass™;
$function = ˜NoFunction™;
$file = ˜NoFile™;
$line = ˜NoLine™;
extract($skip, EXTR_IF_EXISTS);
Chapter 10: Threaded Discussion 341

$debug_error .= sprintf(“\t<li>%s::%s [%s:%s]\n”
, $class
, $function
, $file
, $line
);
}
$debug_error .= “</ul>\n”;

$public_error .= $debug_error;

// if E_ALL has been explicitly set in the debug mask
// dump *everything*...
if (($error_debugging & E_ALL) == E_ALL)
{
// OK, not everything. but you can uncomment this
// if you want.

// $context = array_merge(
// $context, get_defined_constants()
// );

$public_error .= “<li>context:”.dumpvar($context).”\n”;
}
}
elseif (!($error_reporting & $error_level))
{
// if the error is not of a level that we™re reporting,
// blank out the error message
$public_error = ˜™;
}

if (!empty($public_error))
{
print <<<EOQ
<blockquote>
<b>$public_name:</b>
$public_error
</blockquote>
EOQ;
}

if (error_debugging() & get_constant(˜fatal™))
{
exit;
342 Part IV: Not So Simple Applications

}
switch ($error_level)
{
// the non-fatal errors
case E_NOTICE:
case E_USER_NOTICE:
case E_WARNING:
case E_USER_WARNING:
return;

// everything else is fatal
default:
exit;
}
}

The first things we should go over are the arguments. Because PHP is calling the
function, it defines what the arguments are. The first one, $error_level, is the
kind of error, and matches the predefined error-level constants. You can find a list
of these online at http://www.php.net/manual/en/ref.errorfunc.php.
The second argument, $error, is the text of the error message. This might be
PHP™s own error message; if you™ve raised an error with user_error(), it is the
text passed in to that function. The next two arguments, $file and $line, tell you
the name of the file and the line number in that file where the error occurred. The
final argument, $context, is an associative array of all the variables in scope at the
time of the error. (Be careful with this ” if the error happens outside of a function,
in a global scope, then this contains not only your own global variables but all of
the PHP superglobals as well. Because some of these variables incorporate others ”
$_REQUEST containing $_POST, $_GET, and $_COOKIE, for example ” trying to
dump this array out can lead to recursion problems.)
This last argument, $context, is something that you can make good use of. It™s
where you get the local value of $debug from, for one thing. It also enables you to
see if you™ve set up a “private” error message. As an example, in the my_query()
function, you can use this to capture the MySQL error message:

$private_error = ˜ack! query failed: ˜
.™<li>errorno=™.mysql_errno()
.™<li>error=™.mysql_error()
.™<li>query=™.$query
;

Then the error handler can write it out to the error log or print it out as debug-
ging output, however you™ve set it up. Meanwhile, a generic message is set up for
the user:
Chapter 10: Threaded Discussion 343

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

You can also set up a “public error” message, which gets used instead of the
default error message. This strategy is one way to cover situations where a normal
PHP error might occur.
Next, you check to see if you have turned on debugging. You can do this just by
setting a variable named $debug to an appropriate value ” this approach is good for
debugging inside a function. Or you can set debugging at a file level with the
debug_file() function (see the next section). You can also just define a constant
named DEBUG that is visible from anywhere in the application.
You check in with your handler functions to see how you want to react to the
error. If logging is turned on, a message is written out to the error log. If debugging
is turned on, you create a detailed debugging message for display. If error reporting
is set to display this level of error, you prepare a message to be shown to the user.
In setting up the debugging message, you can make use of a very nice new fea-
ture in PHP, the debug_backtrace() function. This function returns an array of all
the functions and files that got you from the initial URL to where you are now,
including the relevant line numbers. When you get an error from a function that™s
five or six levels down, this function is a godsend.
Finally, you display a message to the screen (if there is one), and then either
return from the function back to the main program, if it was a warning or notice, or
exit, if it was a full error.

debug_file()
You saw this function in the error handler. The debug_file() function sets up a
debugging level for an entire file:

function debug_file()

{
// store the file names for which we set up debugging levels
static $debug_files = array();

// some quick & dirty argument handling. we can do this
// because we™re only interested in two possible arguments,
// $file and $level

<<

. 59
( 132 .)



>>