Learn PHP 7: Object-Oriented Modular Programming using HTML5, CSS3, JavaScript, XML, JSON, and MySQL (2016)

Chapter 5. Handling and Logging Exceptions

Steve Prettyman

(1)

Georgia, USA

Electronic supplementary material

The online version of this chapter (doi:10.1007/978-1-4842-1730-6_5) contains supplementary material, which is available to authorized users.

“The education of a man is never completed until he dies.” —Robert E. Lee (As quoted in Peter’s Quotations: Ideas for Our Time (1977) by Laurence J. Peter, p. 175)

Chapter Objectives/Student Learning Outcomes

After completing this chapter, the student will be able to:

·               Explain the difference between errors and exceptions

·               Create a PHP program that can handle general exceptions

·               Create a PHP program that can create, raise, and handle user exceptions

·               Explain and use a switch and/or embedded if/else statement

·               Create a PHP program that uses the while loop and/or for loop

·               Create a program that reads/updates a text file using a two-dimensional array

·               Create a PHP program that logs exceptions and e-mails support personnel

Handling Exceptions

As a programmer, you want to do everything possible to ensure that your program will not crash. Anyone using your application will get a bad taste in their mouths if they have to deal with system crashes. You have probably dealt with this situation too. As a user, you may have chosen one application over another because of bad reviews. Once an application has been determined to be “buggy,” it’s difficult to convince customers to use the product, even if newer versions have corrected some or all of the problems. An application must be created to handle every possible unanticipated event.

A program must look at each scenario and decide if it can continue or if it must shut down. There will always be a possibility that the application cannot continue to operate due to an unexpected event. Properly developed programs will let the user know that there is a problem without the program crashing. Users are more likely to understand when an application asks them to “try again later” (assuming the problem is fixed before they return to the web site).

Errors are program events that are handled by the system that cause the program to shut down. In some cases, the system can shut down the program and display an error message. Some errors immediately cause the program to crash (such as the server itself crashing). Errors are usually events beyond the control of the program and not directly caused by code (or lack of code) in the program. For example, insufficient memory will cause application errors.

Exceptions are events that are not part of the normal flow of the program logic. All exceptions should be handled by the program. Exceptions can be “raised” when the application anticipates a problem (a missing file) or when the user does something out of the ordinary (tries to enter invalid information). The program should “catch” all the exceptions. It can then examine the exception and determine if it can be corrected, ignored, or if the application must shut down. If a program does not catch exceptions, the system will display the exception message and then shut down the application.

PHP produces a mixture of errors and exceptions depending on the circumstances. Before PHP 5, exception handling did not exist. Thus, some older PHP commands produced errors (which shut down the program) instead of exceptions. In PHP 7 exception handling is the “rule”. PHP 7 Errors can be handled with exception handling techniques. If exceptions are not handled with program code, the program will halt as if it were a fatal error.

Any time an application is dependent on something external, it is probable that at some point that action will not take place. For example, in the Dog application, the user is expected to enter the proper information. The application must anticipate that not all users will enter correct information. The application is also dependent on several files existing on server (dog_interface, dog_container, dog_applications, and get_breeds). If any of these files are missing, the application cannot continue to function properly.

Most object-oriented programming languages use a standard format for handing exceptions. The current version of PHP also uses this approach. As you explore PHP examples on the Internet, you will discover existing PHP code that does not use this standard format. While this code will still execute in the current version of PHP, it is recommend that the standard techniques be used. The standard approach uses the try-catch block.

try {

// code that might cause an exception

}

catch(Exception $e) {

// code that executes if there is an exception

}

catch(Error $e) {

// PHP 7+ capture and handle errors

}

Any code that could cause an exception should be included in the try block. In addition, you may also want to consider placing other conditions (such as math calculations) in the try block.

try {

$result = $firstNumber / $secondNumber;

}

catch(Exception $e) {

// code that executes if there is an exception

}

catch(Error $e) {

// PHP 7+ capture and handle errors

}

This example might produce an exceptiwon if $secondNumber contains a zero (dividing by zero). If the exception occurs, the code would jump to the catch block. Any code in the block will then be executed. The statement $e->getMessage(); will display any system message related to the exception (in this case a message about the attempt to divide by zero). However, you do not have to use the system message; you can use echo or print to display messages to the users.

try {

$result = $firstNumber /$secondNumber;

}

catch(Exception $e) {

echo "You entered zero for the second number. Your entry must be greater than zero";

}

However, there is a problem with these examples. If you were trying to catch more than one type of exception in the try block, all exceptions would go into the one catch block. Any exception would display the same message. There are a couple of different ways you can handle this.

One way is by throwing your own exception instead of having the system throw it.

try {

        if ($secondNumber == 0)

{ throw new Exception("Zero Exception"); }

else { $result = $firstnumber / $secondnumber; }

// other code with exceptions }

catch(Exception $e) {

        switch ($e->getMessage()) {

                case "Zero Exception":

                        echo "The value of second number must be greater than zero";

                        break;

                case "Some other exception":

                        echo "You did something else wrong";

                        break;

                default:

                        echo $e->getMessage();

}

Programming note—In addition to getMessage method, theException andError objects include:

getCode() —Displays the code causing the exception

getFile() —Displays the file name containing code that threw the exception

getLine() —Displays the line number that threw the exception

getTrace() andgetTraceAsString() —Displays backtrace (exception flow through the program) information

In some circumstances it might be appropriate to display the Exception orError message to the users. However, the other methods should only be used for debugging or log entries. Providing code information to the users is usually unnecessary and is a breach of security.

In this example, a switch statement was used in the catch block to look at all possible exception messages. A switch statement accomplishes the same task as an embedded if statement. You could have used:

If($e->getMessage == "Zero Exception")

{ echo "The value of second number must be greater than zero"; }

else if($e->getMessage == "Some other exception")

{ echo "You did something else wrong"; }

else

{ echo $e->getMessage(); }

For some, the switch statement is easier to understand when looking at multiple possible values for the same property (variable) or the result of executing a method (as in this example). The default section of the switch statement (or the last else statement in the embedded if statement) catches anything you did not anticipate. In this example, you simply display the exception message for other exceptions.

As stated earlier, it’s very important that you handle all exceptions and errors. By including the default code you are able to handle exceptions and errors you may have never anticipated. Notice that each case section must include a break as the last statement. This keeps the code from following through into the next case statement.

Catch(Exception $e) {

        switch($e->getMessage()) {

                case "Zero Exception":

                        echo "The value of second number must be greater than zero";

                case "Some other exception":

                        echo "You did something else wrong";

                        break;

                default:

                        echo $e->getMessage():

        }

In this example, if the Zero Exception occurred, both of the messages ("The value of the second number must be greater than zero" and "You did something else wrong") would be displayed. The use of the switch statement is very common in catch blocks. However, as stated earlier, you can use the embedded if statement if you prefer.

Another way you can handle multiple exceptions is to create your own exceptions, throw them, and then catch them. You will need to create a class for your own exception.

class zeroException extends Exception {

   public function errorMessage() {

         $errorMessage = "Second Number cannot be " . $this->getMessage();

         return $errorMessage;

         }

}

try {

if ($secondNumber == 0)

{ throw new zeroException("Zero"); }

else

{ $result = $firstnumber / $secondnumber; }

// other code with exceptions }

catch(zeroException $e) {

        echo $e->errorMessage();

}

catch(Exception $e) {

        Echo $e->getMessage();

}

The zeroException class extends the class Exception. The extends keyword is used to inherit all of the functionality of the Exception classInheritance is another key component of object-oriented programming (along with encapsulation and polymorphism). A child class (likezeroException) can inherit all the properties and methods of its parent class (Exception). The child class then can add methods (such as the function errorMessage) specific to the class. Since zeroException inherited Exception, it is treated the same as any other exception. The zeroException can be thrown (throw new zeroException("Zero")) and it can be caught (catch(zeroException $e)).

Program note—Programmer-created exception classes inherit from Exception . Thus, all the functionality of the Exception class is available from within any new exception class.

Class zeroException extends Exception { }

The previous code creates a valid new zeroException class with no new methods.

catch(zeroException $e) { echo $e->getMessage(); }

This catch block will be called by the new exception and display the exception message generated by the Exception class.

For each exception or error class that is created and thrown, there must be a catch block to catch the exception or error. In the example, there are two catch blocks; one catches the zeroException and the other catches any other exceptions that might occur. Just like the previous example using a switch default or if else statement, you should always have the last catch blocks handle any remaining exceptions or errors. If the generic catch block is listed first, all exceptions would be caught by that block and not the specific block for the exception.

As stated, the developer should make every attempt to keep the application from crashing. Errors, however, are designed to display messages and shut down programs with an error code (what you consider to be “crashing” the program). Before PHP 7, in some cases, you could override this functionality by creating a method that will handle errors.

function errorHandler($severity, $message, $file, $line) {

    throw new errorException($message, 0, $severity, $file, $line); }

set_error_handler('errorHandler');

// set_error_handler() doesn’t work with all fatal errors, some can’t be thrown as Exceptions.

try { trigger_error( "User Error", E_USER_ERROR);

  }

catch(errorException $e)

{ echo $e->getMessage(); }

catch(Exception $e)

{ echo $e->getMessage(); }

// Code placed here would execute after an error with this handler. It would not execute if

// there was not a handler.

In this example, the set_error_handler method redirects all errors (that can be redirected) to the method errorHandler. When the method trigger_error causes E_USER_ERROR to occur, the handling of the error is redirected to the errorHandler method. This method then gathers the information from the error to throw an exception (errorException). The exception is captured by the catch(errorException $e) method, which causes the message "User Error" to be displayed.

In PHP 7, the Error object captures potential system errors as exceptions.

try {

call_method(null); // no such method!

} catch (Error $e)

{ echo $e->getMessage; }

Previously, the call to a non-existent function would cause a fatal error. Using the EngineException object allows the programmer to handle the error. The example shown previously (before PHP 7) would only capture some errors; this new technique is designed to allow the programmer much more control over errors. If this catch block is not included, any “error” would cause the program to crash with the "Fatal error: Uncaught exception" message.

If you have PHP 7 installed, use the new Error object along with the Exception object to avoid fatal errors whenever possible.

Security and performance—Usually the use of throwing and catching exceptions can reduce the amount of code needed in a program. However, there is a trade-off. Several studies of different object-oriented program languages have concluded that exception handling is less efficient (performance) than using developer created routines. The developer should use exceptions as true “exceptions” to the normal flow of the application. For more frequently occurring situations, the developer should create situation handling routines in the application.

A978-1-4842-1730-6_5_Fig1_HTML.jpg

Figure 5-1.

Data flow for the dog application

In the Dog application, the information flows between many different programs. Each of these programs must be able to handle exceptions properly. However, message handling should all occur in the interface. Any objects that are part of the business rules tier (dog_container, dog, andget_breeds) should pass any exception messages to the interface to handle. At first this may sound like a complex and confusing task. However, the hierarchy of exception handling will greatly simplify this task. As you are about to see, using exception handling will reduce the amount of code necessary.

When exceptions are thrown, the environment will look in the program (or class) itself to determine if there is a catch block that can handle the exception. If there is not a catch block, it will go up one level in the hierarchy and check any calling program (or program that has made an instance of the class) for a catch block. This process will continue until either a catch block has been discovered or it has been determined that the environment itself must handle the exception.

Using this process, you can throw exceptions in the dog_container, dog, and get_breeds modules without using catches. In the dog_interface, you can create a try block around calls to these files. Multiple catch blocks (or one with a switch statement) could be created in the interface to handle the exceptions from both the interface and all the other modules. This satisfies one of the requirements of three-tier programming. The business rules tier (and data tier) pass messages to the interface tier. The interface tier then determines how to handle those messages. It could display them to the users, place them in a log file (which you will look at later in this chapter), or ignore them (if it does not adversely affect the operation of the application).

Before you change the Dog application code, let’s look at an example of exceptions being handled by the hierarchy.

Example 5-1. testerror.php with error and exception-producing methods

<?php

class testerror {

   function produceerror() {

       trigger_error( "User Error", E_USER_ERROR);

       echo "This line will not display";  }

   function throwexception() {

       throw new userException("User Exception");

       echo "This line will not display";  }  }

?>

Example 5-2. The handleerror.php file captures error or exception

<?php

function errorHandler($severity, $message, $file, $line) {

            throw new errorException($message, 0, $severity, $file, $line);

   }

class userException extends Exception { }

Set_error_handler(‘errorHandler’);

try {

require_once(“testerror.php”);

$tester = new testerror();

$tester->produceerror();

echo “This line does not display”;

$tester->throwexception(); // will not execute if produceerror() is executed

echo “This line does not display”; }

catch (errorException $e ){

           echo $e->getMessage(); }

catch (userException $e) {

  echo $e->getMessage(); }

catch (Exception $e) {

           echo $e->getMessage(); }

echo “This line will display”;

?>

The testerror class (in Example 5-1) includes a method to cause an error (produceerror) and a method that throws an exception (throwexception). However, the class does not have try or catch blocks. It does not have the ability to react to any exceptions or errors that might occur.

The handleerror program (in Example 5-2) includes a method that will handle user errors (errorHandler), along with the set_error_handler command to redirect errors to this method. It also includes a class (userException) that can react when the userException exception is thrown in thetry block. The require_once statement is included in the try block in an attempt to capture the error if the file is missing. However, this happens to be a system error (not a user error) which cannot be redirected. To capture system errors in PHP 7, the Error class must be used within a catchblock as previously shown.

After the require_once statement, an instance of class testerror is created. If this class is missing, the system will also error with a fatal message. The block calls the produceerror method, which causes a user error. This error is redirected to the errorHandler, which throws an exception (errorException). The catch block receives the exception and displays the error message. Since exceptions do not shut down the program (like fatal errors), the flow of the program jumps to the first line after all the catch blocks and executes the echo statement (echo "This line will display";). The reaction to the error will cause the program to skip any remaining code in the try block. In this example, the throwexception method call would be ignored.

If the $tester->produceerror() line is commented out, the throwexception method call can take place. The userException is thrown in the method. The userException class inherits the Exception class. No special methods have been included in userException. The flow of the program will jump to the catch block for userException. This block uses the Exception class getMessage method to display the message. The logic then jumps to the first line of code after the catch blocks and executes the echo "This line will display" statement.

Program note— try/catch can also include a finally block after all catch blocks. The finally block will execute for all caught exceptions after the associated catch block has executed. PHP allows the finally block to exist without any catch blocks (but the try block must still exist). One of the most common uses of the finally block is to close files and/or databases when an exception has occurred. A program should not close before files and databases have been properly closed. If not closed properly, the data may become corrupt and not be accessible.

Do It

1.

Go to the book’s web site and download the files for Examples 5-1 and 5-2. Adjust the testerror program to only create an error. Create an additional testexception program (with a testexception class) to throw an exception. Now adjust the handleerror program to create an instance of both programs. The handleerror program should now be able to handle errors or exceptions from either program (class).

Exception and Error Handling vs. If/Else Conditions

A programmer can always choose to handle exceptions and errors using If/else conditional statements as shown in the dog application files from Chapter 4. It is not any less efficient to handle errors in this way (it might even be more efficient). However, as you are about to discover, theattitude of the code in the business rules tier (and data tier) changes if you use exception handling. When you use if/else statements, the flow of the program spends a lot of time being pessimistic by preparing for the worst (errors and/or exceptions). In many cases, by using exception handling, the coding for most of the business rules tier (and data tier) becomes optimistic including code which handles the normal operation of the program. The application relies on the interface tier to handle any problems.

Example 5-3. The dog.class with exception handling

<?php

class Dog

{

// ------------------------------------ Properties -----------------------------------------

private $dog_weight = 0;

private $dog_breed = "no breed";

private $dog_color = "no color";

private $dog_name = "no name";

private $error_message = "??";

private $breedxml = "";

// ------------------------------------ Constructor ----------------------------------------

function __construct($properties_array)

{

  if (method_exists('dog_container', 'create_object')) {

  $this->breedxml = $properties_array[4];

  $name_error = $this->set_dog_name($properties_array[0]) == TRUE ? 'TRUE,' : 'FALSE,';

  $color_error = $this->set_dog_color($properties_array[2]) == TRUE ? 'TRUE,' : 'FALSE,';

  $weight_error= $this->set_dog_weight($properties_array[3]) == TRUE ? 'TRUE' : 'FALSE';

  $breed_error = $this->set_dog_breed($properties_array[1]) == TRUE ? 'TRUE,' : 'FALSE,';

  $this->error_message = $name_error . $breed_error . $color_error . $weight_error;

  if(stristr($this->error_message, 'FALSE'))

  {

        throw new setException($this->error_message);

  }

}

    else { exit; }

}

function set_dog_name($value) {

  $error_message = TRUE;

  (ctype_alpha($value) && strlen($value) <= 20) ? $this->dog_name = $value : $this->error_message = FALSE;

  return $this->error_message; }

function set_dog_weight($value) {

  $error_message = TRUE;

  (ctype_digit($value) && ($value > 0 && $value <= 120)) ? $this->dog_weight = $value : $this->error_message = FALSE;

  return $this->error_message; }

function set_dog_breed($value) {

  $error_message = TRUE;

  ($this->validator_breed($value) === TRUE) ? $this->dog_breed = $value : $this->error_message = FALSE;

  return $this->error_message; }

function set_dog_color($value) {

  $error_message = TRUE;

  (ctype_alpha($value) && strlen($value) <= 15) ? $this->dog_color = $value : $this->error_message = FALSE;

  return $this->error_message; }

// ----------------------------------Get Methods--------------------------------------------

function get_dog_name() {

  return $this->dog_name; }

function get_dog_weight() {

  return $this->dog_weight; }

function get_dog_breed() {

  return $this->dog_breed; }

function get_dog_color() {

  return $this->dog_color; }

function get_properties() {

  return "$this->dog_name,$this->dog_weight,$this->dog_breed,$this->dog_color."; }

// ----------------------------------General Method-----------------------------------------

  private function validator_breed($value)

  {

    $breed_file = simplexml:load_file($this->breedxml);

    $xmlText = $breed_file->asXML();

    if(stristr($xmlText, $value) === FALSE)

  {

    return FALSE;

  }

  else

    {

      return TRUE;

    }

  }

}

?>

Comparing Example 4-8 to Example 5-3, you will notice only a couple of slight changes to the code. The __toString method has been removed and replaced by an if statement that checks to see if FALSE exists anywhere in the error_message string. If it does exist, a setException message is raised, passing the error_message string to the exception handler. This causes a logical change in the flow of the overall application. Instead of the dog_interface program (in Example 4-12) checking for user entry errors by calling the __toString method, the Dog class notifies thedog_interface (via a thrown exception) when user errors occur. Previously the interface had to pull the errors from the Dog class. In this example, the Dog class pushes the errors to the interface class. As you will see, this will eliminate code from the dog_interface program, since it no longer has to ask if there are any errors.

Security and performance—The __toString method “exposes” whatever it returns to any program that makes an instance of the class in which it exists. Using this method to pass error messages might allow a hacker to determine what incorrect information they are sending into a program. In the dog.class example from Chapter 4__toString passes back the error_message string containing 'TRUE' or 'FALSE' responses. This is more secure than returning error messages. However, by replacing the __toString method with throwing a special exception, you provide even better security. Hackers must now not only know what the error_message means, but they must also know the name of the exception (setException) in order to capture it in their own programs.

Example 5-4. The getbreeds.class with exception handling

<?php

class GetBreeds {

   function __construct($properties_array) {

        if (!(method_exists('dog_container', 'create_object')))

           { exit; }

   }

     private $result = "??";

  public function get_select($dog_app)

  {

            if (($dog_app != FALSE) && ( file_exists($dog_app)))

            {

                $breed_file = simplexml:load_file($dog_app);

                $xmlText = $breed_file->asXML();

                $this->result = "<select name='dog_breed' id='dog_breed'>";

                 $this->result = $this->result . "<option value='-1' selected>Select a dog breed</option>";

                 foreach ($breed_file->children() as $name => $value)

                 {

                      $this->result = $this->result . "<option value='$value'>$value</option>";

                 }

                 $this->result = $this->result . "</select>";

                  return $this->result;

            }

            else

            {

                   throw new Exception("Breed xml file missing or corrupt");

            }

    }

}

?>

Comparing the previous GetBreeds class (in Example 4-11) with Example 5-4 shows only one change. It returns 'FALSE' and throws a general exception indicating that the breed.xml file is missing or corrupt. Again, the GetBreeds class pushes any exceptions to the interface. The interface no longer has to determine if there are any exceptions. Even though missing file errors cannot be redirected to be handled as exceptions, the code uses file_exists to throw an exception if the file is missing.

Example 5-5. The dog_container.php file with exception handling

<?php

class dog_container

{

  private $app;

  private $dog_location;

  function __construct($value) {

      if (function_exists('clean_input')) {

          $this->app = $value;

       } else { exit; }

   }

   public function set_app($value) {

      $this->app = $value; }

public function get_dog_application($search_value) {

     $xmlDoc = new DOMDocument();

     if ( file_exists("e5dog_applications.xml") ) {

         $xmlDoc->load( 'e5dog_applications.xml' );

         $searchNode = $xmlDoc->getElementsByTagName( "type" );

         foreach( $searchNode as $searchNode )  {

              $valueID = $searchNode->getAttribute('ID');

              if($valueID == $search_value)   {

                    $xmlLocation = $searchNode->getElementsByTagName( "location" );

                     return $xmlLocation->item(0)->nodeValue;

              break;    }

  } }

    throw new Exception("Dog applications xml file missing or corrupt"); }

  function create_object($properties_array) {

      $dog_loc = $this->get_dog_application($this->app);

      if(($dog_loc == FALSE) || (!file_exists($dog_loc))) {

          throw new Exception("File $dog_loc missing or corrupt.");  }

   else

    {

       require_once($dog_loc);

       $class_array = get_declared_classes();

       $last_position = count($class_array) - 1;

       $class_name = $class_array[$last_position];

       $dog_object = new $class_name($properties_array);

       return $dog_object;

   }

}

}

?>

The dog_container in Example 5-5 replaces returning 'FALSE' from Example 4-10 when the dog_application.xml file, dog.class file, and/or the get_breeds file is missing. Instead, an exception is thrown indicating which file is missing.

Example 5-6. The dog_interface.php file with exception handling

<?php

  function clean_input($value)

{

    $value = htmlentities($value);

    $value = strip_tags($value);

    if (get_magic_quotes_gpc())

     {

          $value = stripslashes($value);

     }

      $value = htmlentities($value);

      $bad_chars = array( "{", "}", "(", ")", ";", ":", "<", ">", "/", "$" );

      $value = str_ireplace($bad_chars,"",$value);

      return $value;

  }

class setException extends Exception {

    public function errorMessage() {

         list($name_error, $breed_error, $color_error, $weight_error) = explode(',', $this->getMessage());

         $name_error   == 'TRUE' ? $eMessage = '' : $eMessage = 'Name update not successful<br/>';

         $breed_error == 'TRUE' ? $eMessage .= '' : $eMessage .= 'Breed update not successful<br/>';

         $color_error == 'TRUE' ? $eMessage .= '' : $eMessage .= 'Color update not successful<br/>';

         $weight_error == 'TRUE' ? $eMessage .= '' : $eMessage .= 'Weight update not successful<br/>';

         return $eMessage;

     }

}

function get_dog_app_properties($lab)

{

print "Your dog's name is " . $lab->get_dog_name() . "<br/>";

print "Your dog weights " . $lab->get_dog_weight() . " lbs. <br />";

print "Your dog's breed is " . $lab->get_dog_breed() . "<br />";

print "Your dog's color is " . $lab->get_dog_color() . "<br />";

}

//----------------Main Section-------------------------------------

try {

        if ( file_exists("e5dog_container.php"))

        {   Require_once("e5dog_container.php"); }

        else

        {    throw new Exception("Dog container file missing or corrupt"); }

                    if (isset($_POST['dog_app'])) {

if ((isset($_POST['dog_name'])) && (isset($_POST['dog_breed'])) && (isset($_POST['dog_color'])) &&  (isset($_POST['dog_weight'])))

        {

            $container = new dog_container(clean_input($_POST['dog_app']));

            $dog_name = clean_input(filter_input(INPUT_POST, "dog_name"));

            $dog_breed = clean_input($_POST['dog_breed']);

            $dog_color = clean_input($_POST['dog_color']);

            $dog_weight = clean_input($_POST['dog_weight']);

            $breedxml = $container->get_dog_application("breeds");

            $properties_array = array($dog_name,$dog_breed,$dog_color,$dog_weight,$breedxml);

            $lab = $container->create_object($properties_array);

            print "Updates successful<br />";

            get_dog_app_properties($lab); }

      else {

             print "<p>Missing or invalid parameters. Please go back to the lab.html page to enter valid information.<br />";

                         print "<a href='dog.html'>Dog Creation Page</a>";

               }

        } else // select box {

                         $container = new dog_container("selectbox");

                         $properties_array = array("selectbox");

                         $lab = $container->create_object($properties_array);

                         $container->set_app("breeds");

                         $dog_app = $container->get_dog_application("breeds");

             $result = $lab->get_select($dog_app);

             print $result;

           }

     } // try

    catch(setException $e)

    {

         echo $e->errorMessage();

    }

   catch(Exception $e)

   {

        echo $e->getMessage();

   }

   catch(Error $e) // PHP 7+ only

   {

        echo $e->getMessage();

   }

?>

When comparing Example 4-12 to Example 5-6, the amount of code needed to handle exceptions is less than using if/else conditional statements. The logical flow of the program is easier to follow with very few else statements. This occurs because the exceptions thrown from all the files in this application are handled by the catch blocks in dog_interface. The user errors are thrown to a special setException exception. The system errors are captured by the Error catch block. The error_check_dog_app method (in Example 4-12) has been replaced by the setException class. The code in the class is very similar to the code in the error_check_dog_app. The display of individual update messages in the $eMessage string is removed, since this class reacts to user errors, not successful updates. A general print line has been added in the main body of the code to let the users know that all updates have been successful. The try block has been added around all the code in this interface. This helps to capture any problems in any part of this application. Notice that an exception is also thrown if the dog_container file cannot be found.

Only three catch blocks are required for this application. The setException catch block calls the errorMessage method from the setException class, which determines what user errors have occurred. The information is then displayed back to the user. The Exception catch block handles all other exceptions. It currently displays this information to the user. However, the Exception and Error catch blocks are currently providing the user too much information. It is a violation of security to inform the user what other problems the application maybe experiencing. You should just tell them that the system is not currently available and ask them to check back later. Displaying detailed errors is okay when you’re testing. However, it’s not good for the real world. You will resolve this breach of security in the next section.

For more information on exception handling, visit

Examples: http://www.w3schools.com/php/php_exception.asp

Video: https://www.thenewboston.com/videos.php?cat=11&video=17171

Do It

1.

Examine the code from this section. Are there any areas in which error checking could have been converted to exception handling? Go to the book’s web site and download the code for this section. Make the potential changes to the existing code to use additional exception handling.

Logging Exceptions

Applications must have an ability to notify systems analysts when problems occur. However, specific messages about errors should not be displayed to the user of the application. The user should be notified that the system is not currently operational. The systems analyst should be notified of the specific problem that has occurred.

The easiest way to provide this ability is to place error messages into a log file. PHP applications can log messages into the default PHP error file or in an application specific file. The php.ini file can be edited (see Chapter 1 for location) to specify the location and name of the default error log file. Once the php.ini file is open in an editor, search for error_log. If a semicolon is located at the beginning of the line, the location has been commented out. Just remove the semicolon and specify a location, such as:

error_log = c:/temp/php_errors.log

-or-

error_log = http://www.asite.com/temp/php_errors.log

When writing to the default error log, PHP will insert a timestamp along with the error message that you submit. Your default time zone may not be set correctly in the php.ini file. Search for date.timezone. The valid time zone settings for the continental United States are:

date.timezone = "America/New_York"

date.timezone = "America/Chicago"

date.timezone = "America/Los_Angeles"

For all other American time zones visit: http://www.php.net/manual/en/timezones.america.php

For worldwide time zones visit: http://php.net/manual/en/timezones.php

The Apache httpd config file (see Chapter 1 for location) can override the settings in the php.ini file. You should also open this file and search for date.timezone. Replace the existing line with a format similar to the following.

php_value date.timezone "America/New_York"

Once you have updated and saved the php.ini and/or apache.httpd files, you must reload your Apache server for the changes to take place (see Chapter 1).

Note

The time zone can also be set with program code. PHP 7 does not support the datefmt_set_timezone_id method. The datefmt_set_timezone method can be used for PHP 5.2+. For more information on setting the time zone with program code, visithttp://php.net/manual/en/datetime.settimezone.php .

<?php

error_log("error message");

?>

Enter this code and save it in a test file. Test it in your environment. If your settings are correct, PHP will create the error log at the location specified in the error_log parameter. Don’t create the file yourself. PHP will not log information to a log file that it did not create. The format of the message sent to your log file should be similar to the following:

[25-Jun-2015 17:01:12 America/New_York] error message

With only the simple one line of code, PHP created the text-based file in the location specified and placed the message in the file.

If you do not have access to these files, you can specify a specific location in the PHP application to send your messages. This ability also allows you to set up multiple application log files. It is common for an application to have informational log files, authentication (login) log files, error log files, and security log files. By separating each type of message, it’s easier to scan for a specific type of message in a log file.

Let’s assume you want to log user errors in one file and other errors in a different file.

<?php

const USER_ERROR_LOG = 'User_Errors.log';

const ERROR_LOG = 'Errors.log';

// sending a user error

error_log("A user error",3,USER_ERROR_LOG);

// sending all other errors

error_log("A general error",3,ERROR_LOG);

<?php

This code will use the constants (USER_ERROR_LOG and ERROR_LOG) to direct the error messages to the correct location. Notice that a second parameter of 3 is used to let the error_log method know that a different location will be used to log the error. A standard format should be used for sending messages to your log(s). The format should include the time/date (if not already included by the environment as mentioned previously), the type of message (if there is more than one message type in the file), the error message, and any other pertinent information. By default, the message size is limited to 120 characters. However this can be changed in the php.ini file.

$date = date('m.d.Y h:i:s');

// For more info on data time format go to: http://php.net/manual/en/function.date.php

$errormessage = "This is the error";

$eMessage =  $date . " | User Error | " . $errormessage . "\n";

error_log($eMessage,3,USER_ERROR_LOG);

The above code would produce

06.06.2015 03:00:55 | User Error | This is the error

A standard text editor (Notepad++ or Notepad) or log-monitoring software (you will create a log reader program later in this chapter) can be used to view the contents of the file.

The system will limit the size of the log file(s). However, assuming that there is not too much logging per day, the application can create logs that are specific for each day.

$USER_ERROR_LOG = "User_Errors" . date('mdy') . ".log";

$ERROR_LOG = "Errors" . date('mdy') . ".log";

...

error_log($eMessage,3,$USER_ERROR_LOG);

Security and performance—The location of the log files should reside in a different folder than the application. The folder will need to allow write access for the application. However, it should be secured from read access or write access outside the server itself. Only authorized personnel should have access to the logs.

Note that the constants (USER_ERROR_LOG and ERROR_LOG) must be changed to variables due to the date method creating a possible variable output (different dates). The format would create a file name similar to User_Errors06062015.log or Errors06062015.log.

PHP also makes it very easy to send an e-mail alert when something has been written to a log file. The webserver must include an e-mail server. Your local machine may not have this capability. However, usually, a web host provider (that has PHP capability) includes an e-mail service. To use this ability, you can add an error_log statement:

error_log("Date/Time: $date - Serious System Problems with Dog Application. Check error log for details", 1, "noone@helpme.com", "Subject: Dog Application Error \nFrom: System Log <systemlog@helpme.com>" . "\r\n");

Security and performance – While it is tempting to inform the associate receiving the e-mail message of the exact problem that has occurred in the application, do not. By default, e-mail is not encrypted. Sending an unencrypted e-mail with detailed information about your application is inviting hackers to corrupt your application. You should, however, provide enough information in the message (such as a date/time stamp and maybe an error number) to help the associate locate the error message(s) in the log file(s).

The first parameter specifies the message of the e-mail. The second parameter informs error_log to e-mail this information. The third parameter provides the “To” e-mail address. The fourth parameter is an extra header field. This field is commonly used to include the subject of the e-mail and the e-mail address that sent the message. The “From” address must be included or the message will not be sent. The “From” address does not, however, need to be an existing address.

For more information on logging errors, visit

Examples: http://php.net/manual/en/function.error-log.php

Examples: http://www.w3schools.com/php/php_error.asp

In the Dog application, you can provide the ability to log exceptions and e-mail major errors by adjusting the catch blocks of the dog_interface (from Example 5-6).

Example 5-7. The dog_inteface.php file with exception logging and e-mail

<?php

const USER_ERROR_LOG = "User_Errors.log";

const ERROR_LOG = "Errors.log";

function clean_input($value)

{

$value = htmlentities($value);

                // Removes any html from the string and turns it into < format

                $value = strip_tags($value);

        if (get_magic_quotes_gpc())

        {

                $value = stripslashes($value);        // Gets rid of unwanted slashes

        }

                $value = htmlentities($value);        // Removes any html from the string and turns it into < format

       $bad_chars = array( "{", "}", "(", ")", ";", ":", "<", ">", "/", "$" );

       $value = str_ireplace($bad_chars,"",$value);

                return $value;

}

class setException extends Exception {

   public function errorMessage() {

         list($name_error, $breed_error, $color_error, $weight_error) = explode(',', $this->getMessage());

        $name_error == 'TRUE' ? $eMessage = '' : $eMessage = 'Name update not successful<br/>';

        $breed_error == 'TRUE' ? $eMessage .= '' : $eMessage .= 'Breed update not successful<br/>';

        $color_error == 'TRUE' ? $eMessage .= '' : $eMessage .= 'Color update not successful<br/>';

        $weight_error == 'TRUE' ? $eMessage .= '' : $eMessage .= 'Weight update not successful<br/>';

      return $eMessage;

           } }

}

function get_dog_app_properties($lab)

{

print "Your dog's name is " . $lab->get_dog_name() . "<br/>";

print "Your dog weights " . $lab->get_dog_weight() . " lbs. <br />";

print "Your dog's breed is " . $lab->get_dog_breed() . "<br />";

print "Your dog's color is " . $lab->get_dog_color() . "<br />";

}

//----------------Main Section-------------------------------------

try {

        if ( file_exists("e5dog_container.php"))

        {

                Require_once("e5dog_container.php");

        }

        else

        {

                throw new Exception("Dog container file missing or corrupt");

        }

        if (isset($_POST['dog_app']))

        {

                if ((isset($_POST['dog_name'])) && (isset($_POST['dog_breed'])) && (isset($_POST['dog_color'])) && (isset($_POST['dog_weight'])))

                {

                        $container = new dog_container(clean_input($_POST['dog_app']));

                        $dog_name = clean_input(filter_input(INPUT_POST, "dog_name"));

                        $dog_breed = clean_input($_POST['dog_breed']);

                        $dog_color = clean_input($_POST['dog_color']);

                        $dog_weight = clean_input($_POST['dog_weight']);

                        $breedxml = $container->get_dog_application("breeds");

                        $properties_array = array($dog_name,$dog_breed,$dog_color,$dog_weight,$breedxml);

                        $lab = $container->create_object($properties_array);

                        print "Updates successful<br />";

                        get_dog_app_properties($lab);

                }

                else

                {

                print "<p>Missing or invalid parameters. Please go back to the dog.html page to enter valid information.<br />";

                print "<a href='dog.html'>Dog Creation Page</a>";

                }

        }

        else // select box

        {

                $container = new dog_container("selectbox");

                $properties_array = array("selectbox");

                $lab = $container->create_object($properties_array);

        $container->set_app("breeds");

        $dog_app = $container->get_dog_application("breeds");

                $result = $lab->get_select($dog_app);

            print $result;

        }

   }

   catch(setException $e)

   {

                echo $e->errorMessage(); // displays to the user

                $date = date('m.d.Y h:i:s');

                $errormessage = $e->errorMessage();

                $eMessage =  $date . " | User Error | " . $errormessage . "\n";

                error_log($eMessage,3,USER_ERROR_LOG); // writes message to user error log file

   }

   catch(Exception $e)

   {

                echo "The system is currently unavailable. Please try again later."; // displays message to the user

                $date = date('m.d.Y h:i:s');

                $errormessage = $e->getMessage();

                $eMessage = $date . " | User Error | " . $errormessage . "\n";

                error_log($eMessage,3,ERROR_LOG); // writes message to error log file

                error_log("Date/Time: $date - Serious System Problems with Dog Application. Check error log for details", 1, "noone@helpme.com", "Subject: Dog Application Error \nFrom: System Log <systemlog@helpme.com>" . "\r\n");

        // e-mails personnel to alert them of a system problem

   }

catch (Error $e)

   {

                echo "The system is currently unavailable. Please try again later."; // displays message to the user

                $date = date('m.d.Y h:i:s');

                $errormessage = $e->getMessage();

                $eMessage = $date . " | Fatal System Error | " . $errormessage . "\n";

                error_log($eMessage,3,ERROR_LOG); // writes message to error log file

                error_log("Date/Time: $date - Serious System Problems with Dog Application. Check error log for details", 1, "noone@helpme.com", "Subject: Dog Application Error \nFrom: System Log <systemlog@helpme.com>" . "\r\n");

        // e-mails personnel to alert them of a system problem

   }

At the top of the Example 5-7 code, the constants USER_ERROR_LOG and ERROR_LOG have been created to pinpoint the name and location of the log files. Locating constants that might be subject to change (such as a tax rate) at the top of the code provides easy access for quick changes by a programmer who is charged with supporting the application. As stated previously, the location of the log file must be in a folder that allows application write access. It is recommended that log files be centrally located in a common folder, with other log files, for easy access by data center personnel (or systems analysts).

The other code changes are located in the catch blocks. The setException catch block returns the error message generated by the setException class to the users. This message lets the users know what properties (Name, Breed, Color, and Weight) were not updated. Errors that caused this exception could have come from the user, or by corruption when the information was transmitted from the client machine to the server. These messages only provides information about the requirements of the properties, which the user already should have known. The catch block also writes a similar message to the user error log. A user error is not an urgent error that needs to be addressed by the analyst. However, tracking trends of user problems can provide an indication of possible changes needed to ensure the user has the best experience possible with the application.

The Exception and Error catch blocks captures all non-user generated exceptions. The messages caused by these exceptions might reveal information that would break the security of the application. Therefore a generic message (such as "The system is currently unavailable. Please try again later.") should be displayed to the user. Detailed information about the exception (error message, file location, coding line that raised the exception) should be placed in the error log for further analysis. Most exceptions caught by these catch blocks will keep the application from running. Therefore, it is important that personnel be informed of the problems occurring. This catch blocks are a good location to send an e-mail to the support personnel to alert them of any problems.

Now that you have built-in exception handling and error handling into the program, you could edit the php.ini file to turn off error reporting to the user. However, you should wait to do this until all development and testing has been completed. Locate the line "display_errors = On" in thephp.ini file. If you change this setting to "display_errors = Off", most error messages will not be displayed to the user. This change will not affect any messages sent back by the program to the user via the echo or print methods (including in any catch blocks). This change will give the developer greater control over the type of messages displayed to the users when there are system problems.

Do It

1.

Download the code for this section. Create or use an existing HTML page that does not check for user input errors. Run the program entering values for the name, breed, weight, and color, which should cause user errors. Stop the program and open the contents of the user error log file. Did the errors appear in the file? If not, check the security of the folder that contains the log file to make sure that it allows write access to the log file. Once you are satisfied that it has caught user errors, try to cause other system errors to occur. Hint: Change the file names to nonexistent names in the dog application XML file. Check the error log to determine if the errors have been written to the file. Were you able to cause any errors that are not captured by one of the log files? If so, is there a way to capture those errors?

Reading Log and Text Files

In the previous section, you discovered that the error_log method writes to a log file using just one line of code. It creates the log file if it does not exist. It appends (adds to the end of the file) any message passed to the contents of the file. It then closes the file. If you were to create your own logging process, it would take several lines of code.

$logFile = fopen("error_log.log", "a");

$eMessage = $e->getMessage();

fwrite($logFile, $eMessage);

fclose($logFile);

The fopen method will also create the file if it does not already exist. The “a” parameter indicates that anything written to the file should be appended. “w” would indicate that any contents in the file would be lost (written over). The fwrite method will then place the string located in the second parameter ($eMessage) into the file indicated by the first parameter ($logFile). $logFile is a pointer that points to the location of the text file. The fclose method closes the text file.

For more information on writing to text files, visit

Examples: visit w3schools at: http://www.w3schools.com/php/php_file_create.asp

Video: visit “The New Boston” at https://www.thenewboston.com/videos.php?cat=11&video=17063

Since a log file is a text-based file, you can use similar logic to create your own application to open a log file and read its contents.

$logFile = fopen("error_log.log", "r");

echo fgets($logFile);

fclose($logFile);

This code will open the log file and read the first line (via a fgets method) in the file and close the file. However, it is likely that there is more than one line in the file. You must be able to loop through and display each line in the file. You can do this using the while loop shown here.

$logFile = fopen("error_log.log", "r");

while(!feof($logFile))

{

          echo fgets($logFile) . "<br>";

}

fclose($logFile);

The while loop will continue to loop as long as the conditional statement is TRUE. Once the statement is FALSE, the code will exit the loop and jump to the next line of code after the end of the loop. In this example the error_log file is open for read only (“r”). The while loop looks at the end of file indicator (feof) of the log file to determine if it has reached the end of the file. If feof returns TRUE, the end of the file has been reached. The loop must continue while you have not reached the end of the file. To cause the conditional statement to produce a TRUE, while there are still records to be read, you must reverse the logic and have feof produce TRUE if there are records and FALSE if there are not records. You can do this by using the ! operator. The ! operator is a NOT operator and it reverses the result. A NOT TRUE is FALSE or a NOT FALSE is TRUE. Thus, !feof operator will now produce TRUE when there are more records and FALSE when there are no more records. The loop in combination with the fgets method will display each record in the file. Once each record is displayed, it will close the file using fclose.

For more information on reading text files,

Visit w3schools for examples: http://www.w3schools.com/php/php_file_open.asp

Visit “The New Boston” for videos: https://www.thenewboston.com/videos.php?cat=11&video=17064

For more information on the while loop:

Visit w3schools for examples: http://www.w3schools.com/php/php_looping.asp

Visit “The New Boston” for videos: https://www.thenewboston.com/videos.php?cat=11&video=17011

The output produced by the previous example is pretty plain.

06.06.2015 03:00:55 | User Error | This is the error

This is not providing you any better viewing than just opening the log file in a text editor. You can use a combination of an HTML table, the explode method, and arrays to produce a much better output. You can place each line from the log file into a two-dimensional array using theexplode method. The two-dimensional array will have rows and columns just like the HTML table.

$dogs = array

  (

  array("Sammy","Lab",18,"Yellow"),

  array("Spot","Mixed",14,"Mixed"),

  array("Princess","Chihuahua",4,"White"),

  array("Max","Boxer",35,"Brown")

  );

Two-dimensional arrays are a collection of rows of information. Each row has common information in each position (column). In the previous example, all dog names are in position 0, dog breeds are in position 1, dog weights are in position 2, and dog colors are in position 3. This associates directly with the positions in a table.

For more information on the multi-dimensional arrays, visit:

examples: http://www.w3schools.com/php/php_arrays_multi.asp

videos: https://www.thenewboston.com/videos.php?cat=11&video=17026

Sammy

Lab

18

Yellow

Spot

Mixed

14

Mixed

Princess

Chihuahua

4

White

Max

Boxer

35

Brown

Each position in the table and the two-dimensional array is referred to by the column and row. In this table, Sammy is in position (0,0). Yellow is in position (0,3). Max is in position (3,0). Brown in in position (3,3). The first position is the column. The second position is the row. In PHP,[] are used to define the position (subscript) for an array.

echo $dogs[0][0] // displays Sammy

echo $dogs[0][3] // displays Yellow

echo $dogs[3][0] // displays Max

echo $dogs[3][3] // displays Brown

You can now adjust the loop to place the log contents in a two-dimensional array. However, you will not know the size of the array. So you can’t use the format previously shown. This might cause developers to get a major migraine if they were not using PHP. PHP, however, allows you to dynamically create the array, just like it allows you to create variables (properties) whenever you need them.

$logFile = fopen("error_log.log", "r");

$row_Count = 0;

while(!feof($logFile))

{

        print_r ($error_Array[$row_Count] = explode(' | ', fgets($logFile)));$row_Count++;

}

fclose($logFile);

In the loop in this example, the explode method breaks the incoming line from the text file via the | character (actually a space, |, and a space). It places each separated string into the $error_Array at the row indicated by the value in $row_Count. The first time through the loop, the first line of the log file is placed in $error_Array[0] (the first row of the array). Because the explode command separated the string, this causes columns to be created for each piece.

If the first line of the file contained:

A general error | stuff | more stuff

then the first row of the array would contain:

$error_Array[0][0] = "A general error"

$error_Array[0][1] = "stuff";

$error_Array[0][2] = "more stuff";

You can verify this by using the print_r command shown in the example. print_r displays the contents of an array in the following format.

Array ( [0] => A general error [1] => stuff [2] => more stuff )

This format verifies that each piece of the string has been placed into the proper position in the array.

$row_count is incremented by 1 before the loop continues. This positions the next line of the file to be placed into the next position in the array ($error_Array[1], if it is the second line of the file). You, of course, don’t want to use print_r to display the results to the users (it’s not very pretty).

However, it is a great tool to help you make sure the program is placing everything properly in the array. You can add code to the loop to build a table.

$logFile = fopen("Errors.log", "r");

$row_Count = 0;

echo "<table>";

while(!feof($logFile))

{        echo "<tr>";

         $error_Array[$row_Count] = explode(' | ', fgets($logFile));

         $displayString = "";

         for($I=0; $I < 3; $I++)

                     {

         echo "<td> " . $error_Array[$row_Count][$I] . " </td> ";

}

            echo "</tr>";

            $row_Count++;

}

echo "</table>";

fclose($logFile);

An echo statement is located just before the while loop to open the HTML table. An additional echo statement (echo "<tr>") exists just inside the while loop to create a row of the table.

For more information on the for loop, visit:

Examples: http://www.w3schools.com/php/php_looping_for.asp

Videos: https://www.thenewboston.com/videos.php?cat=11&video=17013

Also in the while loop, a for loop has been created to loop through each of the columns of the row. Since you know that there are four columns, the for loop is a good choice. The for loop is used when you know exactly how many times to loop. The first parameter (before the ;) of the forloop initializes the counting variable ($I=0). This variable ($I) is used to count each loop. The second parameter ($I < 3) includes the comparison to determine if the logical flow will stay in the loop. If the comparison is TRUE, the loop will continue. If it is FALSE, the logical flow jumps to the first statement after the loop (echo "</tr>"). The third parameter ($I++) can increment or decrement the counting variable. The for loop helps the programmer to remember to initialize the variable, check the condition, and increment the variable by requiring all the information in one code line.

The echo statement in the for loop uses the $row_Count and $I variables to pull the information from each column in the current row. The first time in the loop, $row_Count will be 0. The echo statement will display the contents of $errorArray[0][0]. As the for loop continues, the contents of $errorArray[0][1], $errorArray[0][2], and $errorArray[0][3] will be displayed. Each value is placed into a cell in the table using the <td> and </td> tags. Once the for loop completes, the flow drops below the loop and closes the row (echo </tr>). Then the row_Count variable is incremented. If there are more rows (more records in the file), the while loop will continue the process with the next row, until there are no more records in the file. Once the flow jumps out of the while loop, the table is closed (echo "</table>"). Then the file is closed.

Text (log) files are sequential files. As items are added (appended), they are added to the bottom of the list. You may want to sort the information, listing the most current first. This can be accomplished with just a slight change to the code.

$logFile = fopen("Errors.log", "r");

$row_Count = 0;

while(!feof($logFile))

{

        $error_Array[$row_Count] = explode(' | ', fgets($logFile));

        $row_Count++;

}

$row_Count--;

fclose($logFile);

echo "<table>";

for ($J=$row_Count; $J >= 0; $J--)

{         echo "<tr>";

        $displayString = "";

        for($I=0; $I < 3; $I++)

        {

               echo "<td> " . $error_Array[$J][$I] . " </td> ";

        }

        echo "</tr>";

}

echo "</table>";

The while loop now loads the array with the records and keeps a count of the number of items in the array. After the loop ends and the file has been closed, a for loop works through the array in reverse order to echo out the rows in the table. The counter variable $J begins with the total number of rows in the array ($row_Count). One is subtracted from $row_Count before the loop because it is incremented in the while loop after the last record has been retrieved, which makes the count one too many. $J is then decremented ($J--) for each loop until the value is less than zero. The internal for loop (for($I=0;$I<3;$I++)) has not changed, as it must still loop through each column of the rows to display the information.

By loading the records into an array, you can modify them if needed. Let’s assume that you want to be able to delete a record from the log. As long as you know the row number that is to be deleted you can remove that record from the array. Then you can repopulate the file with the remaining records.

First you will make a slight change to the echo code you have completed to include a link next to the record to be deleted. You then will add a delete method, move the display code to a display method (so it can be called whenever needed), and create a save changes method to update the log file.

Example 5-8. The readerrorlog.php file

<?php

function deleteRecord($recordNumber, &&#x0024;row_Count, &&#x0024;error_Array) {

        for ($J=$recordNumber; $J < $row_Count - 1; $J++) {

                for($I=0; $I < 3; $I++)

                { $error_Array[$J][$I] = $error_Array[$J + 1][$I]; }

}

        Unset($error_Array[$row_Count]);

        $row_Count--;

        }

function saveChanges($row_Count,$error_Array,$log_File) {

        $logFile = fopen($log_File, "w");

        for($I=0; $I < $row_Count; $I++) {

                $writeString = $error_Array[$I][0] . " | " . $error_Array[$I][1] . " | " . $error_Array[$I][2];

                fwrite($logFile, $writeString);

        }

        fclose($logFile);

}

function displayRecords($row_Count, $error_Array) {

echo "<html><head>";

echo "<style> table { border: 2px solid #5c744d;}  </style>";

echo "</head><body><table>";

echo "<caption>Log File: " . ERROR_LOG . "</caption>";

echo "<tr><th></th><th>Date/Time</th><th>Error Type</th><th>Error Message</th></tr><tr>";

          for ($J=$row_Count; $J >= 0; $J--) {

                echo "<td><a href='readlogfilea.php?rn=$J'>Delete</a></td>";

                for($I=0; $I < 3; $I++)  {

                        echo "<td> " . $error_Array[$J][$I] . " </td> ";

                }

        echo "</tr>";

}

echo "</table>";

echo "</body></html>";

} // main section

const ERROR_LOG = "Errors.log";

$logFile = fopen(ERROR_LOG, "r");

$row_Count = 0;

while(!feof($logFile))

{

            $error_Array[$row_Count] = explode(' | ', fgets($logFile));

            $row_Count++;

}

fclose($logFile);

if(isset($_GET['rn']))

{

            deleteRecord($_GET['rn'], $row_Count, $error_Array);

            saveChanges($row_Count,$error_Array,ERROR_LOG);

}

displayRecords($row_Count,$error_Array);

?>

A978-1-4842-1730-6_5_Fig2_HTML.jpg

Figure 5-2.

The readerrorlog.php file with user errors

In Example 5-8, the displayRecords method contains most of the same code previously shown. Extra CSS code has been added to make the display a little more professional. Also, an HTML href link has been included with each record displayed. The link recalls the program, passing the record number that the user wants to delete.

The set of code in the “main section” (the first lines of code that execute) creates a constant ERROR_LOG to define the location and name of the log file. The file is opened and loaded into the array in the same manner as shown previously.

Once the array is loaded, the program checks to see if it has been called by one of the delete links for each record. If a value has been passed via HTTP GET, the program then calls the deleteRecord method. Once the deleteRecord method is complete, the program calls the saveChangesmethod. Whether or not a value has been passed into the program, it executes the last statement, which calls the displayRecords method.

Program note—The header line of the deleteRecords method (function deleteRecord($recordNumber, &&#x0024;row_Count, &&#x0024;error_Array)) uses by reference, instead of by value, to allow $row_Count and $error_Array to be updated in the method. By default, parameters passed into a method cannot be changed by the method (by value). The & indicates that the parameter is passed by reference. This allows the value to be changed. deleteRecords can adjust the count of the number of rows in the array ($row_Count) and the information in the array itself ($error_Array).

By value passes the contents (data) that is contained in the parameter into the method. By reference passes the memory address of the parameter into the method, which allows the method to change the value of the parameter in memory.

The deleteRecords method accepts the record number to be deleted as one of its parameters. Any position in the array above this record number is unchanged. Any position below this record must be shifted up one position. For example, if an array has ten positions (0-9) and the fifth position (4) is to be deleted, then positions 5-9 must now become positions 4-8.

In the following example, position $J+1 is placed into position J for any record after the $recordNumber to be deleted:

A978-1-4842-1730-6_5_Figa_HTML.jpg

The last position in the array is no longer needed. The last position of the array is released (using unset). This will inform the operating system that the space is memory is no longer needed. The operating system will call its garbage collector to free up the memory location. We will discover an easier way to accomplish this task when we discuss associative arrays in a later chapter. Once the array has been reconfigured, the saveChanges method is called to replace the records in the log file. The code shown is very similar to previous examples in this chapter, with one exception. The fopen method uses the parameter “w”. The “w” parameter will erase anything that is already in the file and replace it with what is currently being written to the file. In this example the file will be updated (replaced) with the new set of records that excludes the record that was deleted. The displayRecords method is called anytime the program is called (with or without a record being deleted). This method displays the contents of the log file.

Programming note—Programs that retrieve data that will be used throughout the program usually retrieve the information in the initial stages of the code and place it in a data structure (array, list,or dataset). As the data is updated in the program, the information in the data structure is updated. When the data processing has been completed, the information is then returned to the original location (text file or database). Updating the text file or database is usually completed as one of the final stages of the program. The process of a user logging out of a program would provide an event to indicate that the updated data should be saved.

Do It

1.

Download the code for Example 5-8 from the book’s web site. Add a try catch block to handle any unexpected problems (such as a nonexistent log file). Test and save your changes.

2.

Adjust the Example 5-8 code from either #1 or the book’s web site to allow the users to select which log file to read. The program should allow the users to select either the user log file or the general system log file. Test and save your changes.

Chapter Terms

Errors

Exceptions

try/catch Block

$e->getMessage();

switch Statement

Embedded if Statement

Default Code

extends

Exception Class

Inheritance

Child Class

Parent Class

trigger_error

Hierarchy of Exception Handling

Raising (Throwing) an Exception

Catching an Exception

Attitude of the Code

Push the Errors

Pull the Errors

Log File

Default PHP Error File

error_log

Timestamp

date.timezone

Application Log Files

Constants

E-mail Alerts

display_errors

Text File

Sequential File

fopen

fwrite

Pointer

fclose

fgets

while Loop

feof

! operator

EngineException

explode

Two-Dimensional Array

Row

Column

Subscript

Dynamic Arrays

First Row of an Array

print_f

increment

decrement

for Loop

HTML HREF

unset

Chapter Questions and Projects

Multiple Choice

1.

Which of the folowing is true about PHP program errors?

a.

They are used for all exceptional situations before PHP 5.0.

b.

They are handled by the operating system.

c.

They might be displayed to the users.

d.

All of these.

2.

Which of the folowing is true about PHP program exceptions?

a.

They are used for all exceptional situations after PHP 5.0.

b.

They can be handled by the operating system.

c.

They might be displayed to the users.

d.

All of these.

3.

The try/catch block is used to do which of the following?

a.

Capture all errors.

b.

Capture only known errors.

c.

Capture all exceptions.

d.

Capture only known exceptions.

4.

Inheritance does which of the following?

a.

Is part of object-oriented programming.

b.

Allows the child class to use all the attributes of its parent class.

c.

Allows the child to create its own methods or properties.

d.

All of these.

5.

Text files do which of the following?

a.

Are easy to use in PHP.

b.

Are more secure than using databases.

c.

Are non-sequential.

d.

None of these.

6.

PHP log files do which of the following?

a.

Are created by the error_log method.

b.

Are updated by the error_log method.

c.

Should be located in a different directory than the application.

d.

All of these

7.

Which of the following describes two-dimensional arrays?

a.

They are similar to HTML tables. They contain rows and columns.

b.

They should be avoided at all costs. They are inefficient and difficult to use.

c.

They require all columns to be loaded with data before rows.

d.

None of these.

8.

Which of the following describes the first position of a two-dimensional array?

a.

It has a subscript of 0.

b.

It has a subscript of 1.

c.

It has a default value.

d.

None of these.

9.

Application log files should include which of the folllowing?.

a.

User log files.

b.

Error log files.

c.

Informational log files.

d.

All of these.

10.

E-mails generated because of program exceptions should do what?

a.

Include all error information, including the error descriptions.

b.

Include the code lines that caused the error to occur.

c.

Include the date and time the error occurred.

d.

All of these.

True/False

1.

All exceptions should be displayed to the users.

2.

The for loop should be used when you know exactly how many loops will occur.

3.

The while loop will continue to loop until the conditional statement becomes true.

4.

unset can be used to release a position in an array.

5.

All PHP arrays must be declared before being used.

6.

A pointer points to the location in memory that an object resides.

7.

The ! operator reverses a TRUE result to a FALSE result.

8.

print_f can be used to display the contents of an array.

9.

Try/Catch blocks should reside in the business rules and data tiers but not the interface tier.

10.

Only Exceptions intentionally thrown by program code should be caught.

Short Answer/Essay

1.

Explain how hierarchy of exception handling works with three-tier applications.

2.

What is the difference between an error and an exception?

3.

How do you correct the time zone if PHP is providing an incorrect timestamp?

4.

How can PHP programmers try to capture errors so they can be treated as if they are exceptions?

5.

Why is it important to have multiple log files produced by an application?

Projects

1.

Adjust the code from project #1 (or #2) from Chapter 4 to include exception handling and logging.

2.

Create an application that will register contestants for your favorite game show. Include verification of the data entered using HTML5 and JavaScript. Also validate the data when it is passed to the application. The application should include an interface php program (interface tier) and a registration class (business rules tier). The registration class should include exception handling (both user exceptions and program exceptions). The interface program should handle the exceptions using try/catch blocks as shown in this chapter.

Term Project

1.

Update the ABC Computer Parts Warehouse Inventory application to include exception handling. The application should attempt to handle all exceptions, and errors, when possible.

User exceptions should be logged to a user log. All other exceptions should be logged to a system log. If the exception is considered to be extreme (will cause the program to otherwise crash), an e-mail should be sent to the system administrator. Hint: The Try/Catch block should only exist in the interface tier.