Handling ErrorsNow 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:
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 ErrorsWhen 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:
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:
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.
Logging ErrorsPHP 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 ErrorsPHP 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 ErrorsPHP 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); print "FATAL error $msg at $filename:$linenum<br>"; break; default: print "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. |