Exception Handling in PHP – Free PHP Tutorials

Exception handling is one of the important concepts in a programming world. There is always a possibility for errors that may cause the program execution to halt abruptly. It does not matter how easy and intuitive your application is designed to be, there will be bad usage from the user or just random errors of connectivity, and your code has to be ready to handle these scenarios so that the user experience is a good as possible. We call these scenarios exceptions: an element of the language that identifies a case that is not as we expected.


The try…catch block in PHP

Your code can throw exceptions manually whenever you think it necessary. For example, take the setId method from the Unique trait. Thanks to type hinting, we are enforcing the ID to be a numeric one, but that is as far as it goes. What would happen if someone tries to set an ID that is a negative number? The code right now allows it to go through, but depending on your preferences, you would like to avoid it. That would be a good place for an exception to happen. Let’s see how we would add this check and consequent exception:

public function setId($id) {
 if ($id < 0) {
 throw new \Exception('Id cannot be negative.');
 }
 if (empty($id)) {
 $this->id = ++self::$lastId;
 } else {
 $this->id = $id;
 if ($id > self::$lastId) {
 self::$lastId = $id;
 }
 }
}

As you can see, exceptions are objects of the class exception. Remember adding the backslash to the name of the class, unless you want to include it with use Exception; at the top of the file. The constructor of the Exception class takes some optional arguments, the first one of them being the message of the exception. Instances of the class Exception do nothing by themselves; they have to be thrown  in order to be noticed by the program. Let’s try forcing our program to throw this exception. In order to do that, let’s try to create a customer with a negative ID. In your init.php file, add the following:

$basic = new Basic(-1, "name", "surname", "email");

If you try it now in your browser, PHP will throw a fatal error saying that there was an uncaught exception, which is the expected behavior. For PHP, an exception is something from what it cannot recover, so it will stop execution. That is far from ideal, as you would like to just display an error message to the user, and let them try again.You can—and should—capture exceptions using the try…catch blocks. You insert the code that might throw an exception in the try block and if an exception happens, PHP will jump to the catch block. Let’s see how it works:

public function setId(int $id) {
 try {
 if ($id < 0) {
 throw new Exception('Id cannot be negative.');
 }
 if (empty($id)) {
 $this->id = ++self::$lastId;
 } else {
 $this->id = $id;
 if ($id > self::$lastId) {
 self::$lastId = $id;
 }
 }
 } catch (Exception $e) {
 echo $e->getMessage();
 }
}

If we test the last code snippet in our browser, we will see the message printed from the catch block. Calling the getMessage method on an exception instance will give us the message—the first argument when creating the object. But remember that the argument of the constructor is optional; so, do not rely on the message of the exception too much if you are not sure how it is generated, as it might be empty. Note that after the exception is thrown, nothing else inside the try block is executed; PHP goes straight to the catch block. Additionally, the block gets an argument,
which is the exception thrown.

Naming the argument as $e is a widely used convention, even though it is not a good practice to use poor descriptive names for variables. Being a bit critical, so far, there is not any real advantage to be seen in using exceptions in this example. A simple if…else block would do exactly the same job,
right? But the real power of exceptions lies in the ability to be propagated across methods. That is, the exception thrown on the setId method, if not captured, will be propagated to wherever the method was invoked, allowing us to capture it there. This is very useful, as different places in the code might want to handle the exception in a different way. To see how this is done, let’s remove the try…catch inserted in setId, and place the following piece of code in your init.php file, instead:

try {
 $basic = new Basic(-1, "name", "surname", "email");
} catch (Exception $e) {
echo 'Something happened when creating the basic customer: '
 . $e->getMessage();
}

The preceding example shows how useful it is to catch propagated exceptions: we can be more specific of what happens, as we know what the user was trying to do when the exception was thrown. In this case, we know that we were trying to create the customer, but this exception might have been thrown when trying to update the ID of an existing customer, which would need a different error message.


The finally block in PHP

There is a third block that you can use when dealing with exceptions: the finally block. This block is added after the try…catch one, and it is optional. In fact, the catch block is optional too; the restriction is that a try must be followed by at least one of them. So you could have these three scenarios:

// scenario 1: the whole try-catch-finally
try {
// code that might throw an exception
} catch (Exception $e) {
// code that deals with the exception
} finally {
// finally block
}
// scenario 2: try-finally without catch
try {
// code that might throw an exception
} finally {
// finally block
}
// scenario 3: try-catch without finally
try {
// code that might throw an exception
} catch (Exception $e) {
// code that deals with the exception
}

The code inside the finally block is executed when either the try or the catch blocks are executed completely. So, if we have a scenario where there is no exception, after all the code inside the try block is executed, PHP will execute the code inside finally. On the other hand, if there is an exception thrown inside the try block, PHP will jump to the catch block, and after executing everything there, it will execute the finally block too.

The finally block is very useful when dealing with expensive resources like database connections.Depending on the type of connection, you will have to close it after use for allowing
other users to connect. The finally block is used for closing those connections, regardless of whether the function throws an exception or not.


Catching different types of exceptions

Exceptions have already been proven useful, but there is still one important feature to show: catching different types of exceptions. As you already know, exceptions are instances of the class Exception, and as with any other class, they can be extended. The main goal of extending from this class is to create different types of exceptions, but we will not add any logic inside—even though you can, of course. Let’s create a class that extends from Exception, and which identifies exceptions related to invalid IDs. Put this code inside the src/Exceptions/InvalidIdException.php file:

<?php
namespace Bookstore\Exceptions;
use Exception;
class InvalidIdException extends Exception {
 public function __construct($message = null) {
 $message = $message ?: 'Invalid id provided.';
 parent::__construct($message);
 }
}

The InvalidIdException class extends from the class Exception, and so it can be thrown as one. The constructor of the class takes an optional argument, $message. The following two lines inside it contain interesting code:

• The ?: operator is a shorter version of a conditional, and works like this: the expression on the left is returned if it does not evaluate to false, otherwise, the expression on the right will be returned. What we want here is to use the message given by the user, or a default one in case the user does not provide any. For more information and usages, you can visit the PHP documentation at http://php.net/manual/en/language.operators.comparison.php.

• parent::__construct will invoke the parent’s constructor, that is, the constructor of the class Exception. As you already know, this constructor gets the message of the exception as the first argument. You could argue that, as we are extending from the Exception class, we do not really need to call any functions, as we can edit the properties of the class straightaway. The reason for avoiding this is to let the parent class manage its own properties. Imagine that, for some reason, in a future version of PHP, Exception changes the name of the property for the message. If you modify it directly, you will have to change that in your code, but if you use the constructor, you have nothing to fear. Internal implementations are more likely to change than external interfaces.

We can use this new exception instead of the generic one. Replace it in your Unique trait as follows: throw new InvalidIdException(‘Id cannot be a negative number.’);

Leave a Comment

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