PHP Pandas: Exceptions

← Back to Index

Exceptions let us know that our programs haven't worked as expected. If something goes wrong, and our code can't continue to work as expected, then we use an exception to let the user know.

Throwing

Let's create a function. We've been using lots of classes recently, let's make sure you still know how to use a function. It's best to make sure, isn't it?

Here we go...

Oh dear, I've forgotten how to create a function. Do you mind reminding me?

Sure thing Dayle, not a problem!

<?php

/**
 * Feed the panda some food.
 *
 * @param  string $food
 * @return void
 */
function feedPanda($food)
{
    echo "The panda eats the {$food}.";
}

Good job! Now I remember. Here we've got a function that lets us feed a panda, right? Let's feed it some apple. They like apple.

<?php

feedPanda('apple');

Let's run the code.

The panda eats the apple.

Awesome! What about chocolate? Do you think the panda would like some chocolate? No? Well that's probably because chocolate is poisonous to pandas. We definitely don't want any chocolate being fed to them.

Hey, I've got an idea. Why don't we throw an exception if someone tries to feed the panda some chocolate? Let's try it out.

<?php

/**
 * Feed the panda some food.
 *
 * @param  string $food
 * @return void
 */
function feedPanda($food)
{
    if ($food === 'chocolate') {
        throw new Exception('NO CHOCOLATE FOR THE PANDA!');
    }

    echo "The panda eats the {$food}.";
}

First, we check to see whether the food we're trying to feed the panda is equal to the string value 'chocolate'. If the string matches, we'll need to use an exception. Let's take a look at that piece of code in isolation.

<?php

throw new Exception('NO CHOCOLATE FOR THE PANDA!');

Exceptions are classes like any other; they need to be instantiated with the new keyword. The first parameter to constructor of the Exception class, is the message that will be associated with the exception.

Exceptions are thrown, like a tennis ball, or a party. The throw keyword is used to trigger an exception. Once this exception has been thrown, then the program will be halted, and the user will be alerted to the error. Let's give it a go, shall we?

<?php

feedPanda('chocolate');

It's ok. I promise that the panda won't be harmed. Let's execute the code.

PHP Fatal error:  Uncaught exception 'Exception' with message 'NO CHOCOLATE FOR THE PANDA!' in <file>:<line>

Excellent, our panda is safe.

Since exceptions are classes like any other, we are able to extend them. We can do this to make the nature of the problem that little bit clearer. Let's go ahead and extend the Exception class.

<?php

class PandaException extends Exception
{
    protected $message = 'NO CHOCOLATE FOR THE PANDA!';
}

You'll notice that in addition to the extension of the Exception class, we've also defined a protected property. Do you remember how protected works? If not, now is a great time to check the scope chapter for a refresh.

You see, the Exception class has a property called $message. This normally gets set to the parameter that we give it within the constructor. Since the property is protected, we are able to override its value within our own class. This means that we no longer have to pass the message when defining our extension.

Let's go ahead and throw our PandaException instead.

<?php

/**
 * Feed the panda some food.
 *
 * @param  string $food
 * @return void
 */
function feedPanda($food)
{
    if ($food === 'chocolate') {
        throw new PandaException;
    }

    echo "The panda eats the {$food}.";
}

We're throwing our PandaException without any parameters. Let's see what happens when we pass the function some chocolate.

PHP Fatal error:  Uncaught exception 'PandaException' with message 'NO CHOCOLATE FOR THE PANDA!' in <file>:<line>

Great! Our exception is now more descriptive, and still has the message that we desired. Let's go one step further and expand our single exception into two.

<?php

class PandaChocolateException extends Exception
{
    protected $message = 'NO CHOCOLATE FOR THE PANDA!';
}

class PandaChilliException extends Exception
{
    protected $message = 'NO CHILLI FOR THE PANDA. OUCH!';
}

Now we have two exceptions, PandaChocolateException and PandaChilliException, for the two kinds of food that the panda's delicate digestive tract would struggle with.

There's no limit to the number of exceptions that we can throw, so let's go ahead and modify our code.

<?php

/**
 * Feed the panda some food.
 *
 * @param  string $food
 * @return void
 */
function feedPanda($food)
{
    // Switch on food.
    switch ($food) {

        // Handle chocolate.
        case 'chocolate':
            throw new PandaChocolateException;

        // Handle chillies.
        case 'chilli':
            throw new PandaChilliException;

        // Handle other food.
        default:
            echo "The panda eats the {$food}.";
    }
}

Here, we've used a switch to deal with the different types of food. Why a switch? Well it's a great excuse to make sure you've learned how they work.

In the switches section, we discovered that the break keyword should be used to halt the execution of the application. Hold on, we've not used the break keyword in the switch statement above. Why do you think that is?

Well you see, exceptions also halt the thread of execution, so we don't need the break statements. Let's try and trigger the PandaChilliException, shall we?

Fatal error: Uncaught exception 'PandaChilliException' with message 'NO CHILLI FOR THE PANDA. OUCH!' in <file> on line <line>

Awesome! Well it looks like we're now masters of throwing exceptions. The trouble is, how is this better than just using a PHP function such as exit() or die() to halt execution instead? What do we have to gain? Well, we're not quite done yet!

The exit() or die() functions can be used to terminate the PHP application. If you pass a string as a parameter, it will display the string before exiting. Pretty useful!

Try & Catch

Now that we've learned to throw exceptions, it's time to learn how to catch them. Go ahead and put on your baseball mitt.

If our code throws exceptions, then we can use try and catch to handle them appropriately. This way, our code can recover, and reach it's true end.

Let's consider the final code snippet from the previous section.

<?php

/**
 * Feed the panda some food.
 *
 * @param  string $food
 * @return void
 */
function feedPanda($food)
{
    // Switch on food.
    switch ($food) {

        // Handle chocolate.
        case 'chocolate':
            throw new PandaChocolateException;

        // Handle chillies.
        case 'chilli':
            throw new PandaChilliException;

        // Handle other food.
        default:
            echo "The panda eats the {$food}.";
    }
}

Let's start small. Let's handle the sithation where the panda is fed some chocolate. Here's how we might achieve this.

<?php

// Set the type of food.
$food = 'chocolate';

try {
    feedPanda($food);
} catch (PandaChocolateException $exception) {
    echo 'That was a close one! No chocolate for the panda.';
}

There's some new syntax here, isn't there? Let's pull it out of the code for clarity.

<?php

try {

    // Code that might trigger the exception.

} catch (Exception $exception) {

    // Code to handle the exception.

}

What we have here, is two connected code blocks. They can be broken down into two purposes.

The try block wraps around the code that might trigger an exception. Any exceptions that are thrown by the code executed within this block, are handed to the catch section.

The catch block traps the exceptions that are throwing in the try. Instead of halting the execution of the code, you can handle them here, allowing for a chance at recovery. For example, in the code snippet above, we're trapping any exceptions of type Exception. Note that this will also trap exceptions which inherit from the Exception class, so that's all of them!

Whatever exception class you type-hint in the catch block, will become trapped when it is thrown within the try. The instance that is thrown, will be passed as a parameter. This allows you to access properties such as the message, or error code. Or if you have decided that it couldn't be handled, you could throw it once more.

Now then, let's take a look at our original code snippet once more.

<?php

// Set the type of food.
$food = 'chocolate';

try {
    feedPanda($food);
} catch (PandaChocolateException $exception) {
    echo 'That was a close one! No chocolate for the panda.';
}

We've wrapped our feedPanda() function within the try catch, because we know that's the code that might throw an exception.

In the catch section we've type hinted the PandaChocolateException, so when this gets thrown, it will be caught within the second code block. Instead of letting the exception trigger an error, we've decided to echo a useful message to our users.

Let's go ahead and execute the code.

That was a close one! No chocolate for the panda.

Perfect! We've prevented the panda from being ill. That's one of our exceptions though, shouldn't we try to catch both of them? After all, what use is only catching one exception? Let's give this a go, shall we?

<?php

// Set the type of food.
$food = 'chilli';

try {
    feedPanda($food);
} catch (PandaChocolateException $exception) {
    echo 'That was a close one! No chocolate for the panda.';
} catch (PandaChilliException $exception) {
    echo 'That was a close one! No chilli for the panda.';
}

To catch additional exceptions, we can simply chain additional catch blocks onto our try-catch block. We type-hint a different exception within each block, so that we can deal with our exceptions in different ways.

Let's execute the code once more.

That was a close one! No chilli for the panda.

Great. There's one final... heh... section to the try-catch block. It's optional, but let's take a look anyway.

Finally

The finally block allows for some code to be executed after the try and catch. If the code is terminated within the catch then it will never reach the finally. Let's take a look at the structure.

<?php

try {

    // Code that might trigger the exception.

} catch (Exception $exception) {

    // Code to handle the exception.

} finally {

    // Code to run after the try and catch.

}

Now we can examine this construct as a three stage process.

  1. try to execute a piece of code.
  2. Attempt to catch any problems that might arise.
  3. finally execute some additional code.

Let's look at a practical example for this construct.

<?php

try {

    $database->connect('username', 'password');

} catch (DatabaseException $databaseException) {

    echo 'Unable to connect to the database.';
    die();

} finally {

    $database->fetchRecords();

}

We wrap the code for connecting to the database within a try block. We can't be sure that the username and password is correct, and if this is not the case, then an exception is thrown.

To prevent this exception from halting the code in an ugly manner, we catch the exception, and exit the application with a useful error message.

If the connection to the database is successful, we can use it to retrieve the record that we want to use, and our application can continue as normal.

Use exceptions in your applications to handle errors in an elegant manner, and use try blocks to ensure that these exceptions are dealt with in an appropriate manner. I promise that this will make you a better programmer.

My books are available online for free to encourage learning. However, if you'd like for me to keep writing, then please consider buying a digital copy over at Leanpub.com.

It's available in PDF, ePub, and Kindle format, and contains a bunch of extras that you won't find on the site. I have a full-time job, and I write my books in my spare time. Please consider buying a copy so that I can continue to write new books from the comfort of my sofa!