Index
    Preface
      What This Book Is About
      What You Need to Know
      How This Book Is Organized
      How to Use This Book
      Conventions Used in This Book
      Using Code Examples
      How to Contact Us
      Web Site and Code Examples
      Acknowledgments
      Chapter 1.  Database Applications and the Web
      Section 1.1.  The Web
      Section 1.2.  Three-Tier Architectures
      Chapter 2.  The PHP Scripting Language
      Section 2.1.  Introducing PHP
      Section 2.2.  Conditions and Branches
      Section 2.3.  Loops
      Section 2.4.  Functions
      Section 2.5.  Working with Types
      Section 2.6.  User-Defined Functions
      Section 2.7.  A Working Example
      Chapter 3.  Arrays, Strings, and Advanced Data Manipulation in PHP
      Section 3.1.  Arrays
      Section 3.2.  Strings
      Section 3.3.  Regular Expressions
      Section 3.4.  Dates and Times
      Section 3.5.  Integers and Floats
      Chapter 4.  Introduction to Object-Oriented Programming with PHP 5
      Section 4.1.  Classes and Objects
      Section 4.2.  Inheritance
      Section 4.3.  Throwing and Catching Exceptions
      Chapter 5.  SQL and MySQL
      Section 5.1.  Database Basics
      Section 5.2.  MySQL Command Interpreter
      Section 5.3.  Managing Databases and Tables
      Section 5.4.  Inserting, Updating, and Deleting Data
      Section 5.5.  Querying with SQL SELECT
      Section 5.6.  Join Queries
      Section 5.7.  Case Study: Adding a New Wine
      Chapter 6.  Querying Web Databases
      Section 6.1.  Querying a MySQL Database Using PHP
      Section 6.2.  Processing User Input
      Section 6.3.  MySQL Function Reference
      Chapter 7.  PEAR
      Section 7.1.  Overview
      Section 7.2.  Core Components
      Section 7.3.  Packages
      Chapter 8.  Writing to Web Databases
      Section 8.1.  Database Inserts, Updates, and Deletes
      Section 8.2.  Issues in Writing Data to Databases
      Chapter 9.  Validation with PHP and JavaScript
      Section 9.1.  Validation and Error Reporting Principles
      Section 9.2.  Server-Side Validation with PHP
      Section 9.3.  JavaScript and Client-Side Validation
      Chapter 10.  Sessions
      Section 10.1.  Introducing Session Management
      Section 10.2.  PHP Session Management
      Section 10.3.  Case Study: Using Sessions in Validation
      Section 10.4.  When to Use Sessions
      Section 10.5.  PHP Session API and Configuration
      Chapter 11.  Authentication and Security
      Section 11.1.  HTTP Authentication
      Section 11.2.  HTTP Authentication with PHP
      Section 11.3.  Form-Based Authentication
      Section 11.4.  Protecting Data on the Web
      Chapter 12.  Errors, Debugging, and Deployment
      Section 12.1.  Errors
      Section 12.2.  Common Programming Errors
      Section 12.3.  Custom Error Handlers
      Chapter 13.  Reporting
      Section 13.1.  Creating a Report
      Section 13.2.  Producing PDF
      Section 13.3.  PDF-PHP Reference
      Chapter 14.  Advanced Features of Object-Oriented Programming in PHP 5
      Section 14.1.  Working with Class Hierarchies
      Section 14.2.  Class Type Hints
      Section 14.3.  Abstract Classes and Interfaces
      Section 14.4.  Freight Calculator Example
      Chapter 15.  Advanced SQL
      Section 15.1.  Exploring with SHOW
      Section 15.2.  Advanced Querying
      Section 15.3.  Manipulating Data and Databases
      Section 15.4.  Functions
      Section 15.5.  Automating Querying
      Section 15.6.  Table Types
      Section 15.7.  Backup and Recovery
      Section 15.8.  Managing Users and Privileges
      Section 15.9.  Tuning MySQL
      Chapter 16.  Hugh and Dave's Online Wines:A Case Study
      Section 16.1.  Functional and System Requirements
      Section 16.2.  Application Overview
      Section 16.3.  Common Components
      Chapter 17.  Managing Customers
      Section 17.1.  Code Overview
      Section 17.2.  Customer Validation
      Section 17.3.  The Customer Form
      Chapter 18.  The Shopping Cart
      Section 18.1.  Code Overview
      Section 18.2.  The Winestore Home Page
      Section 18.3.  The Shopping Cart Implementation
      Chapter 19.  Ordering and Shipping at the Online Winestore
      Section 19.1.  Code Overview
      Section 19.2.  Credit Card and Shipping Instructions
      Section 19.3.  Finalizing Orders
      Section 19.4.  HTML and Email Receipts
      Chapter 20.  Searching and Authentication in the Online Winestore
      Section 20.1.  Code Overview
      Section 20.2.  Searching and Browsing
      Section 20.3.  Authentication
      Appendix A.  Linux Installation Guide
      Section A.1.  Finding Out What's Installed
      Section A.2.  Installation Overview
      Section A.3.  Installing MySQL
      Section A.4.  Installing Apache
      Section A.5.  Installing PHP
      Section A.6.  What's Needed for This Book
      Appendix B.  Microsoft Windows Installation Guide
      Section B.1.  Installation Overview
      Section B.2.  Installing with EasyPHP
      Section B.3.  What's Needed for This Book
      Appendix C.  Mac OS X Installation Guide
      Section C.1.  Getting Started
      Section C.2.  Installing MySQL
      Section C.3.  Setting Up Apache and PHP
      Section C.4.  What's Needed for This Book
      Appendix D.  Web Protocols
      Section D.1.  Network Basics
      Section D.2.  Hypertext Transfer Protocol
      Appendix E.  Modeling and Designing Relational Databases
      Section E.1.  The Relational Model
      Section E.2.  Entity-Relationship Modeling
      Appendix F.  Managing Sessions in theDatabase Tier
      Section F.1.  Using a Database to Keep State
      Section F.2.  PHP Session Management
      Section F.3.  MySQL Session Store
      Appendix G.  Resources
      Section G.1.  Client Tier Resources
      Section G.2.  Middle-Tier Resources
      Section G.3.  Database Tier Resources
      Section G.4.  Security and Cryptography Resources
      Appendix H.  The Improved MySQL Library
      Section H.1.  New Features
      Section H.2.  Getting Started
      Section H.3.  Using the New Features
    Colophon
    Copyright



 

Previous Section  < Day Day Up >  Next Section

12.3 Custom Error Handlers

The errors produced by PHP are useful when developing scripts, but aren't sufficient for deployment in a web database application. Errors should inform users without confusing them, not expose secure internal information, report details to administrators, and have a look and feel consistent with the application. This section shows you how to add a professional error handler to your application, and also how to improve the internal PHP error handler to produce even more information during development.

If you're not keen to develop a custom handler (or don't want to use ours!), you'll find an excellent class that includes one at http://www.phpclasses.org/browse.html/package/345.

12.3.1 A Basic Custom Handler

To begin, we show you how to implement a simple custom handler. The set_error_handler( ) function allows you to define a custom error handler that replaces the internal PHP handler for non-critical errors:


string set_error_handler(string error_handler)

The function takes one parameter, a user-defined error_handler function that is called whenever an error occurs. On success, the function returns the previously defined error handler function name, which can be saved and restored later with another call to set_error_handler( ). The function returns false on failure.

The custom error handler is not called for the following errors: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, and E_COMPILE WARNING. For these, the PHP internal error handler is always used.

For example, to set up a new error handler that's defined in the function customHandler( ), you can register it with:

set_error_handler("customHandler");

The function name is passed as a quoted string, and doesn't include the brackets. After the new handler is defined, the error_reporting level in php.ini or defined in the script with error_reporting( ) has no effect: all errors are either passed to the custom handler or, if they're critical, to the PHP internal default handler. We discuss this more later.

A custom error handler function must accept at least two parameters: an integer error number and a descriptive error string. Three additional optional parameters can be also be used: a string representing the filename of the script that caused the error; an integer line number indicating the line in that file where the error was noticed; and, an array of additional variable context information.

Our initial implementation of the customHandler( ) function is shown in Example 12-1. It supports all five parameters, and uses them to construct an error string that displays more information than the default PHP internal handler. It handles only E_NOTICE and E_WARNING errors, and ignores all others.

After running the example, the handler outputs the following:

<hr><font color="red">

<b>Custom Error Handler -- Warning/Notice<b>

<br>An error has occurred on 38 line in the

  /usr/local/apache2/htdocs/example.12-1.php file.

<br>The error is a "Missing argument 1 for double( )" (error #2).

 <br>Here's some context information:<br>

<pre>

Array

(

    [number] => 

)

</pre></font>

<hr>

The useful additional information is the output of a call to the print_r( ) that dumps the state of all variables in the current context. In this case, there's only one variable which doesn't have a value: that's not surprising, because the warning is generated because the parameter is missing!

The context information is extracted from the fifth, array parameter to the customHandler( ) function. It contains as elements all of the variables that are in the current scope when the error occurred. In our Example 12-1, only one variable was in scope within the function, $number. If the customHandler( ) function is called from outside of all functions (in the main body of the program), it shows the contents of all global variables including the superglobals $_GET, $_POST, and $_SESSION.

Example 12-1. A script with a custom error handler
<!DOCTYPE HTML PUBLIC

                 "-//W3C//DTD HTML 4.01 Transitional//EN"

                 "http://www.w3.org/TR/html401/loose.dtd">

<html>

<head>

  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">

  <title>Error</title>

<body>

<h1>Two times!</h1>

<?php

function customHandler($number, $string, $file, $line, $context)

{

  switch ($number)

  {

     case E_WARNING:

     case E_NOTICE:

       print "<hr><font color=\"red\">\n";

       print "<b>Custom Error Handler -- Warning/Notice<b>\n";

       print "<br>An error has occurred on {$line} line in 

              the {$file} file.\n";

       print "<br>The error is a \"{$string}\" (error #{$number}).\n ";

       print "<br>Here's some context information:<br>\n<pre>\n";

       print_r($context);

       print "\n</pre></font>\n<hr>\n";

       break;

     default:

        // Do nothing

  }

}



function double($number)

{

  return $number*2;

}



set_error_handler("customHandler");



// Generates a warning for a missing parameter

print "Two times ten is: " . double( );

?>

</body>

</html>

As we stated earlier, the customHandler( ) function isn't called for the critical error types. For example, if we omit the semi-colon from the end of the first print statement:

print "Two times ten is: " . double( )

then the parse error that's output is the PHP default:

Parse error: parse error, unexpected T_PRINT in

  /usr/local/apache2/htdocs/example.12-1.php on line 46

You can't change this behavior.[1] Custom handlers work only for the E_WARNING and E_NOTICE errors, and for the entire USER class. The techniques to generate USER class errors are discussed in the next section.

[1] This isn't strictly true. It isn't possible to change the behavior within your scripts or in the php.ini file. However, it is possible to force all output produced by your script through a function, and to catch them after they've been output; this has a significant performance penalty. See http://www.webkreator.com/php/configuration/handling-fatal-and-parse-errors.html for detailed information.

The custom handler we've shown here deliberately doesn't support USER class errors. If, for example, an E_USER_ERROR is generated, the handler is called, but nothing is output and the script doesn't stop. It's the responsibility of the programmer to deal with all error types, and to stop or continue the execution as appropriate. We develop a handler for all errors in the next section.

12.3.2 A Production Error Handler

The simple custom error handler in the previous section has several disadvantages:

  • The handler offers only slightly more information than the PHP internal handler. Ideally, it should also include a backtrace, showing which function called the one containing the error, and so on back to the beginning of the script.

  • It shows technical information to the user, which is both confusing and a security risk. It should explain to the user that there's a problem with their request, and then log or send the technical information to someone who can fix it.

  • It can't handle programmer-generated errors. For example, in Chapter 6, we've used the showerror( ) function to handle database server errors. These errors should be integrated with our custom handler.

  • Our handler doesn't stop script execution, and doesn't leave the application in a known state. For example, if a session is open or the database is locked, the error handler doesn't clean these up.

In this section, we improve our custom handler to address these problems.

12.3.2.1 Including debugging information

Example 12-2 shows an improved error handler that reports more information about how and where the error occurred. For example, if an E_WARNING error is generated by the fragment:

// Generates a warning for a missing parameter

print "Two times ten is: " . double( );

then the handler outputs:

[PHP Error 20030616104153]E_WARNING on line 67 in bug.php. 

[PHP Error 20030616104153]Error: "Missing argument 1 for double( )" 

  (error #2). 

[PHP Error 20030616104153]Backtrace: 

[PHP Error 20030616104153] 0: double (line 67 in bug.php) 

[PHP Error 20030616104153] 1: double (line 75 in bug.php) 

[PHP Error 20030616104153]Variables in double ( ): 

[PHP Error 20030616104153] number is NULL 

[PHP Error 20030616104153]Client IP: 192.168.1.1

The backTrace( ) function uses the PHP library function debug_backtrace( ) to show a call graph, that is, the hierarchy of functions that were called to reach the function containing the bug. In this example, call #1 was from the main part of the script (though this is shown as a call from double( ), which is the function name that was called—this is a bug in debug_backtrace( )) and call #0 was the double( ) function that caused the error.

The debug_backtrace( ) function stores more details than the function name, but they are in a multidimensional array. If you're interested in using the function directly, try adding the following to your code:

var_dump(debug_backtrace( ));

Our custom handler also includes the following fragment:

$prepend = "\n[PHP Error " . date("YmdHis") . "]";

$error = ereg_replace("\n", $prepend, $error);

This replaces the carriage return at the beginning of each error line with a fragment that includes the date and time. Later in this section, we write this information to an error log file.

Example 12-2. A custom handler with a backtrace
<?php

function backTrace($context)

{

   // Get a backtrace of the function calls

   $trace = debug_backtrace( );



   $calls = "\nBacktrace:";



   // Start at 2 -- ignore this function (0) and the customHandler( ) (1)

   for($x=2; $x < count($trace); $x++)

   {

     $callNo = $x - 2;

     $calls .= "\n  {$callNo}: {$trace[$x]["function"]} ";

     $calls .= "(line {$trace[$x]["line"]} in {$trace[$x]["file"]})";

   }



   $calls .= "\nVariables in {$trace[2]["function"]} ( ):";



   // Use the $context to get variable information for the function

   // with the error

   foreach($context as $name => $value)

   {

     if (!empty($value))

       $calls .= "\n  {$name} is {$value}";

     else

       $calls .= "\n  {$name} is NULL";

   }

   return ($calls);

}



function customHandler($number, $string, $file, $line, $context)

{

  $error = "";



  switch ($number)

  {

     case E_WARNING:

       $error .= "\nE_WARNING on line {$line} in {$file}.\n";

       break;

     case E_NOTICE:

       $error .= "\nE_NOTICE on line {$line} in {$file}.\n";

       break;

     default:

       $error .= "UNHANDLED ERROR on line {$line} in {$file}.\n";

  }

  $error .= "Error: \"{$string}\" (error #{$number}).";

  $error .= backTrace($context);

  $error .= "\nClient IP: {$_SERVER["REMOTE_ADDR"]}";



  $prepend = "\n[PHP Error " . date("YmdHis") . "]";

  $error = ereg_replace("\n", $prepend, $error);



  // Output the error as pre-formatted text

  print "<pre>{$error}</pre>";

  // Log to a user-defined filename

  // error_log($error, 3, "/home/hugh/php_error_log");



}

12.3.2.2 Logging and notifying the user

Output of errors to the user agent (usually a web browser) is useful for debugging during development but shouldn't be used in a production application. Instead, you can use the PHP library error_log( ) function to log to an email address or a file. Also, you should alert the user of actions they can take, without providing them with unnecessary technical information.

The error_log( ) function has the following prototype:


int error_log (string message, int message_type [, string destination [, string extra_headers]])

The string message is the error message to be logged. The message_type can be 0, 1, or 3. A setting of 0 sends the message to the PHP system's error logger, which is configured using the error_log directive in the php.ini file. A setting of 1 sends an email to the destination email address with any additional email extra_headers that are provided. A setting of 3 appends the message to the file destination. A setting of 2 isn't available.

In practice, you should choose between logging to an email address or to a user-defined file; it's unlikely that the web server process will have permissions to write to the system error logger. To log to a file using our customHandler( ) in Example 12-2, uncomment the statement:

error_log($error, 3, "/home/hugh/php_error_log");

This will log to whatever is set as the logging destination by the third parameter; in this example, we're writing into a file in the administrator's home directory. You could use the directory C:\Windows\temp on a Microsoft Windows platform. If you'd prefer that errors arrive in email, replace the error_log( ) call with:

// Use a real email address!

error_log($error, 1, "hugh@asdfgh.com");

In practice, we recommend logging to a file and monitoring the file. Receiving emails might sound like a good idea, but in practice if the DBMS is unavailable or another serious problem occurs, you're likely to receive hundreds of emails in a short time.

When the application goes into production, we also recommend removing the print statement that outputs messages to the browser. Instead, you should add a generic message that alerts the user to a problem and asks them contact the system administrator. You might also follow these statements with a call to die( ) to stop the program execution; remember, it's up to you whether you stop the program when an error occurs.

A better approach than adding print statements to show the error to the user is to create a template with the same look and feel as your application, and include the error messages there; we use this approach in our online winestore in later chapters. This approach also has the additional advantage that it prevents the problem we describe next.

An additional problem with printing errors without a template is that they can still appear anywhere in a partial page. This can lead to user confusion, produce non-compliant HTML, and look unattractive. If you use a template, you can choose whether to output the page or not: nothing is output until you call the show( ) method. However, even without a template, it's possible to prevent this happening by using the PHP library output buffering library.

The output buffering approach works as shown in the simplified error handler in Example 12-3. The call to ob_start( ) at the beginning of the script forces all output to be held in a buffer. When an error occurs, the ob_end_clean( ) function in the customHandler( ) function throws away whatever is in the buffer, and then outputs only the error message and stops the script. If no errors occur, the script runs as normal and the ob_end_flush( ) function outputs the document by flushing the buffer. With this approach, partial pages can't occur.

Example 12-3. Using output buffering to prevent partial output pages
<?php

  // start buffering

  ob_start( );

?>

<!DOCTYPE HTML PUBLIC

                 "-//W3C//DTD HTML 4.01 Transitional//EN"

                 "http://www.w3.org/TR/html401/loose.dtd">

<html>

<head>

  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">

  <title>Error</title>

<body>

<?php

function customHandler($number, $string, $file, $line, $context)

{

  // Throw away the current buffer

  ob_end_clean( );



  print "An error occurred!";

  die( );

}



set_error_handler("customHandler");



// Generates an E_NOTICE

print $a;



// Output the buffer

ob_end_flush( );



?>

</body>

</html>

12.3.2.3 Triggering your own errors

In Chapter 6, we triggered our own errors by calling the showerror( ) function, which outputs MySQL error messages. We added our own calls to die( ) to handle PEAR DB errors in Chapter 7. However, these approaches aren't consistent with using the custom error handler we've built in this chapter. Now that we have an error handler, it would be useful to be able to trigger its use through programmer-generated errors. This is where the USER class of errors and the PHP library function trigger_error( ) are useful:


void trigger_error (string error_message [, int error_type])

The function triggers a programmer-defined error using two parameters: an error_message and an optional error_type that's set to one of E_USER_ERROR, E_USER_WARNING, or E_USER_NOTICE. The function calls the current error handler, and provides the same five parameters as other PHP error types.

Example 12-4 is a modified handler that processes errors generated by trigger_error( ). In addition, it stops the script when WARNING or ERROR class errors occur.

Example 12-4. A custom error handler that supports programmer-generated errors
function customHandler($number, $string, $file, $line, $context)

{

  $error = "";



  switch ($number)

  {

     case E_USER_ERROR:

       $error .= "\nERROR on line {$line} in {$file}.\n";

       $stop = true;

       break;

     case E_WARNING:

     case E_USER_WARNING:

       $error .= "\nWARNING on line {$line} in {$file}.\n";

       $stop = true;

       break;

     case E_NOTICE:

     case E_USER_NOTICE:

       $error .= "\nNOTICE on line {$line} in {$file}.\n";

       $stop = false;

       break;

     default:

       $error .= "UNHANDLED ERROR on line {$line} in {$file}.\n";

       $stop = false;

  }

  $error .= "Error: \"{$string}\" (error #{$number}).";

  $error .= backTrace($context);

  $error .= "\nClient IP: {$_SERVER["REMOTE_ADDR"]}";



  $prepend = "\n[PHP Error " . date("YmdHis") . "]";

  $error = ereg_replace("\n", $prepend, $error);



  // Throw away the buffer

  ob_end_clean( );



  print "<pre>{$error}</pre>";

  // Log to a user-defined filename

  // error_log($error, 3, "/home/hugh/php_error_log");



  if ($stop == true)

    die( ); 

}

You can use this handler for several different purposes. For example, if a MySQL connection fails, you can report an error and halt the script:

// Connect to the MySQL server

if (!($connection = @ mysql_connect($hostname, $username, $password)))

   trigger_error("Could not connect to DBMS", E_USER_ERROR);

You can also send error codes and messages through to the handler that are reported as the error string:

if (!(mysql_select_db($databaseName, $connection)))

 trigger_error(mysql_errno( ) . " : " . mysql_error( ), E_USER_ERROR);

You could even use this to log security or other problems. For example, if the user fails to log in with the correct password, you could store a NOTICE:

if ($password != $storedPassword)

  trigger_error("Incorrect login attempt by {$username}", E_USER_NOTICE);

We use trigger_error( ) extensively for error reporting in the online winestore in Chapter 16 through Chapter 20.

12.3.2.4 Cleaning up the application

An advantage of a custom error handler is that you can add additional features to gracefully stop the application when an error occurs. For example, you might delete session variables, close database connections, unlock a database table, and log out the user. What actions are carried out is dependent on the application requirements, and we don't discuss this in detail here. However, our online winestore error handler in Chapter 16 carries out selected cleanup actions based on the state of session variables, and leaves the application in a known state.

    Previous Section  < Day Day Up >  Next Section







    Copyright © 2010 | Domen maybe sale - bye this domen