MOVED TO CW Error and Exception Handling (edited – lorraine)

Beginning PHP and PostgreSQL 8: From Novice to Professional

Written by W. Jason Gilmore, Robert H. Treat

Note – fix tables & empty spaces

Published by Apress

Chapter 8



Even if you wear an S on your chest when it comes to programming, you can be sure that errors will be a part of all but the most trivial of applications. Some of these errors are programmer-induced; that is, they’re the result of blunders during the development process. Others are user-induced, caused by the end user’s unwillingness or inability to conform to application constraints. For example, the user might enter “12341234” when asked for an e-mail address, obviously ignoring what would otherwise be expected as valid input. Regardless of the source of the error, your application must be able to encounter and react to such unexpected errors in a graceful fashion, hopefully doing so without a loss of data or the crash of a program or system. In addition, your application should be able to provide users with the feedback necessary to understand the reason for such errors and potentially adjust their behavior accordingly.

This chapter introduces several features PHP has to offer for handling errors. Specifically, the following topics are covered:

  1. Configuration directives: PHP’s error-related configuration directives determine the bulk of the language’s error-handling behavior. Many of the most pertinent directives are introduced in this chapter. 
     
  2. Error logging: Keeping a running log of application errors is the best way to record progress regarding the correction of repeated errors, as well as quickly take note of newly introduced problems. In this chapter, you learn how to log messages to both your operating system syslog and a custom log file. 
     
  3. Exception handling: This long-awaited feature, prevalent among many popular languages (Java, C#, and Python, to name a few) and new to PHP 5, offers a standardized process for detecting, responding to, and reporting errors.

Historically, the development community has been notoriously lax in implementing proper application error handling. However, as applications continue to grow increasingly complex and unwieldy, the importance of incorporating proper error-handling strategies into your daily development routine cannot be understated. Therefore, you should invest some time becoming familiar with the many features PHP has to offer in this regard.

Configuration Directives

Numerous configuration directives determine PHP’s error-reporting behavior. Many of these directives are introduced in this section.

error_reporting (string)

Scope: PHP_INI_ALL ; Default value: E_ALL & ~E_NOTICE & ~E_STRICT

The error_reporting directive determines the reporting sensitivity level. Thirteen separate levels are available, and any combination of these levels is valid. See Table 8-1 for a complete list of these levels. Note that each level is inclusive of all levels residing below it. For example, the E_WARNING level reports any messages resulting from all 10 levels residing below it in the table.

Table 8-1. PHP’s Error-Reporting Levels  

Level

Description

E_ALL

All errors and warnings

E_ERROR

Fatal run-time errors

E_WARNING

Run-time warnings

E_PARSE

Compile-time parse errors

E_NOTICE

Run-time notices

E_STRICT

PHP version portability suggestions

E_CORE_ERROR

Fatal errors that occur during PHP’s initial start

E_CORE_WARNING

Warnings that occur during PHP’s initial start

E_COMPILE_ERROR

Fatal compile-time errors

E_COMPILE_WARNING

Compile-time warnings

E_USER_ERROR

User-generated errors

E_USER_WARNING

User-generated warnings

E_USER_NOTICE

User-generated notices

Take special note of E_STRICT , because it’s new as of PHP 5. E_STRICT suggests code changes based on the core developers’ determinations as to proper coding methodologies, and is intended to ensure portability across PHP versions. If you use deprecated functions or syntax, use references incorrectly, use var rather than a scope level for class fields, or introduce other stylistic discrepancies, E_STRICT calls it to your attention.


Note  The logical operator NOT is represented by the tilde character ( ~ ). This meaning is specific to this directive, as the exclamation mark ( ! ) bears this significance throughout all other parts of the language.


During the development stage, you’ll likely want all errors to be reported. Therefore, consider setting the directive like this:

error_reporting E_ALL

However, suppose that you were only concerned about fatal run-time, parse, and core errors. You could use logical operators to set the directive as follows:

error_reporting E_ERROR | E_PARSE | E_CORE_ERROR

As a final example, suppose you want all errors reported except for user-generated ones:

error_reporting E_ALL & ~(E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE)

As is often the case, the name of the game is to remain well-informed about your application’s ongoing issues without becoming so inundated with information that you quit looking at the logs. Spend some time experimenting with the various levels during the development process, at least until you’re well aware of the various types of reporting data that each configuration provides.

display_errors (On | Off)

Scope: PHP_INI_ALL ; Default value: On

Enabling the display_errors directive results in the display of any errors meeting the criteria defined by error_reporting . You should have this directive enabled only during testing, and keep it disabled when the site is live. The display of such messages not only is likely to further confuse the end user, but could also provide more information about your application/server than you might like to make available. For example, suppose you were using a flat file to store newsletter subscriber e-mail addresses. Due to a permissions misconfiguration, the application could not write to the file. Yet rather than catch the error and offer a user-friendly response, you instead opt to allow PHP to report the matter to the end user. The displayed error would look something like:

Warning: fopen(subscribers.txt): failed to open stream: Permission denied in /home/www/htdocs/pmnp/8/displayerrors.php on line 3

Granted, you’ve already broken a cardinal rule by placing a sensitive file within the document root tree, but now you’ve greatly exacerbated the problem by informing the user of the exact location and name of the file. The user can then simply enter a URL similar to http:// www.example.com/subscribers.txt , and proceed to do what he will with your soon-to-be furious subscriber base.

display_startup_errors (On | Off)

Scope: PHP_INI_ALL ; Default value: Off

Enabling the display_startup_errors directive will display any errors encountered during the initialization of the PHP engine. Like display_errors , you should have this directive enabled during testing, and disabled when the site is live.

log_errors (On | Off)

Scope: PHP_INI_ALL ; Default value: Off

Errors should be logged in every instance, because such records provide the most valuable means for determining problems specific to your application and the PHP engine. Therefore, you should keep log_errors enabled at all times. Exactly to where these log statements are recorded depends on the error_log directive.

error_log (string)

Scope: PHP_INI_ALL ; Default value: Null

Errors can be sent to the system syslog, or can be sent to a file specified by the administrator via the error_log directive. If this directive is set to syslog , error statements will be sent to the syslog on Linux, or to the event log on Windows.

If you’re unfamiliar with the syslog, it’s a Unix-based logging facility that offers an API for logging messages pertinent to system and application execution. The Windows event log is essentially the equivalent to the Unix syslog. These logs are commonly viewed using the Event Viewer.

log_errors_max_len (integer)

Scope: PHP_INI_ALL ; Default value: 1024

The log_errors_max_len directive sets the maximum length, in bytes, of each logged item. The default is 1,024 bytes. Setting this directive to 0 means that no maximum length is imposed.

ignore_repeated_errors (On | Off)

Scope: PHP_INI_ALL ; Default value: Off

Enabling this directive causes PHP to disregard repeated error messages that occur within the same file and on the same line.

ignore_repeated_source (On | Off)

Scope: PHP_INI_ALL ; Default value: Off

Enabling this directive causes PHP to disregard repeated error messages emanating from different files or different lines within the same file.

track_errors (On | Off)

Scope: PHP_INI_ALL ; Default value: Off

Enabling this directive causes PHP to store the most recent error message in the variable $php_errormsg . Once registered, you can do as you please with the variable data, including output it, save it to a database, or do any other task suiting a variable.

Error Logging

If you’ve decided to log your errors to a separate text file, the Web server process owner must have adequate permissions to write to this file. In addition, be sure to place this file outside of the document root to lessen the likelihood that an attacker could happen across it and potentially uncover some information that is useful for surreptitiously entering your server. When you write to the syslog, the error messages look like this:

Dec 5 10:56:37 example.com httpd: PHP Warning:
fopen(/home/www/htdocs/subscribers.txt): failed to open stream: Permission
denied in /home/www/htdocs/book/8/displayerrors.php on line 3

When you write to a separate text file, the error messages look like this:

[05-Dec-2005 10:53:47] PHP Warning:
fopen(/home/www/htdocs/subscribers.txt): failed to open stream: Permission
denied in /home/www/htdocs/book/8/displayerrors.php on line 3

As to which one to use, that is a decision that you should make on a per-environment basis. If your Web site is running on a shared server, then using a separate text file or database table is probably your only solution. If you control the server, then using the syslog may be ideal, because you’d be able to take advantage of a syslog-parsing utility to review and analyze the logs. Take care to examine both routes and choose the strategy that best fits the configuration of your server environment.

PHP enables you to send custom messages as well as general error output to the system syslog. Four functions facilitate this feature. These functions are introduced in this section, followed by a concluding example.

define_syslog_variables()

void define_syslog_variables(void)

The define_syslog_variables() function initializes the constants necessary for using the openlog() , closelog() , and syslog() functions. You need to execute this function before using any of the following logging functions.

openlog()

int openlog(string ident, int option, int facility)

The openlog() function opens a connection to the platform’s system logger and sets the stage for the insertion of one or more messages into the system log by designating several parameters that will be used within the log context:

  • ident : A message identifier added to the beginning of each entry. Typically this value is set to the name of the program. Therefore, you might want to identify PHP-related messages as “PHP” or “PHP5”. 
  • option : Determines which logging options are used when generating the message. A list of available options is offered in Table 8-2. If more than one option is required, separate each option with a vertical bar. For example, you could specify three of the options like so: LOG_ODELAY | LOG_PERROR | LOG_PID
  • facility : Helps determine what category of program is logging the message. There are several categories, including LOG_KERN , LOG_USER , LOG_MAIL , LOG_DAEMON , LOG_AUTH , LOG_LPR , and LOG_LOCALN , where N  is a value ranging between 0 and 7. Note that the designated facility determines the message destination. For example, designating LOG_CRON results in the submission of subsequent messages to the cron log, whereas designating LOG_USER results in the transmission of messages to the messages file. Unless PHP is being used as a command-line interpreter, you’ll likely want to set this to LOG_USER . It’s common to use LOG_CRON when executing PHP scripts from a crontab . See the syslog documentation for more information about this matter.

Table 8-2. Logging Options

Option Description
LOG_CONS

If error occurs when writing to the syslog, send output to the system console.

LOG_NDELAY Immediately open the connection to the syslog.
LOG_ODELAY

Do not open the connection until the first message has been submitted for logging. This is the default.

LOG_PERROR Output the logged message to both the syslog and standard error.
LOG_PID Accompany each message with the process ID (PID).

 

 

 

 

 

 

 

 

closelog()

int closelog(void)

The closelog() function closes the connection opened by openlog() .

syslog()

int syslog(int priority, string message)

The syslog() function is responsible for sending a custom message to the syslog. The first parameter, priority , specifies the syslog priority level, presented in order of severity here:

  1. LOG_EMERG : A serious system problem, likely signaling a crash 
     
  2. LOG_ALERT : A condition that must be immediately resolved to avert jeopardizing system integrity 
     
  3. LOG_CRIT : A critical error, which could render a service unusable but does not necessarily place the system in danger 
     
  4. LOG_ERR : A general error 
     
  5. LOG_WARNING : A general warning 
     
  6. LOG_NOTICE : A normal but notable condition 
     
  7. LOG_INFO : General informational message 
     
  8. LOG_DEBUG : Information that is typically only relevant when debugging an application

The second parameter, message , specifies the text of the message that you’d like to log. If you’d like to log the error message as provided by the PHP engine, you can include the string %m in the message. This string will be replaced by the error message string ( strerror ) as offered by the engine at execution time.

Now that you’ve been acquainted with the relevant functions, here’s an example:

<?php
    define_syslog_variables();
    openlog("CHP8", LOG_PID, LOG_USER);
    syslog(LOG_WARNING,"Chapter 8 example warning.");
    closelog();
?>

This snippet would produce a log entry in the messages syslog file similar to the following:

——————————————–Dec 5 20:09:29 CHP8[30326]: Chapter 8 example warning.
——————————————–

Exception Handling

Languages such as Java, C#, and Python have long been heralded for their efficient error-management abilities, accomplished through the use of exception handling. If you have prior experience working with exception handlers, you likely scratch your head when working with any language, PHP included, that doesn’t offer similar capabilities. This sentiment is apparently a common one across the PHP community, because as of version 5.0, exception-handling capabilities have been incorporated into the language. In this section, you’ll learn all about this feature, including the basic concepts, syntax, and best practices. Because exception handling is new to PHP, you may not have any prior experience incorporating this feature into your applications. Therefore, a general overview is presented regarding the matter. If you’re already familiar with the basic concepts, feel free to skip ahead to the PHP-specific material later in this section.

Why Exception Handling Is Handy

In a perfect world, your program would run like a well-oiled machine, devoid of both internal and user-initiated errors that disrupt the flow of execution. However, programming, like the real world, remains anything but an idyllic dream, and unforeseen events that disrupt the ordinary chain of events happen all the time. In programmer’s lingo, these unexpected events are known as exceptions. Some programming languages have the capability to react gracefully to an exception by locating a code block that can handle the error. This is referred to as throwing the exception. In turn, the error-handling code takes ownership of the exception, or catches it. The advantages to such a strategy are many.

For starters, exception handling essentially brings order to the error-management process through the use of a generalized strategy for not only identifying and reporting application errors, but also specifying what the program should do once an error is encountered. Furthermore, exception-handling syntax promotes the separation of error handlers from the general application logic, resulting in considerably more organized, readable code. Most languages that implement exception handling abstract the process into four steps:

  1. The application attempts something.
  2. If the attempt fails, the exception-handling feature throws an exception. 
     
  3. The assigned handler catches the exception and performs any necessary tasks. 
     
  4. The exception-handling feature cleans up any resources consumed during the attempt.

Almost all languages have borrowed from the C++ language’s handler syntax, known as try / catch . Here’s a simple pseudocode example:

try {
   
perform some task
   
if something goes wrong
       
throw exception("Something bad happened" )
// Catch the thrown exception
} catch(exception) {
   
output the exception message
}

You can also set up multiple handler blocks, which enables you to account for a variety of errors. You can accomplish this either by using various predefined handlers, or by extending one of the predefined handlers, essentially creating your own custom handler. PHP currently only offers a single handler, exception . However, that handler can be extended if necessary. It’s likely that additional default handlers will be made available in future releases. For the purposes of illustration, let’s build on the previous pseudocode example, using contrived handler classes to manage I/O and division-related errors:

try {
   
perform some task
   
if something goes wrong
       
throw IOexception("Something bad happened")
   
if something else goes wrong
       
throw Numberexception("Something really bad happened")
// Catch IOexception
} catch(IOexception) {
   
output the IOexception message
}
// Catch Numberexception
} catch(Numberexception) {
   
output the Numberexception message
}

If you’re new to exceptions, such a syntactical error-handling standard seems like a breath of fresh air. In the next section, we’ll apply these concepts to PHP by introducing and demonstrating the variety of new exception-handling procedures made available in version 5.

PHP’s Exception-Handling Implementation

This section introduces PHP’s exception-handling feature. Specifically, we’ll touch upon the base exception class internals, and demonstrate how to extend this base class, define multiple catch blocks, and introduce other advanced handling tasks. Let’s begin with the basics: the base exception class.

PHP’s Base Exception Class

PHP’s base exception class is actually quite simple in nature, offering a default constructor consisting of no parameters, an overloaded constructor consisting of two optional parameters, and six methods. Each of these parameters and methods is introduced in this section.

The Default Constructor

The default exception constructor is called with no parameters. For example, you can invoke the exception class like so:

throw new Exception();

Once the exception has been instantiated, you can use any of the six methods introduced later in this section. However, only four will be of any use; the other two are useful only if you instantiate the class with the overloaded constructor, introduced next.

The Overloaded Constructor

The overloaded constructor offers additional functionality not available to the default constructor through the acceptance of two optional parameters:

  1. message : Intended to be a user-friendly explanation that presumably will be passed t o the user via the getMessage() method, introduced in the following section. 
     
  2. error code : Intended to hold an error identifier that presumably will be mapped to some identifier-to-message table. Error codes are often used for reasons of internationalization and localization. This error code is made available via the getCode() method, introduced in the next section. Later, you’ll learn how the base exception class can be extended to compute identifier-to-message table lookups.

You can call this constructor in a variety of ways, each of which is demonstrated here:

throw new Exception("Something bad just happened", 4)
throw new Exception("Something bad just happened");
throw new Exception("",4);

Of course, nothing actually happens to the exception until it’s caught, as demonstrated later in this section.

Methods

Six methods are available to the exception class:

  1. getMessage() : Returns the message if it was passed to the constructor. 
     
  2. getCode() : Returns the error code if it was passed to the constructor. 
     
  3. getLine() : Returns the line number for which the exception is thrown. 
     
  4. getFile() : Returns the name of the file throwing the exception. 
     
  5. getTrace() : Returns an array consisting of information pertinent to the context in which the error occurred. Specifically, this array includes the file name, line, function, and function parameters. 
     
  6. getTraceAsString() : Returns all of the same information as is made available by getTrace() , except that this information is returned as a string rather than as an array.

Caution  Although you can extend the exception base class, you cannot override any of the preceding methods, because they are all declared as final . See Chapter 6 more for information about the final scope.


Listing 8-1 offers a simple example that embodies the use of the overloaded base class constructor, as well as several of the methods.

Listing 8-1. Raising an Exception

try {

    $fh = fopen("contacts.txt", "r") ;
    if (! $fh) {
        throw new Exception("Could not open the file!");
    }
}
catch (Exception $e) {
   
echo "Error (File: ".$e->getFile().", line ".
          $e->getLine()."): ".$e->getMessage();
}

If the exception is raised, something like the following would be output:

——————————————–Error (File: /usr/local/apache2/htdocs/read.php, line 6): Could not open the file!
——————————————–

Extending the Exception Class

Although PHP’s base exception class offers some nifty features, in some situations, you’ll likely want to extend the class to allow for additional capabilities. For example, suppose you want to internationalize your application to allow for the translation of error messages. These messages reside in an array located in a separate text file. The extended exception class will read from this flat file, mapping the error code passed into the constructor to the appropriate message (which presumably has been localized to the appropriate language). A sample flat file follows:

1,Could not connect to the database! 2,Incorrect password. Please try again. 3,Username not found.
4,You do not possess adequate privileges to execute this command.

When MyException is instantiated with a language and error code, it will read in the appropriate language file, parsing each line into an associative array consisting of the error code and its corresponding message. The MyException class and a usage example are found in Listing 8-2.

Listing 8-2. The MyException Class in Action

class MyException extends Exception {
    function __construct($language,$errorcode) {
        $this->language = $language;
        $this->errorcode = $errorcode;
   
}

    function getMessageMap() {
        $errors = file("errors/".$this->language.".txt");
        foreach($errors as $error) {
            
list($key,$value) = explode(",",$error,2);
            
$errorArray[$key] = $value;
        }
        return $errorArray[$this->errorcode];
   
}
} # end MyException

try {
   
throw new MyException("english",4);
}
catch (MyException $e) {
   
echo $e->getMessageMap();
}

Catching Multiple Exceptions

Good programmers must always ensure that all possible scenarios are taken into account. Consider a scenario in which your site offers an HTML form from which the user could subscribe to a newsletter by submitting his or her e-mail address. Several outcomes are possible. For example, the user could do one of the following:

  1. Provide a valid e-mail address 
     
  2. Provide an invalid e-mail address 
     
  3. Neglect to enter any e-mail address at all 
     
  4. Attempt to mount an attack such as a SQL injection

Proper exception handling will account for all such scenarios. However, in order to do so, you need to provide a means for catching each exception. Thankfully, this is easily possible with PHP. Listing 8-3 shows the code that satisfies this requirement.

Listing 8-3. Catching Multiple Exceptions

<?php
/* The InvalidEmailException class is responsible for notifying the site
    administrator in the case that the e-mail is deemed invalid. */
class InvalidEmailException extends Exception {

   function __construct($message, $email) {
        $this->message = $message;
        $this->notifyAdmin($email);
   }

   private function notifyAdmin($email) {
      mail("admin@example.org","INVALID EMAIL",$email,"From:web@example.com");
   }

}

/* The subscribe class is responsible for validating an e-mail address
   and adding the user e-mail address to the database. */
class subscribe {

   function validateEmail($email) {
      try {
        if ($email == "") {
           throw new Exception("You must enter an e-mail address!");


       
} else {
           list($user,$domain) = explode("@", $email);
           if (! checkdnsrr($domain, "MX"))
           {
             
throw new InvalidEmailException("Invalid e-mail address!", $email);
              } else {                 
                 return 1;
              }
          }
      } catch (Exception $e) {
         echo $e->getMessage();
      } catch (InvalidEmailException $e) {
         echo $e->getMessage();
      }

    }
    /* This method would presumably add the user’s e-mail address to
        a database. */
    function subscribeUser() {
       echo $this->email." added to the database!";
    }

} #end subscribe class

/* Assume that the e-mail address came from a subscription form. */

$_POST[’email’] = "someuser@example.com";

/* Attempt to validate and add address to database. */ 
if (isset($_POST[’email’])) {
     $subscribe = new subscribe();
     if($subscribe->validateEmail($_POST[’email’])) 
        
$subscribe->subscribeUser($_POST[’email’]);
}

?>

You can see that it’s possible for two different exceptions to fire, one derived from the base class and one extended from the base class, InvalidEmailException .

Summary

The topics covered in this chapter touch upon many of the core error-handling practices used in today’s programming industry. While the implementation of such features unfortunately remains more preference than policy, the introduction of capabilities such as logging and error handling has contributed substantially to the ability of programmers to detect and respond to otherwise unforeseen problems in their code.

In the next chapter, we’ll take an in-depth look at PHP’s string-parsing capabilities, covering the language’s powerful regular expression features, and offering insight into many of the powerful string-manipulation functions.

[gp-comments width="770" linklove="off" ]

chat sex hikayeleri Ensest hikaye