Tuesday, July 14, 2009

Beginning PHP and Oracle From Novice to Professional by W. Jason Gilmore and Bob Bryla Chapter 8

Even if you wear an S on your chest when it comes to programming, you can be sure that errors will creep into all but the most trivial of applications. Some of these errors are programmer-induced—
they are the result of mistakes made 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. Yet 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 losing data or crashing the application. 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:

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.

Error logging: Keeping a running log is the best way to record progress regarding the correction of repeated errors, as well as quickly identify newly introduced problems. In this chapter, you learn how to log messages to both your operating system syslog and a custom log file.

Exception handling: Prevalent among many popular languages (Java, C#, and Python, to name a few), exception handling was added to PHP with the version 5 release. Exception handling 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 develop- ment routine cannot be overstated. 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.

151

Setting the Desired Error Sensitivity Level
The error_reporting directive determines the reporting sensitivity level. Fourteen 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_ALL level reports any messages resulting from the 13 other levels residing below it in the table.

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

Error Level Description

E_ALL All errors and warnings E_COMPILE_ERROR Fatal compile-time errors E_COMPILE_WARNING Compile-time warnings
E_CORE_ERROR Fatal errors that occur during PHP’s initial start E_CORE_WARNING Warnings that occur during PHP’s initial start E_ERROR Fatal run-time errors
E_NOTICE Run-time notices

E_PARSE Compile-time parse errors

E_RECOVERABLE_ERROR Near-fatal errors (introduced in PHP 5.2)

E_STRICT PHP version portability suggestions (introduced in PHP 5.0)

E_USER_ERROR User-generated errors E_USER_NOTICE User-generated notices E_USER_WARNING User-generated warnings
E_WARNING Run-time warnings

Introduced in PHP 5, E_STRICT suggests code changes based on the core developers’ determina- tions 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. In PHP 6, E_STRICT is integrated into E_ALL; therefore, when running PHP 6, you’ll need to set the error_reporting directive to E_ALL in order to view these portability suggestions.

■Note The error_reporting directive uses the tilde character (~) to represent the logical operator NOT.

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.

Displaying Errors to the Browser

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 are 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 this:

Warning: fopen(subscribers.txt): failed to open stream: Permission denied in
/home/www/htdocs/ 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.

Displaying Startup Errors

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.

Logging Errors

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.

Identifying the Log File

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 Linux-based logging facility that offers an API for logging messages pertinent to system and application execution. The Windows event log is essen- tially the equivalent of the Linux syslog. These logs are commonly viewed using the Event Viewer.

Setting the Maximum Log Line Length

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.

Ignoring Repeated Errors

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

Ignoring Errors Originating from the Same Location

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

Storing Most Recent Error in a Variable

Enabling track_errors 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 docu- ment 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.
You have the option of setting the error_log directive to the operating system’s logging facility (syslog on Linux, Event Viewer on Windows), which will result in PHP’s error messages being written to the operating system’s logging facility or to a text file. 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, using a separate text file or database table is probably your only solution. If you control the server, 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.

Initializing PHP’s Logging Facility

The define_syslog_variables() function initializes the constants necessary for using the openlog(),
closelog(), and syslog() functions. Its prototype follows:

void define_syslog_variables(void)

You need to execute this function before using any of the following logging functions.

Opening the Logging Connection

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. Its prototype follows:

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

Several parameters are supported, including the following:

ident: Identifies messages. It is 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 such 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 an 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).

Closing the Logging Connection

The closelog() function closes the connection opened by openlog(). Its prototype follows:

int closelog(void)

Sending a Message to the Logging Destination

The syslog() function is responsible for sending a custom message to the syslog. Its prototype follows:

int syslog(int priority, string message)

The first parameter, priority, specifies the syslog priority level, presented in order of severity here:

LOG_EMERG: A serious system problem, likely signaling a crash

LOG_ALERT: A condition that must be immediately resolved to avert jeopardizing system integrity

LOG_CRIT: A critical error, which could render a service unusable but does not necessarily place the system in danger

LOG_ERR: A general error

LOG_WARNING: A general warning

LOG_NOTICE: A normal but notable condition

LOG_INFO: A general informational message

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 execu- tion 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, 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 experi- ence 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, excep- tion-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 allows 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("Could not open file.")
if something else goes wrong
throw Numberexception("Division by zero not allowed.")

// 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. The next section applies 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 touch upon the base excep- tion 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.

Extending the 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:

message: Intended to be a user-friendly explanation that presumably will be passed to the user via the getMessage() method, introduced in the following section.

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:

getMessage(): Returns the message if it is passed to the constructor. getCode(): Returns the error code if it is passed to the constructor. getLine(): Returns the line number for which the exception is thrown. getFile(): Returns the name of the file throwing the exception.
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.
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/8/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 interna- tionalize 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 My_Exception is instantiated with a language and an error code, it will read in the appro- priate language file, parsing each line into an associative array consisting of the error code and its corresponding message. The My_Exception class and a usage example are found in Listing 8-2.

Listing 8-2. The My_Exception Class in Action

class My_Exception 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 My_Exception

try {
throw new My_Exception("english",4);
}
catch (My_Exception $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 news- letter by submitting his or her e-mail address. Several outcomes are possible. For example, the user could do one of the following:

• Provide a valid e-mail address

• Provide an invalid e-mail address

• Neglect to enter any e-mail address at all

• Attempt to mount an attack such as a SQL injection

Proper exception handling will account for all such scenarios. However, 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 Invalid_Email_Exception class is responsible for notifying the site administrator in the case that the e-mail is deemed invalid. */

class Invalid_Email_Exception 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 Invalid_Email_Exception( "Invalid e-mail address!", $email);

else
return 1;
}

} catch (Exception $e) {
echo $e->getMessage();
} catch (Invalid_Email_Exception $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 Invalid_Email_Exception class.

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 unfore- seen problems in their code.
In the next chapter we 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.

0 comments: