© Steve Prettyman 2020
S. PrettymanLearn PHP 8https://doi.org/10.1007/978-1-4842-6240-5_6

6. Handling and Logging Exceptions

Steve Prettyman1 
(1)
Key West, FL, USA
 

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 the for loop

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

  • Create a PHP program that logs exceptions and emails 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 is 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. Since PHP 7 exception handling is the “rule.” 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. The standard approach uses the try-catch block .
try {
// code that might cause an exception
}
catch(Throwable $t) {
// PHP 7+ format for catching all exceptions. Will not capture pre PHP 7 problems.
}
catch(Error $e) {
// PHP 7+ capture and handle errors. Will not capture errors that cannot recover such as
// server memory errors
}
catch(Exception $e) {
// code that will capture PHP 5+ exceptions. Not included in book examples
}
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(Throwable $t) {
// code that executes if there is an exception
      $t->getMessage();
}
catch(Error $e) {
// PHP 7+ capture and handle errors
      $e->getMessage();
}
This example might produce an exception if $secondNumber contains a zero (dividing by zero). If the exception occurs, the code will jump to the Throwable catch block. Any code in the block will then be executed. The statement $t->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(Throwable $t) {
      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;
      }
}
catch(Throwable $t) {
                        echo $t->getMessage();
}
  • Programming note—In addition to getMessage method , the Exception and Error 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() and getTraceAsString()—Display backtrace (exception flow through the program) information

  • In some circumstances, it might be appropriate to display the Exception or Error 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 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"; }

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.

As stated earlier, it is 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.

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(Throwable $t) {
         Echo $t->getMessage();
}
The zeroException class extends the class Exception. The extends keyword is used to inherit all of the functionality of the Exception class. Inheritance is another key component of object-oriented programming (along with encapsulation and polymorphism). A child class (like zeroException) 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 class that is created and thrown, there must be a catch block to catch the exception. 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.

Since PHP 7, the Error object captures many potential system errors as exceptions.
try {
      call_method(null); // no such method!
}
catch (Error $e)
{
      echo $e->getMessage();
}
Previously, the call to a nonexistent function would cause a fatal error. Using the EngineException object allows the programmer to handle the error. 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. 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 programming 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.

../images/394210_2_En_6_Chapter/394210_2_En_6_Fig1_HTML.jpg
Figure 6-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, and get_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, we can throw exceptions in the dog_container, dog, and get_breeds modules without using catches. In the dog_interface , we 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) passes 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 we will look at later in this chapter), or ignore them (if it does not adversely affect the operation of the application).

Before we change the Dog application code, let us look at an example of exceptions being handled by the hierarchy.
<?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 6-1

testerror.php with error and exception-producing methods

<?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 (Throwable $t) {
                  echo $t->getMessage();
      }
      echo “This line will display”;
?>
Example 6-2

The handleerror.php file captures error or exception

The handleerror program (in Example 6-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 the try 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, the Error class must be used within a catch block.

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

    Go to the book’s web site and download the files for Example 6-1 and 6-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 5. It is not any less efficient to handle errors in this way (it might even be more efficient). However, as we are about to discover, the attitude of the code in the business rules tier (and data tier) changes if we use exception handling. When we 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.
<?php declare(strict_types=1);
class Dog
{
// ------------------------------ Properties ------------------------------
      private int $dog_weight = 0;
      private string $dog_breed = "no breed";
      private string $dog_color = "no color";
      private string $dog_name = "no name";
      private string $error_message = "??";
      private string $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) : bool {
      $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() : string {
            return $this->dog_name; }
      function get_dog_weight() : int {
            return $this->dog_weight; }
      function get_dog_breed() : string {
            return $this->dog_breed; }
      function get_dog_color() : string{
            return $this->dog_color; }
      function get_properties() : string {
            return "$this->dog_name,$this->dog_weight,$this->dog_breed, $this->dog_color."; }
// -----------------------------General Method-----------------------------
      private function validator_breed($value) : bool
      {
            $breed_file = simplexml_load_file($this->breedxml);
            $xmlText = $breed_file->asXML();
            if(stristr($xmlText, $value) === FALSE)
            {
                  return FALSE;
            }
            else
            {
                  return TRUE;
            }
      }
}
?>
Example 6-3

The dog class with exception handling

Comparing Example 5-8 to Example 6-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 5-12) checking for user entry errors by calling the __toString method, the Dog class notifies the dog_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 5, __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.

<?php declare(strict_types=1);
class GetBreeds {
      function __construct($properties_array) {
                  if (!(method_exists('dog_container', 'create_object')))
                  { exit; }
            }
            private string $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");
            }
      }
}
?>
Example 6-4

The getbreeds.class with exception handling

Comparing the previous GetBreeds class (in Example 5-11) with Example 6-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.
<?php declare(strict_types=1);
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;
            }
      }
}
?>
Example 6-5

The dog_container.php file with exception handling

The dog_container in Example 6-5 replaces returning 'FALSE' from Example 5-9 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.
<?php declare(strict_types=1);
      class setException extends Exception {
            public function errorMessage() {
            list($name_error, $breed_error, $color_error, $weight_error) =
                  explode(',', $this->getMessage());
                  $name_error == 'TRUE' ? $eMessage = '' :
                  string $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_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(filter_var($_POST['dog_app'],
                  FILTER_SANITIZE_STRING));
            string $dog_name = filter_var( $_POST['dog_name'],
                  FILTER_SANITIZE_STRING);
            string $dog_breed = filter_var( $_POST['dog_breed'],
                  FILTER_SANITIZE_STRING);
            string $dog_color = filter_var( $_POST['dog_color'],
                  FILTER_SANITIZE_STRING);
            string $dog_weight = filter_var( $_POST['dog_weight'],
                  FILTER_SANITIZE_STRING);
                  $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_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(Throwable $t)
{
      echo $t->getMessage();
}
catch(Error $e)
{
      echo $e->getMessage();
}
?>
Example 6-6

The dog_interface.php file with exception handling

When comparing Example 5-12 to Example 6-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 5-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 Throwable 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 may be 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 are testing. However, it is not good for the real world. You will resolve this breach of security in the next section.

For more information on exception handling, visit www.php.net/manual/en/language.errors.php7.php.

Do It

  1. 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: www.php.net/manual/en/timezones.america.php
For worldwide time zones visit: www.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. The datefmt_set_timezone method can be used for PHP 5.2+. For more information on setting the time zone with program code, visit www.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. Do not 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-2020 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 is easier to scan for a specific type of message in a log file.

Let us assume we 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);
<?
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 . " ";
error_log($eMessage,3,USER_ERROR_LOG);
The preceding code would produce
06.06.2020 03:00:55 | User Error | This is the error

A standard text editor (Notepad++ or Notepad) or log-monitoring software (we 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_Errors06062020.log or Errors06062020.log.

PHP also makes it very easy to send an email alert when something has been written to a log file. The web server must include an email server. Your local machine may not have this capability. However, usually, a web host provider (that has PHP capability) includes an email 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, "[email protected]", "Subject: Dog Application Error From: System Log <[email protected]>" . " ");
  • Security and performance—While it is tempting to inform the associate receiving the email message of the exact problem that has occurred in the application, do not. By default, email is not encrypted. Sending an unencrypted email 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/timestamp 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 email. The second parameter informs error_log to email this information. The third parameter provides the “To” email address. The fourth parameter is an extra header field. This field is commonly used to include the subject of the email and the email 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 www.php.net/manual/en/function.error-log.php.

In the Dog application, we can provide the ability to log exceptions and email major errors by adjusting the catch blocks of the dog_interface (from Example 6-6).
<?php declare(strict_types=1);
const USER_ERROR_LOG = "User_Errors.log";
const ERROR_LOG = "Errors.log";
class setException extends Exception {
      public function errorMessage() : string {
            list($name_error, $breed_error, $color_error, $weight_error) =
            explode(',', $this->getMessage());
            string $name_error == 'TRUE' ? $eMessage = '' : $eMessage =
            'Name update not successful<br/>';
            string $breed_error == 'TRUE' ? $eMessage .= '' : $eMessage .=
            'Breed update not successful<br/>';
            string $color_error == 'TRUE' ? $eMessage .= '' : $eMessage .=
            'Color update not successful<br/>';
            string $weight_error == 'TRUE' ? $eMessage .= '' : $eMessage .=
            'Weight update not successful<br/>';
            string return $eMessage;
      }
}
function get_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(filter_var($_POST['dog_app'],
                  FILTER_SANITIZE_STRING));
            string $dog_name = filter_var( $_POST['dog_name'],
                  FILTER_SANITIZE_STRING);
            string $dog_breed = filter_var( $_POST['dog_breed'],
                  FILTER_SANITIZE_STRING);
            string $dog_color = filter_var( $_POST['dog_color'],
                  FILTER_SANITIZE_STRING);
            string $dog_weight = filter_var( $_POST['dog_weight'],
                  FILTER_SANITIZE_STRING);
            $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_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 . " ";
            error_log($eMessage,3,USER_ERROR_LOG); // writes message to user error log file
      }
      catch(Throwable $t)
      {
            echo "The system is currently unavailable. Please try again later.";
            // displays message to the user
                  $date = date('m.d.Y h:i:s');
                  $errormessage = $t->getMessage();
                  $eMessage = $date . " | User Error | " . $errormessage . " ";
                  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, "[email protected]", "Subject: Dog Application Error From: System Log <[email protected]>" . " ");
            // 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 . " ";
                  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, "[email protected]", "Subject: Dog Application Error From: System Log <[email protected]>" . " ");
            // e-mails personnel to alert them of a system problem
      }
Example 6-7

The dog_interface.php file with exception logging and email

At the top of the Example 6-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 provide 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 Throwable and Error catch blocks capture all nonuser-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. The catch blocks are a good location to send an email to the support personnel to alert them of any problems.

Now that we have built-in exception handling and error handling into the program, we could edit the php.ini file to turn off error reporting to the user. However, we should wait to do this until all development and testing has been completed. Locate the line "display_errors = On" in the php.ini file. If we 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. 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, we 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 w3schools at www.w3schools.com/php/php_file_create.asp.

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. We must be able to loop through and display each line in the file. We 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 we 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, we must reverse the logic and have feof produce TRUE if there are records and FALSE if there are no records. We 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 at www.w3schools.com/php/php_file_open.asp.

For more information on the while loop, visit w3schools at www.w3schools.com/php/php_looping.asp.

The output produced by the previous example is pretty plain.
06.06.2020 03:00:55 | User Error | This is the error
This is not providing us any better viewing than just opening the log file in a text editor. We can use a combination of an HTML table, the explode method, and arrays to produce a much better output. We can place each line from the log file into a two-dimensional array using the explode 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 multidimensional arrays, visit www.w3schools.com/php/php_arrays_multidimensional.asp.

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 is 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
We can now adjust the loop to place the log contents in a two-dimensional array. However, we will not know the size of the array. So, we cannot use the format previously shown. This might cause developers to get a major migraine if they were not using PHP. PHP, however, allows us to dynamically create the array, just like it allows us 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";
We 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). We, of course, do not want to use print_r to display the results to the users (it is not very pretty).

However, it is a great tool to help us make sure the program is placing everything properly in the array. We 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 www.w3schools.com/php/php_looping_for.asp.

Also, in the while loop, a for loop has been created to loop through each of the columns of the row. Since we know that there are four columns, the for loop is a good choice. The for loop is used when we know exactly how many times to loop. The first parameter (before the ;) of the for loop 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, we can modify them if needed. Let us assume that we want to be able to delete a record from the log. As long as we know the row number that is to be deleted, we can remove that record from the array. Then we can repopulate the file with the remaining records.

First, we will make a slight change to the echo code we have completed to include a link next to the record to be deleted. We 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.
<?php
function deleteRecord($recordNumber, &$row_Count, &$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);
?>
Example 6-8

The readerrorlog.php file

../images/394210_2_En_6_Chapter/394210_2_En_6_Fig2_HTML.jpg
Figure 6-2

The readerrorlog.php file with user errors

In Example 6-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 saveChanges method . 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, &$row_Count, &$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 are 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:

../images/394210_2_En_6_Chapter/394210_2_En_6_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 in 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. 1.

    Download the code for Example 6-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. 2.

    Adjust the Example 6-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

Email 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. 1.
    Which of the following is true about PHP program errors?
    1. a.

      They are used for all exceptional situations before PHP 5.0.

       
    2. b.

      They are handled by the operating system.

       
    3. c.

      They might be displayed to the users.

       
    4. d.

      All of these.

       
     
  2. 2.
    Which of the following is true about PHP program exceptions?
    1. a.

      They are used for all exceptional situations after PHP 5.0.

       
    2. b.

      They can be handled by the operating system.

       
    3. c.

      They might be displayed to the users.

       
    4. d.

      All of these.

       
     
  3. 3.
    The try/catch block is used to do which of the following?
    1. a.

      Capture all errors

       
    2. b.

      Capture only known errors

       
    3. c.

      Capture all exceptions

       
    4. d.

      Capture only known exceptions

       
     
  4. 4.
    Inheritance does which of the following?
    1. a.

      Is part of object-oriented programming

       
    2. b.

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

       
    3. c.

      Allows the child to create its own methods or properties

       
    4. d.

      All of these

       
     
  5. 5.
    Text files do which of the following?
    1. a.

      Are easy to use in PHP

       
    2. b.

      Are more secure than using databases

       
    3. c.

      Are nonsequential

       
    4. d.

      None of these

       
     
  6. 6.
    PHP log files do which of the following?
    1. a.

      Are created by the error_log method

       
    2. b.

      Are updated by the error_log method

       
    3. c.

      Should be located in a different directory than the application

       
    4. d.

      All of these

       
     
  7. 7.
    Which of the following describes two-dimensional arrays?
    1. a.

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

       
    2. b.

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

       
    3. c.

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

       
    4. d.

      None of these.

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

      It has a subscript of 0.

       
    2. b.

      It has a subscript of 1.

       
    3. c.

      It has a default value.

       
    4. d.

      None of these.

       
     
  9. 9.
    Application log files should include which of the following?
    1. a.

      User log files

       
    2. b.

      Error log files

       
    3. c.

      Informational log files

       
    4. d.

      All of these

       
     
  10. 10.
    Emails generated because of program exceptions should do what?
    1. a.

      Include all error information, including the error descriptions

       
    2. b.

      Include the code lines that caused the error to occur

       
    3. c.

      Include the date and time the error occurred

       
    4. d.

      All of these

       
     
True/False
  1. 1.

    All exceptions should be displayed to the users.

     
  2. 2.

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

     
  3. 3.

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

     
  4. 4.

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

     
  5. 5.

    All PHP arrays must be declared before being used.

     
  6. 6.

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

     
  7. 7.

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

     
  8. 8.

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

     
  9. 9.

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

     
  10. 10.

    Only Exceptions intentionally thrown by program code should be caught.

     
Short Answer/Essay
  1. 1.

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

     
  2. 2.

    What is the difference between an error and an exception?

     
  3. 3.

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

     
  4. 4.

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

     
  5. 5.

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

     
Projects
  1. 1.

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

     
  2. 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. 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 email should be sent to the system administrator. Hint: The Try/Catch block should only exist in the interface tier.

     
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.196.217