Документация
HTML CSS PHP PERL другое

How PHP Manages Errors

 
Previous
Table of Contents
Next

How PHP Manages Errors

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 Errors

When 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

  • What type of error it was (an error, warning, or notice)

  • Which facility, extension, or function generated the error

  • What the actual text of the error was

  • In what file and on what line it occurred

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 Generates

PHP 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.

Table 18-1. Error Types Generated by PHP5

Value

Error Constant/Name

Description

1

E_ERROR

These are fatal runtime errors from which PHP cannot recover. Script execution is halted.

2

E_WARNING

These are runtime problems (nonfatal errors) that PHP reports but otherwise do not cause PHP to discontinue script execution.

4

E_PARSE

These are compile-time errors generated by the language parser, indicating that the script cannot be executed.

8

E_NOTICE

These are things that PHP thinks might be worth noting but do not always indicate an error condition. The default php.ini indicates that notice-level error messages should not be reported to the end user.

16

E_CORE_ERROR

Errors such as these are generated when PHP encounters an error when initializing the language runtime. They cause the script not to execute.

32

E_CORE_WARNING

These are generated when problems (nonfatal errors) occur while initializing the PHP runtime environment. Like E_WARNING, they do not prevent the script from executing.

64

E_COMPILE_ERROR

Compiler errors are generated by PHP when something is wrong with your script that prevents PHP from executing your script.

128

E_COMPILE_WARNING

Compiler warnings are generated by PHP when there is a nonfatal problem with your script, but otherwise they do not interfere with the normal operation of your script.

256

E_USER_ERROR

These are user-generated errors that can be raised by the programmer via the TRigger_error function. Script execution is terminated.

512

E_USER_WARNING

These are user-generated warnings that can be raised by the programmer via the TRigger_error function. Script execution is normally not terminated.

1024

E_USER_NOTICE

These are user-generated notices that can be raised by the programmer via the TRigger_error function. Script execution is normally not terminated, and these messages are not displayed in default configurations of PHP.

2048

E_STRICT

This message is new to PHP5. It is used to signal coding practices or structures that do not conform to the latest recommendations that guarantee maximal interoperability and future compatibility.


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 Errors

Now 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 Errors

You 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 Script

In 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 Errors

You 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 Behavior

The 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='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 Results

Although 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.

Table 18-2. Delivery Options for the error_log function

Value

Destination

0

The message is sent to the system logging facility that PHP uses, which is controlled by the error_log option in php.ini. (See the "Configuring PHP Error Handling" section.)

1

The message is sent as an e-mail to the address specified in the third parameter. The fourth parameter specifies any additional SMTP headers to be included with the message. This function operates using the PHP mail function; therefore, PHP needs to be configured so that this function operates properly. (Consult the PHP Manual for the mail function).

3

The message is written to the end of the file specified in the third parameter. This file needs to be writable by the same operating system user account that the web server and PHP are operating under.


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 Handling

Although 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.

Table 18-3. Important php.ini Settings for Error Handling

Name

Default Value

Description

error_reporting

E_ALL & ~E_NOTICE

Like the error_reporting function, this option controls which errors are reported. This is a bitwise ORing of the errors you are reporting.

display_errors

On

This indicates whether PHP displays errors on the screen as part of output.

display_startup_errors

Off

Even if display_errors is turned on, errors generated when PHP is starting up are not shown by default. This lets you show these errors.

log_errors

Off

This controls whether errors should be sent to the host web server's log files. This is highly dependant on the web server we are operating in, and the output is often not in the same format as that used by the web server (cf. Apache HTTP Server).

error_log

NULL

This indicates where errors are sent. If left to the default value of NULL, errors are sent to the web server log; otherwise, they are written to the given file. However, if the value is set to "syslog," the operating system logging facilities are used.



Previous
Table of Contents
Next
© 2000- NIV