Home > Articles

Error Handling

This chapter is from the book

This chapter is from the book

Errors are a fact of life. Mr. Murphy has an entire collection of laws detailing the prevalence and inescapability of errors. In programming, errors come in two basic flavors:

  • External errors—These are errors in which the code takes an unanticipated path due to a part of the program not acting as anticipated. For example, a database connection failing to be established when the code requires it to be established successfully is an external error.

  • Code logic errors—These errors, commonly referred to as bugs, are errors in which the code design is fundamentally flawed due to either faulty logic ("it just doesn't work that way") or something as simple as a typo.

These two categories of errors differ significantly in several ways:

  • External errors will always occur, regardless of how "bug free" code is. They are not bugs in and of themselves because they are external to the program.

  • External errors that aren't accounted for in the code logic can be bugs. For example, blindly assuming that a database connection will always succeed is a bug because the application will almost certainly not respond correctly in that case.

  • Code logic errors are much more difficult to track down than external errors because by definition their location is not known. You can implement data consistency checks to expose them, however.

PHP has built-in support for error handling, as well as a built-in severity system that allows you to see only errors that are serious enough to concern you. PHP has three severity levels of errors:

  • E_NOTICE

  • E_WARNING

  • E_ERROR

E_NOTICE errors are minor, nonfatal errors designed to help you identify possible bugs in your code. In general, an E_NOTICE error is something that works but may not do what you intended. An example might be using a variable in a non-assignment expression before it has been assigned to, as in this case:

<?php
  $variable++;
?>

This example will increment $variable to 1 (because variables are instantiated as 0/false/empty string), but it will generate an E_NOTICE error. Instead you should use this:

<?php
  $variable = 0;
  $variable++;
?>

This check is designed to prevent errors due to typos in variable names. For example, this code block will work fine:

<?
  $variable = 0;
  $variabel++;
?>

However, $variable will not be incremented, and $variabel will be. E_NOTICE warnings help catch this sort of error; they are similar to running a Perl program with use warnings and use strict or compiling a C program with –Wall.

In PHP, E_NOTICE errors are turned off by default because they can produce rather large and repetitive logs. In my applications, I prefer to turn on E_NOTICE warnings in development to assist in code cleanup and then disable them on production machines.

E_WARNING errors are nonfatal runtime errors. They do not halt or change the control flow of the script, but they indicate that something bad happened. Many external errors generate E_WARNING errors. An example is getting an error on a call to fopen() to mysql_connect().

E_ERROR errors are unrecoverable errors that halt the execution of the running script. Examples include attempting to instantiate a non-existent class and failing a type hint in a function. (Ironically, passing the incorrect number of arguments to a function is only an E_WARNING error.)

PHP supplies the trigger_error() function, which allows a user to generate his or her own errors inside a script. There are three types of errors that can be triggered by the user, and they have identical semantics to the errors just discussed:

  • E_USER_NOTICE

  • E_USER_WARNING

  • E_USER_ERROR

You can trigger these errors as follows:

while(!feof($fp)) {
 $line = fgets($fp);
 if(!parse_line($line)) {
  trigger_error("Incomprehensible data encountered", E_USER_NOTICE);
 }
}

If no error level is specified, E_USER_NOTICE is used.

In addition to these errors, there are five other categories that are encountered somewhat less frequently:

  • E_PARSE—The script has a syntactic error and could not be parsed. This is a fatal error.

  • E_COMPILE_ERROR—A fatal error occurred in the engine while compiling the script.

  • E_COMPILE_WARNING—A nonfatal error occurred in the engine while parsing the script.

  • E_CORE_ERROR—A fatal runtime error occurred in the engine.

  • E_CORE_WARNING—A nonfatal runtime error occurred in the engine.

In addition, PHP uses the E_ALL error category for all error reporting levels.

You can control the level of errors that are percolated up to your script by using the php.ini setting error_reporting. error_reporting is a bit-field test set that uses defined constants, such as the following for all errors:

error_reporting = E_ALL

error_reporting uses the following for all errors except for E_NOTICE, which can be set by XOR'ing E_ALL and E_NOTICE:

error_reporting = E_ALL ~ E_NOTICE

Similarly, error_reporting uses the following for only fatal errors (bitwise OR of the two error types):

error_reporting = E_ERROR | E_USER_ERROR

Note that removing E_ERROR from the error_reporting level does not allow you to ignore fatal errors; it only prevents an error handler from being called for it.

Handling Errors

Now that you've seen what sort of errors PHP will generate, you need to develop a plan for dealing with them when they happen. PHP provides four choices for handling errors that fall within the error_reporting threshold:

  • Display them.

  • Log them.

  • Ignore them.

  • Act on them.

None of these options supersedes the others in importance or functionality; each has an important place in a robust error-handling system. Displaying errors is extremely beneficial in a development environment, and logging them is usually more appropriate in a production environment. Some errors can be safely ignored, and others demand reaction. The exact mix of error-handling techniques you employ depends on your personal needs.

Displaying Errors

When you opt to display errors, an error is sent to the standard output stream, which in the case of a Web page means that it is sent to the browser. You toggle this setting on and off via this php.ini setting:

display_errors = On 

display errors is very helpful for development because it enables you to get instant feedback on what went wrong with a script without having to tail a logfile or do anything but simply visit the Web page you are building.

What's good for a developer to see, however, is often bad for an end user to see. Displaying PHP errors to an end user is usually undesirable for three reasons:

  • It looks ugly.

  • It conveys a sense that the site is buggy.

  • It can disclose details of the script internals that a user might be able to use for nefarious purposes.

The third point cannot be emphasized enough. If you are looking to have security holes in your code found and exploited, there is no faster way than to run in production with display_errors on. I once saw a single incident where a bad INI file got pushed out for a couple errors on a particularly high-traffic site. As soon as it was noticed, the corrected file was copied out to the Web servers, and we all figured the damage was mainly to our pride. A year and a half later, we tracked down and caught a cracker who had been maliciously defacing other members' pages. In return for our not trying to prosecute him, he agreed to disclose all the vulnerabilities he had found. In addition to the standard bag of JavaScript exploits (it was a site that allowed for a lot of user-developed content), there were a couple particularly clever application hacks that he had developed from perusing the code that had appeared on the Web for mere hours the year before.

We were lucky in that case: The main exploits he had were on unvalidated user input and nondefaulted variables (this was in the days before register_global). All our database connection information was held in libraries and not on the pages. Many a site has been seriously violated due to a chain of security holes like these:

  • Leaving display_errors on.

  • Putting database connection details (mysql_connect()) in the pages.

  • Allowing nonlocal connections to MySQL.

These three mistakes together put your database at the mercy of anyone who sees an error page on your site. You would (hopefully) be shocked at how often this occurs.

I like to leave display_errors on during development, but I never turn it on in production.

Production Display of Errors

How to notify users of errors is often a political issue. All the large clients I have worked for have had strict rules regarding what to do when a user incurs an error. Business rules have ranged from display of a customized or themed error page to complex logic regarding display of some sort of cached version of the content they were looking for. From a business perspective, this makes complete sense: Your Web presence is your link to your customers, and any bugs in it can color their perceptions of your whole business.

Regardless of the exact content that needs to be returned to a user in case of an unexpected error, the last thing I usually want to show them is a mess of debugging information. Depending on the amount of information in your error messages, that could be a considerable disclosure of information.

One of the most common techniques is to return a 500 error code from the page and set a custom error handler to take the user to a custom error page. A 500 error code in HTTP signifies an internal server error. To return one from PHP, you can send this:

header("HTTP/1.0 500 Internal Server Error");

Then in your Apache configuration you can set this:

ErrorDocument 500 /custom-error.php

This will cause any page returning a status code of 500 to be redirected (internally—meaning transparently to the user) to /custom-error.php.

In the section "Installing a Top-Level Exception Handler," later in this chapter, you will see an alternative, exception-based method for handling this.

Logging Errors

PHP internally supports both logging to a file and logging via syslog via two settings in the php.ini file. This setting sets errors to be logged:

log_errors = On

And these two settings set logging to go to a file or to syslog, respectively:

error_log = /path/to/filename

error_log = syslog

Logging provides an auditable trace of any errors that transpire on your site. When diagnosing a problem, I often place debugging lines around the area in question.

In addition to the errors logged from system errors or via trigger_error(), you can manually generate an error log message with this:

error_log("This is a user defined error");

Alternatively, you can send an email message or manually specify the file. See the PHP manual for details. error_log logs the passed message, regardless of the error_reporting level that is set; error_log and error_reporting are two completely different entries to the error logging facilities.

If you have only a single server, you should log directly to a file. syslog logging is quite slow, and if any amount of logging is generated on every script execution (which is probably a bad idea in any case), the logging overhead can be quite noticeable.

If you are running multiple servers, though, syslog's centralized logging abilities provide a convenient way to consolidate logs in real-time from multiple machines in a single location for analysis and archival. You should avoid excessive logging if you plan on using syslog.

Ignoring Errors

PHP allows you to selectively suppress error reporting when you think it might occur with the @ syntax. Thus, if you want to open a file that may not exist and suppress any errors that arise, you can use this:

$fp = @fopen($file, $mode);

Because (as we will discuss in just a minute) PHP's error facilities do not provide any flow control capabilities, you might want to simply suppress errors that you know will occur but don't care about.

Consider a function that gets the contents of a file that might not exist:

$content = file_get_content($sometimes_valid);

If the file does not exist, you get an E_WARNING error. If you know that this is an expected possible outcome, you should suppress this warning; because it was expected, it's not really an error. You do this by using the @ operator, which suppresses warnings on individual calls:

$content = @file_get_content($sometimes_valid);

In addition, if you set the php.ini setting track_errors = On, the last error message encountered will be stored in $php_errormsg. This is true regardless of whether you have used the @ syntax for error suppression.

Acting On Errors

PHP allows for the setting of custom error handlers via the set_error_handler() function. To set a custom error handler, you define a function like this:

<?php
require "DB/Mysql.inc";
function user_error_handler($severity, $msg, $filename, $linenum) {
  $dbh = new DB_Mysql_Prod;
  $query = "INSERT INTO errorlog
         (severity, message, filename, linenum, time) 
         VALUES(?,?,?,?, NOW())";
  $sth = $dbh->prepare($query);
  switch($severity) {
  case E_USER_NOTICE:
   $sth->execute('NOTICE', $msg, $filename, $linenum);  
   break;
  case E_USER_WARNING:
   $sth->execute('WARNING', $msg, $filename, $linenum);   
   break;
  case E_USER_ERROR:
   $sth->execute('FATAL', $msg, $filename, $linenum); 
   echo "FATAL error $msg at $filename:$linenum<br>";
   break;
  default:
   echo "Unknown error at $filename:$linenum<br>";
   break;
  }
}
?>

You set a function with this:

set_error_handler("user_error_handler");

Now when an error is detected, instead of being displayed or printed to the error log, it will be inserted into a database table of errors and, if it is a fatal error, a message will be printed to the screen. Keep in mind that error handlers provide no flow control. In the case of a nonfatal error, when processing is complete, the script is resumed at the point where the error occurred; in the case of a fatal error, the script exits after the handler is done.

Mailing Oneself

It might seem like a good idea to set up a custom error handler that uses the mail() function to send an email to a developer or a systems administrator whenever an error occurs. In general, this is a very bad idea.

Errors have a way of clumping up together. It would be great if you could guarantee that the error would only be triggered at most once per hour (or any specified time period), but what happens more often is that when an unexpected error occurs due to a coding bug, many requests are affected by it. This means that your nifty mailing error_handler() function might send 20,000 mails to your account before you are able to get in and turn it off. Not a good thing.

If you need this sort of reactive functionality in your error-handling system, I recommend writing a script that parses your error logs and applies intelligent limiting to the number of mails it sends.

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020