Data and Logic Together: Indicating a Problem with Exceptions in PHP

In Example 6-5, what happens if something other than an array is passed in as the $ingredients argument? As the code is written in Example 6-5, nothing! $this->ingredients is assigned the value of $ingredients no matter what it is. But if it’s not an array, this causes problems when hasIngredient() is called—that method assumes the $ingredients property is an array.

Constructors are great for verifying that supplied arguments are the right type or otherwise appropriate. But they need a way to complain if there is a problem. This is where an exception comes in. An exception is a special object that can be used to indi­cate that something exceptional has happened. Creating an exception interrupts the PHP engine and sets it on a different code path.

Example 6-7 modifies the Entree constructor to throw an exception if the $ingredients argument is not an array. (“Throwing” an exception means you use an exception tell the PHP engine that something went wrong.)

Example 6-7. Throwing an exception

class Entree {

public $name;

public $ingredients = array();

public function  construct($name, $ingredients) {

if (! is_array($ingredients)) {

throw new Exception(‘$ingredients must be an array’);

}

$this->name = $name;

$this->ingredients = $ingredients;

}

public function hasIngredient($ingredient) {

return in_array($ingredient, $this->ingredients);

}

}

Exceptions are represented by the Exception class. The first argument to Exception’s constructor is a string describing what went wrong. So, the line throw new Exception(‘ $ingredients must be an array’); creates a new Exception object and then hands it to the throw construct in order to interrupt the PHP engine.

If $ingredients is an array, then the code runs just as before. If it’s not an array, then the exception is thrown. Example 6-8 shows the creation of an Entree object with a bad $ingredients argument.

Example 6-8. Causing an exception to be thrown

$drink = new Entree(‘Gtass of Milk’, ‘milk’);

if ($drink->hasIngredient(‘milk’)) {

print “Yummy!”;

}

Example 6-8 displays an error message like this (assuming the code is in a file named exception-use.php and the Entree class definition is in a file named construct- exception.php):

PHP Fatal error:       Uncaught Exception: $ingredients must be an array

in construct-exception.php:9 Stack trace:

#0 exception-use.php(2): Entree->           construct(‘Glass of Milk’, ‘milk’)

#1 {main}

thrown in construct-exception.php on line 9

In that error output, there are two separate things to recognize. The first is the error message from the PHP engine: PHP Fatal error: Uncaught exception ‘Exception’ with message ‘$ingredients must be an array’ in construct- exception.php:^ This means that in line 9 of construct-exception.php (the file defin­ing the Entree class), an exception was thrown. Because there was no additional code to deal with that exception (we’ll see how to do that shortly), it’s called “uncaught” and causes the PHP engine to come to a screaming halt—a “fatal” error that stops program execution immediately.

The second thing in that error output is a stack trace: a list of all the functions that were active when the PHP engine stopped. Here there’s just one: the Entree construc­tor that got called from new Entree. The {main} line in the stack trace represents the first level of program execution before anything else runs. You’ll always see that at the bottom of any stack trace.

It’s good that we prevented hasIngredient() from getting called so it doesn’t operate on a non-array of ingredients, but completely stopping the program with such a harsh error message is overkill. The flip side of throwing exceptions is catching them —grabbing the exception before the PHP engine gets it and bails out.

Indicating a Problem with Exceptions |   109

To handle an exception yourself, do two things:

  1. Put the code that might throw an exception inside a try block.
  2. Put a catch block after the potentially exception-throwing code in order to han­dle the problem.

Example 6-9 adds try and catch blocks to deal with the exception.

Example 6-9. Handling an exception

try {

$drink = new Entree(‘Gtass of Milk’, ‘milk’);

if ($drink->hasIngredient(‘milk’)) {

print “Yummy!”;

}

} catch (Exception $e) {

print “Couldn’t create the drink: ” . $e->getMessage();

}

In Example 6-9, the try and catch blocks work together. Each of the statements inside the try block is run, stopping if an exception is encountered. If that happens, the PHP engine jumps down to the catch block, setting the variable $e to hold the Exception object that was created. The code inside the catch block uses the Exception class’s getMessageQ method to retrieve the text of the message given to the exception when it was created. Example 6-9 prints:

Couldn’t create the drink: $ingredients must be an array

Source: Sklar David (2016), Learning PHP: A Gentle Introduction to the Web’s Most Popular Language, O’Reilly Media; 1st edition.

Leave a Reply

Your email address will not be published. Required fields are marked *