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()
ordie()
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.
try
to execute a piece of code.- Attempt to
catch
any problems that might arise. 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!