64x64 icon dark hosting
Choose a hosting plan here and get a free year's subscription to Tuts+ (worth $180).
Advertisement

PHP Exceptions

by
Gift

Start a hosting plan from $3.92/mo and get a free year on Tuts+ (normally $180)

In this article we are going to learn about PHP Exceptions from ground up. These concepts are utilized in many large, scalable and object oriented applications and frameworks. Take advantage of this language feature to improve your skills as a web application developer.


1 An Example First

Before we begin with all the explanations, I would like to show an example first.

Let's say you want to calculate the area of a circle, by the given radius. This function will do that:

It is very simple, however it does not check if the radius is a valid number. Now we are going to do that, and throw an exception if the radius is a negative number:

Let's see what happens when we call it with a negative number:

The script crashes with the following message:

Since it was a fatal error, no more code execution happened after that. However you may not always want your scripts to stop whenever an Exception happens. Luckily, you can 'catch' them and handle them.

This time, let's do it an array of radius values:

Now we get this output:

There are no more errors, and the script continues to run. That is how you catch exceptions.


Full Screencast



2 What is an Exception?

Exceptions have been around in other object oriented programming languages for quite some time. It was first adopted in PHP with version 5.

By definition an Exception is 'thrown', when an exceptional event happens. This could be as simple as a 'division by zero', or any other kind of invalid situation.

This may sound similar to other basic errors that you have already seen many times. But Exceptions have a different kind of mechanism.

Exceptions are actually objects and you have the option to 'catch' them and execute certain code. This is done by using 'try-catch' blocks:

We can enclose any code within a 'try' block. The following 'catch' block is used for catching any exception that might have been thrown from within the try block. The catch block never gets executed if there were no exceptions. Also, once an exception happens, the script immediately jumps to the catch block, without executing any further code.

Further in the article we will have more examples that should demonstrate the power and flexibility of using exceptions instead of simple error messages.


3 Exceptions Bubble Up

When an exception is thrown from a function or a class method, it goes to whoever called that function or method. And it keeps on doing this until it reaches the top of the stack OR is caught. If it does reach the top of the stack and is never called, you will get a fatal error.

For example, here we have a function that throws an exception. We call that function from a second function. And finally we call the second function from the main code, to demonstrate this bubbling effect:

So, when we call foo(), we attempt to catch any possible exceptions. Even though foo() does not throw one, but bar() does, it still bubbles up and gets caught at the top, so we get an output saying: "Caught exception: Message from bar()."


4 Tracing Exceptions

Since exceptions do bubble up, they can come from anywhere. To make our job easier, the Exception class has methods that allows us to track down the source of each exception.

Let's see an example that involves multiple files and multiple classes.

First, we have a User class, and we save it as user.php:

It uses another class named Validator, which we put in validator.php:

From our main code, we are going to create a new User object, set the name and email values. Once we call the save() method, it will utilize the Validator class for checking the email format, which might return an exception:

However, we would like to catch the exception, so there is no fatal error message. And this time we are going to look into the detailed information about this exception:

The code above produces this output:

So, without looking at a single line of code, we can tell where the exception came from. We can see the file name, line number, exceptions message and more. The trace data even shows the exact lines of code that got executed.

The structure of the default Exception class is shown in the PHP manual, where you can see all the methods and data it comes with:



5 Extending Exceptions

Since this is an object oriented concept and Exception is a class, we can actually extend it to create our own custom exceptions.

For example you may not want to display all the details of an exception to the user. Instead, you can display a user friendly message, and log the error message internally:

We just created two new types of exceptions. And they may have custom methods.

When we catch the exception, we can display a fixed message, and call the custom methods internally:

This is the first time we are looking at an example with multiple catch blocks for a single try block. This is how you can catch different kinds of Exceptions, so you can handle them differently.

In this case we will catch a DatabaseException and only that catch block will get executed. In this blog we may call our new custom methods, and display a simple message to the user.

Please note that the catch block with the default Exception class must come last, as our new child classes are also still considered that class. For example 'DatabaseException' is also considered 'Exception' so it can get caught there if the order is the other way around.


6 Handling Uncaught Exceptions

You may not always want to look for exceptions in all of your code, by wrapping everything in try-catch blocks. However, uncaught exceptions display a detailed error message to the user, which is also not ideal in a production environment.

There is actually a way to centralize the handling of all uncaught exceptions so you can control the output from a single location.

For this, we are going to be utilizing the set_exception_handler() function:

The first line instructs PHP to call a given function when an exception happens and is not caught. This is the output:

As you can see the script aborted after the first exception and did not execute the second one. This is the expected behavior of uncaught exceptions.

If you want your script to continue running after an exception, you would have to use a try-catch block instead.


7 Building a MySQL Exception Class

We are going to finish off this tutorial by building a custom MySQL Exception class that has some useful features, and see how we can use it.

You may notice that we put pretty much all of the code in the contructor. Whenever an exception is thrown, it is like creating a new object, which is why the constructor is always called first. At the end of the constructor we also make sure to call the parent constructor.

This exception will be thrown whenever we encounter a MySQL error. It will then fetch the error number, and the message directly from mysql, and then store that information in a log file, along with the timestamp. In our code we can catch this exception, display a simple message to the user, and let the exception class handle the logging for us.

For example, let's try to connect to MySQL without providing any user/password information:

We need to prepend the error suppression operator (@) before the mysql_connect() call so that it does not display the error to the user. If the function fails, we throw an exception, and then catch it. Only our user friendly message will be show to the browser.

The MysqlException class takes care of the error logging automatically. When you open the log file, you will find this line:

Let's add more code to our example, and also provide a correct login info:

If the database connection succeeds, but the database named 'my_db' is missing, you will find this in the logs:

If the database is there, but the query fails, due to a syntax error for example, you may see this in the log:


Bonus Building a DB Class with PHP Magic

We can make the code example above even cleaner by writing our own database class, which handles the throwing of the exceptions. This time I am going to be using some 'magic' features of PHP to construct this class.

At the end, we want our main code to look like this:

It is nice and clean. We do not check for any errors in every single database call. This new Database class has the responsibility to throw exceptions when errors happen. And since these exceptions do bubble up, they get caught by our catch block at the end.

And here is the magic Database class:

It only has one method, and it gets invoked whenever we call a static method on this class. You may read more on this concept here: PHP Overloading.

So, when we call Database::connect(), this code automatically calls mysql_connect(), passes the arguments, checks for errors, throws exceptions when needed, and returns the returned value from the function call. It basically acts like a middle man, and handles the dirty work.


Conclusion

I hope you enjoyed this tutorial and learned from it. Now you should have a better grasp of this subject. Try and see if you can utilize PHP Exceptions in your next project. See you next time!

Advertisement