Every well-constructed PHP application should have error handling. While there is no
definitive method for handling errors since it varies depending on application
needs and a developer's
style, nonetheless, there are some "best practices" that
should be implemented in all PHP applications.
WHAT IS AN ERROR?
The answer may seem straightforward but actually requires a bit of discussion.
An "error" is an expected or unexpected event that occurs when your
PHP code is running. An "expected" error can be a database query that
returns no result or an html form that is missing values for designated required
fields. An "unexpected" error is one that assumes a particular application state
which, for some as yet unknown reason, does not exist. The most obvious unexpected error is
a database that is not running or a missing file that one of your scripts expects to find.
ERROR HANDLING ENVIRONMENT
The recommended "best practice" has three parts:
The developer should be alerted to all errors, notices, warnings, etc. both during development and when
the PHP application goes into production.
The developer should be able to choose how to be notified of these problems.
No errors, notices, warnings, etc. should ever be displayed to the user.
Even in development, errors dumped to the browser can be bad because they can become hidden in the HTML.
In production, you should display
a generic page that says "System down for maintenance" or some other
generic message. At no time do you want to tip off a hacker as to what went wrong,
nor do you want to display the details of what went wrong. That information is for you alone.
Although some developers feel that errors should be displayed during development and even in
a production environment - modified, of course, so as not to tip off a hacker -
we will assume here that errors will never be displayed.
We can set these three parameters in one of two ways.
If you have control over the php.ini file then set the following parameters: (See Note 1)
error_reporting = E_ALL
log_errors = On
display_errors = Off
If you don't have control over php.ini, then at the beginning of your script
you can set the same parameters using the following:
When "log_errors" is set to "On", errors will go into
one of two places:
The Apache error log file
If you control your own server, you can view the Apache error
logs using the following command:
tail -f /path/to/your/log/error_log
The " -f " setting will show the errors as they are appended to the file.
If the error has already occurred, you can use the following command to view the
last 100 lines of the log.
tail -100 error_log | more
The number "100" can be changed to any number of lines you may want to view and piping the
output to the more command will show a screen's worth of data at a time.
In a shared hosting environment, to a specified file within your designated directory.
In this environment, you may need to download the error log file in order to view it.
In either case, the file that PHP will write its errors to can be changed in either the php.ini file
in the "Error Handling and Logging" section, specifically, error_log = filename or,
if you do not have control over php.ini, you can set the file in your code by
using the following:
This type of error can be the the result of something a website
visitor has done such as providing invalid form input (the visitor omitted some values or entered letters
in a field that is expecting numbers) or it can be a database query that returns no records.
For example, you may have a form that requires that particular fields be completed before submitting or
particular fields may
require a particular type of input such as only numbers and dashes in a field that captures phone numbers.
Therefore, your PHP code should ALWAYS check for acceptable values.
Illustrated below is a simple example that illustrates the concept.
Click here to see
it in action.
<?php
/**************************************************************
Check values entered by user.
The code shown here is meant solely to illustrate the concept
in the main article. There are many different coding methods
that one can use to validate form input.
***************************************************************/
//Initialize array to an empty array
$errMsg = array();
/*
Check to see if the form has been posted. If true, then
check values.
*/
if (isset($_POST['submit'])){
if (trim($_POST['Name']) === ''){
$errMsg[] = 'Please enter your name';
}
//We want to make sure that the phone number is in the format we want
if (!preg_match("/^[0-9]{3}[-]{1,1}[0-9]{4}$/", $_POST['Phone'])){
$errMsg[] = 'Phone number should be entered as digits in the following format: ###-####';
}
/*
If an error has occurred, the array "$errMsg" will have a count greater than zero
so display our error message to the user
*/
if (count($errMsg)!== 0){
foreach ($errMsg as $key => $value){
$tmp .= "<li>$value</li>";
}
echo "An error has occurred:<ul>$tmp</ul>";
} else {
echo "Congratulations! You successfully entered your Name and Phone Number!";
}
}
//Note below that we are echoing back to the user any values already entered and we are
//making those values browser-safe with the "htmlentities" function
//(See Note 2)
?>
Unexpected errors are errors that are not part of the normal operation of your PHP application such
as a database that suddenly stops running. When an unexpected error occurs, you want to:
know the state of the application
know when it happened
know that it happened
have a record of the above three items
prevent the user from knowing any of the details of what happened
(See Note 3)
Below are general guidelines for implementing our recommended best practices for notifying the developer of unexpected errors.
In the top level include file for your application, set the error handler
for your application to your own error handler by using
set_error_handler('myErrHandler');
You could simply place this statement at the beginning of all of your PHP scripts but
using an "include" file makes it easier to maintain your code.
See require_once and related functions and the PHundamentals article
Site Structure: Where to Locate Includes?.
When an error occurs, trigger the error handler, e.g.,
if(!mysql_connect("myDatabase","myUser","myPassword")){
trigger_error('Can not connect to database',E_USER_ERROR);
}
So what should your error handler look like? We've provided a sample error handler though the exact
error handler you use is solely up to you. However, the sample code below fulfills the five
basic requirements of an error handler as noted above.
The error handler lets you know the "state" of the application (handled by trigger_dump())
The error handler lets you know when it happened (handled by date())
The error handler lets you know that it happened (handled by mail())
The error handler records all three of the items above (handled by error_log())
You need to prevent the user from knowing any of the details of what happened, except that it
happened (handled by header)
<?php
function myErrHandler($code, $message, $errFile, $errLine) {
//Set message/log info
$subject = "$message: MAJOR PROBLEM at " . APP_NAME . ': ' . date("F j, Y, g:i a");
$body = "errFile: $errFile\r\n"
. "errLine: $errLine\r\n"
. trigger_dump($GLOBALS);
/*
An email will be sent to the site administrator.
Its subject line will have the date and time it occurred while
the body will contain the state of all of the global variables. This information
is obtained through the function trigger_dump.
*/
mail(ADMIN_EMAIL_ADDR,$subject,$body);
//The same subject line and body of the email will get written to the error log.
error_log("$subject\r\n $body");
/*
We don't want users to know the true nature of the problem so
we just redirect them to a generic error page that has been created.
The generic page should have a simple message, such as "System down
for maintenance." The key idea is not to let any potentially malicious
user learn about the actual problem that had occurred.
*/
header ("Location: http://{$_SERVER['HTTP_HOST']}/". GENERIC_ERR_PAGE );
exit;
}
/*
The function below is called by the mail
and error_log calls above.
*/
function trigger_dump( $mixed,$type = 0 ) {
/*
$mixed will handle whatever you may decide to pass to it.
$type will determine what this function should do with the
information obtained by var_dump
*/
switch( (int) $type ) {
case 0:
/*
Grab the contents of the output buffer
when you want to send the information
back to the calling function
*/
ob_start();
var_dump($mixed);
//If you are using PHP ver. 4.3 use the
//code below:
return ob_get_clean();
//If you are using an earlier version
//of PHP, then use the code below:
$ob_contents = ob_get_contents();
ob_end_clean();
return $ob_contents;
case 1:
/*
When you want to display the information to the browser
*/
print '<pre>';
var_dump($mixed);
print '</pre>';
break;
case 2:
//When you want your error handler to deal with the information
ob_start();
var_dump($mixed);
//If you are using PHP ver. 4.3 use the
//code below:
trigger_error(ob_get_clean());
break;
//If you are using an earlier version
//of PHP, then use the code below:
$ob_contents = ob_get_contents();
ob_end_clean();
trigger_error($ob_contents);
break;
}
}
?>
1 - For additional information on php.ini settings, see the latest version of
php.ini-recommended. See also the
PHundamentals article
PHP Initialization.
A list of configuration settings that can be changed via ini_set can be
found here.
2 - For additional information on the use of htmlentities see the PHundamentals
article
"Storing Data Submitted From a Form and Displaying Data from a Database".
While you can use JavaScript to trap form-related user errors, you should NEVER
solely rely on it. Users can intentionally turn off JavaScript and thereby bypass your error checking.
For a discussion of
other form-related issues, see the PHundamentals article
Spoofed Form Submissions.
3 - Whether to show a generic "System down for maintenance" message or not is, of course, a
judgement call on the part of the developer. For example, one could simply redirect the user to the home page
which would be a visual indicator (to the user) that something had gone awry but it does not
tip off the user as to "what" went awry. In the case of "expected" errors,
such as a search function that uses URL parameters, you might want to implement a set of
"default" search parameters.
For example, your site might have a URL like the following:
http://mydomain.com/search.php?location=USA. If a user should alter the search
parameter, e.g., location=123, your code could handle this by automatically substituting
the default search parameters. Alternately, you could display a message such as "No records were
found. Please try again."
Contributors to this note include the following:
John F. Andrews
Mark Armendariz
David Mintz
Mitch Pirtle
Chris Shiflett
Hans Zaunere
the PHundamentals Team: Jeff Siegel, Michael Southwell
If you see an error, or have a suggestion for an improvement, please let us know
and Contact Us.