Codementor Events

How PHP 7 Handles Exceptions

Published Mar 06, 2017Last updated Mar 08, 2017
How PHP 7 Handles Exceptions

Errors are an inevitable part of the code production process. Good code producers must ensure that their code is ready for handling errors. The best code has an in-built error handling mechanism that handles all exceptions and ensures that the overall code does not break or fail to execute because of an error in one component.

In the older versions of PHP, the code developer had to write special exceptional handling code that took care of errors. In PHP 7.x, several dedicated classes take care of the process of exception handling. In this article, I will introduce these two classes and discuss how these classes function in real-world code.

Class Throwable

PHP 7 introduced a new reserved word: Throwable. This is also the name of a class from which two other classes, Exception and Error are extended. The main usage of this class is to catch any error, whether it is an exception or an error. Check out this example code snippet:

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

Another example is this Parse Error (available in PHP 7.x)

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

Class Error

Error is another new reserved word in PHP 7. This word refers to a new class introduced to handle several error exceptions, including fatal errors and type errors. The class Error has four subclasses that extend directly from the class. These subclasses are:

  • Arithmetic Error
  • Type Error
  • Parse Error
  • Assertion Error

Class Error is particularly important in the context of upgrading code to PHP 7.x. If the code written in the previous versions has a custom class named Error, rename the class immediately. Failure to do so will result in a Fatal Error in PHP 7.x.

I will now discuss the four subclasses:

Arithmetic Error

This subclass catches errors that occur during the execution of mathematical operations. A common example is the use of intdiv(), where a number gets divided by zero or -1. In such cases, an arithmetic error is thrown. Consider the following code:

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

When this snippet is executed, the error “Division of PHP_INT_MIN by -1 is not an integer” will be displayed because bit shifted it negatively. Another class, DivisionByZeroError, also extends from Arithmetic Error subclass. This error is thrown in two different conditions:

First, in the case of calculation of modulus of a number divided by 0, DivisionByZeroError occurs and displays the “Modulo by zero” error message:

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

In the above snippet, replace the modulus (%) with division (/). In this case, you will get a warning and the result could be either a 士INF or NAN.

Another way of throwing DivisionByZeroError exception is through the following sample snippet:

<?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();
}

You could also use the intdiv() function to throw DivisionByZeroError.

Note: This bug has already been reported here.

Type Error

This error subclass throws exceptions when the value of the data that you attempt to save is different from the value defined in the method or variable. This is normally used with scalar type PHP 7 functions. Consider the following code snippet:

<?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();
}

When the above code snippet is executed, TypeError exception will be thrown and the error “must be of the type integer, string given” will be displayed.

Now, remove the line declare(strict_types=1); from the snippet and execute the code again. This time, no exception is thrown. The dispelled result would be 7, unless you replace the number by a string such as name.

Parse Error

The most common scenario in which this error occurs is when you try to use eval() function to insert a new line into the code, or use an external PHP file that has a syntax error.

In the previous version of PHP, a syntax error is either a result of the eval() or the a result of the external file breaking the code, which results in fatal errors. To prevent this, code producers had to use special exception handlers to prevent the code from breaking down entirely!

Consider the following PHP file (named index3.php):

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

Next, I will call this file from another PHP file:

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

When this code snippet is executed, I will get “syntax error, unexpected end of file” instead of a fatal error. This subclass is very helpful in a number of scenarios. For instance, consider the scenario where you are sending user information to another class and by mistake, irrelevant information is sent. In the previous PHP versions, this would result in a broken code.

Assertion Error

In the older PHP versions, you had to create your own assertion exceptions handling functions (for handling fatal errors) in the case where assert_options() was used to bind custom functions.

This error only occurs when an assertion made through assert() fails. For this, you first have to configure the assert directives in PHP.ini file. There are two options in this regard:

  1. Assert.exception: The default value is 0 and it merely generates a warning for the object rather than throwing an error. If the default value is changed to 1, it will throw an exception or an Assertion Error. This error can be caught by Throwable or AssertionError.
  2. Zend.assertions: The default value is -1 (production mode). In this case, assertion code will not be generated. When this default value is changed to 1 (development mode), the assertion code is generated and executed. Note that in the case where the value is set to 0, assertion code is generated but not executed at the runtime.

Consider the following assert that is, by default, set up to fail:

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

When the above code snippet is executed, only a warning, "assert(): Two is less than one failed" is generated and the exception will not be caught because assert.exception is 0. To ensure that AssertionError catches the assert exception, I will change the assert.exception to 1.

Now when the following code snippet is executed:

<?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, the exception will be caught and the message "Two is less than one" will be displayed.

Changes for Backward Compatibles

Backward compatibility is an important issue for PHP 7.x. The most important thing to remember is that in PHP 7.x, fatal errors are inherited from the Error class. For code written in previous versions, where set_exception_handler() is used to set custom handlers, catching of fatal errors might slip through! To avoid this, remember to avoid custom handlers on PHP 7.x code and just use Throwable class for custom exception catching and handling

Handling Multiple Exceptions

PHP 7.1 introduced multiple exceptions catching, where a single catch statement could be used to handle multiple exceptions. Consider this code snippet:

<?php
try {
    // Some code...
} catch (ExceptionType1 | ExceptionType2 $e) {
    // Code to handle the exception
} catch (\Exception $e) {
    // ...
}

Summary

Exception handling is a tricky area that could cause problems in upgrading code from previous versions to PHP 7.x. It is important to remember that the new classes greatly streamline the process of exception catching and handling. For more help, check out my short PHP upgrade guide for upgrading your PHP website. Additionally, if you want to find out more about how PHP 7 can be used to create MVC web app, read this article.

Discover and read more posts from Ahmed Khan
get started
post commentsBe the first to share your opinion
Show more replies