A Crash Course of Changes to Exception Handling in PHP 7

Ahmed Khan
Ahmed Khan
Share

This article was peer reviewed by Thomas Punt, Niklas Keller, and Younes Rafie. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

Exception handling saves your code in the most unusual circumstances. PHP 7 has introduced two new classes that assist a developer in handling errors with ease, and that’s what we’ll look at in this post. Before the introduction of these classes in PHP 7, exception error classes were written to handle the different types of errors.

Illustration of female leg stepping on rake with the word Oops in big letters, indicating pending accident

Throwable Class

Throwable is the interface from which Exception and Error classes branch out. This particular class helps you catch any throwable errors, irrespective of whether they are an exception or an error. For example:

<?php
try {
    throw new Exception("This is an exception");
}
catch (Throwable $e) {
    echo $e->getMessage();
}

Or the newly defined ParseError:

<?php
try {
    $result = eval("2*'7'");
}
catch (Throwable $e) {
    echo $e->getMessage();
}

After executing this code, you will get a ParseError because “;” is missing inside eval().

User defined classes cannot implement Throwable directly, and must instead extend Exception which implements Throwable.

Error Class

The Error class in PHP 7 is a new type of class that handles the different errors – they are either fatal errors or type errors, and this class is only for internal PHP errors. Error is divided into four subclasses:

  1. ArithmeticError
  2. TypeError
  3. ParseError
  4. AssertionError

A thing to keep in mind before upgrading to PHP 7 is that if you have defined a custom Error class, you have to make sure you changed the name before upgrading. If you don’t, you will get a fatal error.

Let’s discuss the above four classes one by one.

ArithmeticError

This error shows up when performing mathematical operations. For example, when you are using intdiv():

<?php

try {
    var_dump(intdiv(PHP_INT_MIN, -1));
}
catch (ArithmeticError $e) {
    echo $e->getMessage();
}

You will get “Division of PHP_INT_MIN by -1” because we have shifted it a bit by a negative amount.

Another class, DivisionByZeroError, also extends from ArithmeticError. This error is thrown under two different conditions:

Note: You will get -1 only in combination with PHP_INT_MIN.

First, if you do a mod of a number by 0:

<?php
try {
    $result = 5 % 0;
    echo $result;
}
catch (DivisionByZeroError $e) {
    echo $e->getMessage();
}

If you use the same method as above and change the % to /, you will get a warning instead and the result can be any one of these: +INF, -INF, or NAN. If you want this to result in an exception, better use an error handler that transforms the warning into a thrown exception.

However, you will have the DivisionByZeroError exception if you execute the following code:

<?php
try {
    $result = is_finite(1.0 / 0);
    if (in_array($result, [INF, NAN,-INF])) {
        throw new DivisionByZeroError('Division by zero error');
    }
}
catch (DivisionByZeroError $e) {
    echo $e->getMessage();
}

The other method that will get you the DivisionByZeroError is by using intdiv() again.

Note: A bug report for this issue has been reported on PHP.net.

TypeError

This error is mostly used with the Scalar Type declarations in PHP 7. The error will be shown when you have created a function or variable of a specific data type and you are trying to save a value of a different data type. For example:

<?php
declare (strict_types = 1);
function add(int $a, int $b)
{
    return $a + $b;
}
try {
    echo add("3", "4");
}
catch (TypeError $e) {
    echo $e->getMessage();
}

If you run the above code, a TypeError will be thrown and you will get must be of the type integer, string was given error. If you run the above code without declare(strict_types=1); you won’t get any problems and the result will be 7, unless you change the number to a non-numerical string.

ParseError

This error is thrown when using eval() to insert a new line of code or using an external PHP file which contains a syntax error. Before ParseError, when you had a syntax error in your external PHP file or in eval(), your code was broken and a fatal error was shown. For example, let’s assume we have a PHP file with the following code:

<?php
$a = 4
$result = $a * 5;

And, we are calling it in another PHP file:

<?php
try {
    require "index3.php";
}
catch (ParseError $e) {
    echo $e->getMessage();
}

When this code is executed, syntax error, unexpected end of file is shown instead of a fatal error. Before this class was introduced, it was almost impossible to handle syntax and fatal errors with ease.

AssertionError

Before the introduction of the AssertionError class, we had to create our own functions to handle assertion exceptions when binding a custom function using assert_options(). This error will only be shown when an assertion made via assert() fails. To work with it, you first need to configure the assert directives in PHP.ini:

  1. Assert.exception: By default its value is 0 and it only generates a warning for the object rather than showing an error. However, when the value is changed to 1, then it will throw an exception or an Assertion Error which can be caught.

  2. Zend.assertions: By default, it’s value is -1 which is for production mode, i.e., the assertion code will not be generated. When it is set to 1 it will be in development mode in which assertion code will be generated and executed. When it is set to 0, assertion code will be generated but won’t be executed during runtime.

For example, let’s make an assert which will fail.

<?php
try {
    assert(2 < 1, "Two is less than one");
}
catch (AssertionError $ex) {
    echo $ex->getMessage();
}

When the above code is executed, you will get only a warning: "assert(): Two is less than one failed" and your exception will not be caught because assert.exception is 0. In order to make AssertionError catch the assert exception, we need to change assert.exception to 1. So when you run the following code:

<?php
ini_set('assert.exception', 1);
try {
    assert(2 < 1, "Two is not less than one");
}
catch (AssertionError $ex) {
    echo $ex->getMessage();
}

Instead of the warning, you will see that an error is caught and only an error message will be shown, i.e. “Two is less than one.”

Since the introduction of the new classes, many of the fatal and recoverable fatal errors have been inherited from the Error class. It is not guaranteed that your custom handler, which you have set by using set_exception_handler(), will catch those errors. So, if you want to throw some custom exceptions in your code, you don’t have to reset your custom handler as it can now catch the errors by just using Throwable.

Summary

If you are using a PHP version older than 7, you should keep these things in mind before transitioning. To be safe, you can also look at the php 7 upgrade guide.

Have the new error and extension classes caused you any grief? Do you like or dislike their introduction in PHP 7? What do you hope to see changed about them, if anything?

Frequently Asked Questions (FAQs) about Exception Handling in PHP 7

What are the major changes in exception handling in PHP 7?

PHP 7 introduced several significant changes to exception handling. The most notable change is the introduction of Throwable interface, which is now the base interface for all exceptions. This means that all errors in PHP 7 are exceptions and can be caught in a try/catch block. Another major change is the introduction of Error class, which is used for internal PHP errors. It’s important to note that not all errors are exceptions, but all exceptions are errors.

How does the Throwable interface work in PHP 7?

The Throwable interface in PHP 7 is the base interface for all exceptions. It is implemented by two classes: Exception and Error. This means that you can catch both Exception and Error objects in a try/catch block. The Throwable interface includes methods like getMessage(), getCode(), getFile(), getLine(), getTrace(), getTraceAsString(), getPrevious(), and __toString().

What is the purpose of the Error class in PHP 7?

The Error class in PHP 7 is used for internal PHP errors. It extends the Throwable interface and is the parent class for several specific error classes, such as TypeError, ParseError, AssertionError, and ArithmeticError. The Error class allows you to catch these types of errors in a try/catch block, which was not possible in previous versions of PHP.

How can I handle exceptions in PHP 7?

In PHP 7, you can handle exceptions using a try/catch block. You can catch both Exception and Error objects. If you want to catch all possible errors, you can catch the Throwable interface. Here’s an example:

try {
// Code that may throw an exception or error
} catch (Throwable $t) {
// Handle exception or error
}

What is the difference between Exception and Error in PHP 7?

In PHP 7, Exception and Error are two separate classes that implement the Throwable interface. The Exception class is used for traditional exceptions, while the Error class is used for internal PHP errors. This means that you can catch and handle internal PHP errors in a try/catch block, which was not possible in previous versions of PHP.

How can I throw an exception in PHP 7?

In PHP 7, you can throw an exception using the throw keyword. You can throw an instance of the Exception class or any class that extends Exception. Here’s an example:

throw new Exception("An error occurred");

How can I create a custom exception in PHP 7?

In PHP 7, you can create a custom exception by extending the Exception class. Your custom exception class can include additional properties and methods. Here’s an example:

class MyException extends Exception {
// Custom properties and methods
}

What is the purpose of the finally keyword in PHP 7?

The finally keyword in PHP 7 is used in a try/catch block to specify code that should be executed regardless of whether an exception is thrown or not. This can be useful for cleanup tasks, such as closing a database connection.

How can I rethrow an exception in PHP 7?

In PHP 7, you can rethrow an exception in a catch block using the throw keyword. This can be useful if you want to catch an exception, do some processing, and then let the exception propagate up the call stack.

How can I handle multiple exceptions in PHP 7?

In PHP 7, you can handle multiple exceptions by including multiple catch blocks in a try/catch statement. Each catch block can catch a different type of exception. Here’s an example:

try {
// Code that may throw an exception
} catch (MyException $e) {
// Handle MyException
} catch (Exception $e) {
// Handle other exceptions
}