Exception Handling in PHP

In this second part of a two-part series on error and exception handling in PHP, we focus specifically on handling exceptions. This article is excerpted from chapter 8 of the book Beginning PHP and Oracle: From Novice to Professional, written by W. Jason Gilmore and Bob Bryla (Apress; ISBN: 1590597702).

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

{mospagebreak title=PHP’s Exception-Handling Implementation}

This section introduces PHP’s exception-handling feature. Specifically, we 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.

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.

{mospagebreak title=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!

——————————————–

{mospagebreak title=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 My_Exception is instantiated with a language and an 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 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();
}

{mospagebreak title=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, 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_Exceptionclass.

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