Now, we should look at how errors are generated and what PHP tells us about the error. With this information, we can begin to look at developing a plan for robust error handling in our web applications. How PHP Displays ErrorsWhen an error condition occurs, the default behavior in PHP is to emit an error message to the output stream. Warning: mysqli::mysqli() [function.mysqli]: Access denied for user 'db_user'@'localhost' (using password: YES) in c:\WebApplications\SampleApplcication\bad.php on line 4 This message contains as much information about the error as PHP has available, including
These four pieces of information are more than enough for the web application author to track down what happened and begin fixing it. Unfortunately, they prove problematic to users for two reasons. First, the text of these is entirely unintelligible to the average end user, who (apart from "Warning" and "Access denied") would understand nothing about the error message. Second, their being emitted to the output stream means that the errors clutter up the user interface presented in our application and might cause them not to be noticed by the user (especially if the font color is temporarily set to the same as the background color or the font size is set to a very small value before the error is emitted by PHP). We will look at better ways to report the errors to the application user in the later section "Working with Errors" and in the "A Holistic Approach to Error Handling" section in Chapter 30, "Strategies for Successful Web Applications." Which Errors PHP GeneratesPHP has a number of errors, warnings, and notice types that it generates and reports. Some of these are generated only by the PHP language engine, while others can be generated by the programmer or other code libraries. These core errors are shown in Table 18-1.
In addition to these core error messages, PHP defines the constant E_ALL with the value 2047, which is used as a "mask" to encompass all the other error types/values except E_STRICT. (The value 2047 is a bitwise OR of all the values in Table 18-1 except for E-STRICT.) You can tell PHP which error types to report by using the error_reporting function. You pass a mask of bits to this function indicating which errors you want reported and use the bitwise operators introduced in the "Bitwise Operators" section in Chapter 2, "The PHP Language," to form the values passed to this function. The default level is E_ALL without E_NOTICE, which is indicated as follows: error_reporting(E_ALL & ~E_NOTICE); (The ~ operator is the bitwise inversion operator, which has the effect of preserving all the bits set in E_ALL except for the E_NOTICE bit). You could do the following to report only the fatal errors that would cause script termination: error_reporting(E_ERROR | E_CORE_ERROR | E_PARSE | E_COMPILE_ERROR | E_USER_ERROR); Finally, you could use this to turn off most PHP error handling and have your script manage it: error_reporting(0); However, this does not turn off parser or compiler errors, which PHP has to report before it can begin to execute the previous function call. Turning off error reporting completely is a terrible idea in a development environmentyou could miss important messages and warnings from PHP. Even in production environments, we argue that you should let PHP raise errors and simply change the way in which they are reported to the end user (and you). Working with ErrorsNow that we know which errors PHP generates, we can learn how to control its behavior when working with them, and even generate our own errors. Ignoring ErrorsYou can use the @ operator (introduced in the "Other Operators" section of Chapter 2) when you want to perform your own error checking rather than using PHP's default error mechanisms. This operator instructs PHP not to generate errors for the currently executing expression (for instance, you cannot use it with an if statement or while loop, although individual statements and expressions within those structures are okay) and indicates that we are responsible for all error handling. For example, if we wanted to do our own error handling to connect to a database, we could do the following: $conn = @new mysqli('host', 'user', 'passwd', 'db'); if (mysqli_connect_errno() !== 0) { echo 'Unable to connect: ' . mysqli_connect_error(); exit; } Please note that the @ operator is not considered a license to write code, such as the following: <?php $conn = @new mysqli(...); @$conn->query("SET NAMES 'utf8'"); $results = @$conn->query('SELECT * FROM users'); while (($row = @$results->fetch_assoc()) != NULL) { echo "user: {$row['username']}<br/>\n"; } @$conn->close(); ?> The preceding code is highly error prone and is likely to produce undesirable results in some circumstances. Terminating the ScriptIn situations when you decide that an error is so severe that script execution cannot continue (such as when you want to list account transactions from a database table and you are unable to connect to the database), you might wish to terminate script execution. This is done with the exit function or its alias, die. The function can be stated on a line by itself: exit; or it can be given a message to send to the output stream before terminating script execution: exit('Unable to continue database server unavailable.'); However, doing this by itself is a bit drastic and interferes with any HTML we have generated in your page. It is not considered good programming practice to generate pages without closing tags, and it results in invalid documents when we use XHTML. Instead, we should take the opportunity to give the user more information and either complete the current page or redirect the user to a page dedicated to errors. Manually Causing Your Own ErrorsYou can use the trigger_error function to signal an error condition from within your written code: trigger_error(message, [error_type]); The optional error_type parameter specifies what type of error you would like to raise (it must be one of the E_USER_ error types shown in Table 18-1). If it is not specified, E_USER_NOTICE is used. The message parameter specifies the text to be used for the error. For example, the following code: if ($hairstyle == 'frizzy') { trigger_error('I\'m having a bad hair day!', E_USER_WARNING); } would generate the following output: Warning: I'm having a bad hair day! in /home/httpd/www/HairStyles/bad.php on line 6 The ability to generate your own errors is particularly useful if you are writing a library for reuse by others. By being able to specify errors, warnings, and notices, you can not only signal error conditions when the library is in use, but also signal warnings and notices to help developers properly use your code. Overriding the Default BehaviorThe default error handling provided by PHP is done by printing a simple message with all the information it has and then either continuing script execution or terminating the script and exiting (depending on the error type). However, we will likely want to replace this with something more useful to us in your web applications that would permit us to print information that your end users would find more informative, write information to log files, and even e-mail one of your application authors to tell them the problem. This is done in PHP by using the set_error_handler function, which sets the custom error handling function to call when an error is generated: set_error_handler(callback_function, [int error_types]); The optional second parameter, which is new to PHP5, lets you specify which errors the function should be used for instead of PHP's default error handling. If it is not specified, all errors will be redirected to this function except the following: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, and most E_STRICT warnings. This is less restrictive than you might think. A majority of the errors that you encounter while writing your scripts (file, database, or network errors) are classified by PHP as warningsthey are usually a problem that indicates that something bad has happened, but not catastrophic. For example, if we wrote the following custom error handler: <?php function my_error_handler ( $in_errno, $in_errstr, $in_errfile, $in_errline, $in_errcontext ) { $errs = array( 2 => 'E_WARNING', 8 => 'E_NOTICE', 256 => 'E_USER_ERROR', 512 => 'E_USER_WARNING', 1024 => 'E_USER_NOTICE', ); $err_type = ''; foreach ($errs as $val => $errstr) { if (($in_errno & $val) != 0) { $err_type .= "$errstr "; } } echo <<<EOTABLE <table align='center' width='75%' border='1' bgcolor='red'> <tr> <td valign='center' align='center'> <img src='/books/3/445/1/html/2/kaboom.png' border='0'/> </td> <td> <b> We're sorry, but an error has occurred.</b><br/> <b>$err_type:</b>($in_errfile, line $in_errline)<br/> $in_errstr<br/> </td> </tr> </table> EOTABLE; // exit on errors, continue otherwise. if ($in_errno == E_USER_ERROR) exit; } ?> we could simply write the following code to set this error handler in our code: set_error_handler('my_error_handler'); If we execute some code that generates a PHP warning notice, such as the following: $conn = new mysqli('blah', 'blah', 'blah', 'blah'); we could then see output similar to that shown in Figure 18-1. Figure 18-1. Using a custom error handler to improve error reporting.Note that you call exit when the error level is E_USER_ERROR in the my_error_handler function. This mirrors the default PHP behavior; if we do not call this, the script continues executing. To use the error handler that PHP uses by default and cease using your custom function, you can call the restore_error_handler function. Logging Error ResultsAlthough your custom error handling function (as shown in the previous section) produces output that is more visually pleasing, we would like it to do a few additional things, such as write an entry to a log file so that the monitors running the web application can see this and take appropriate action. PHP provides a very flexible function that can send error messages to a file called error_log. This function takes from two to four parameters, as follows: error_log(message, delivery_type, destination, email_headers); The message parameter is the text we want recorded, and the second parameter indicates where the message is sent. The possible values for the latter are listed in Table 18-2.
Specifying a value of 0 for the second parameter causes PHP to write messages to a log file or use operating system facilities for error logging (syslog on Unix and the Event Log service on Windows). These are configured by setting the error_log entry in php.ini, which is discussed in the "Configuring PHP Error Handling" section. To write the text of an error message to a log file that we maintain somewhere in our web application hierarchy, we can simply write the following code: <?php // // on failure, write a log entry, and redirect the user // back to the login page with an error message. // if (!validate_user_login($username, $password)) { error_log("Failed Login Attempt for $username", 3, '../logs/auth.log'); header('Location: /login.php?err=1'); exit; } ?> Configuring PHP Error HandlingAlthough error reporting and handling is a built-in extension in PHP, there are a number of options in php.ini that allow this extension to be configured and customized. A few of the important options are shown in Table 18-3.
|