PHP Exception Handling: PHP Exception Class Hierarchy

PHP Exception Handling – ArithmeticError

Today we begin our new PHP Exception Handling series with a deep dive into the ArithmeticError. As you can probably guess, the ArithmeticError is thrown when PHP code attempts to perform an invalid mathematical operation.

Throughout this article we’ll dig into the ArithmeticError in more detail, looking at where it sits within the PHP Exception Hierarchy. We’ll also take a look at some functional code aimed to illustrate how ArithmeticErrors might occur in your own adventures, so you can plan accordingly. Let’s get to it!

The Technical Rundown

  • All PHP errors implement the Throwable interface, or are extended from another inherited class therein.
  • Error implements the Throwable interface.
  • ArithmeticError extends the Error class.

When Should You Use It?

The ArithmeticError isn’t too difficult to grasp, but it’s important to briefly review why it has the Error suffix, as opposed to an Exception suffix. Put simply, PHP has two general categories of exceptions — internal errors and user exceptions. Historically, an internal error would’ve caused a total failure (a fatal error), forcing the PHP execution to halt completely. However, modern PHP versions now provide a catchable class known as Error for those more critical internal errors, allowing your application to continue execution after the error is properly handled.

This means, of course, that ArithmeticError is considered an internal error, primarily because PHP doesn’t know how to handle calculations that cannot be completed as requested by the code. To illustrate we have a few simple examples. We’ll start with the full, functional code below, then explore each scenario in a bit more detail afterward:

Most of the time PHP tries to handle improper mathematical issues on its own, without the need for user intervention or throwing an error. For example, if integer overflow might occur, such as using an integer value greater than a 32-bit system can handle (2,147,483,648 or higher), PHP will often automatically convert that to a floating point number to perform the calculations.

However, there are some scenarios where PHP doesn’t have an escape route, so to speak, so it must throw an ArithmeticError your way. One such scenario that we’re exploring above is performing an improper bitwise shift operation. Here we have a DoBitwiseShift() function that takes two numbers (1 and 3), and performs a left bitwise shift. As a reminder, a bitwise shift means to shift the bits of a target object X number of times. Therefore, $a << $b indicates we want to shift the bits of $a $b times to the left. The result is that we’re effectively multiplying $a by two, multiplied by $b:

The output of our operation above shows that 1 << 3 is equal to 8, because we’re effectively multiplying 1 by 2 three total times:

However, let’s now try changing $b to a negative number and see what happens:

As it happens, we’re unable to perform a bit shift a negative number of times because it just doesn’t make any logical sense. Therefore, the above function throws an ArithmeticError that we’re able to catch:

As mentioned before, another potential issue is when trying to force PHP to create objects of a certain class type, yet outside the bounds of what values that type allows. We see this in action in extreme cases using the intdiv function, which accepts two integer arguments and returns the integer quotient of their division operation.

For example, here we’re calling intdiv() with the largest possible 32-bit integer value as the dividend, and a lowly 3 as the divisor:

The output result shows that this works as expected:

Remember that since intdiv needs to generate and return an integer, we cannot get a float or decimal-type value as a result. Therefore, our result above was slightly rounded so it doesn’t include the extra third that would’ve been on there with a floating point number.

However, let’s try this again with some extra numbers:

PHP doesn’t handle this well at all, and sure enough, it throws another ArithmeticError our way:

This example in particular seems a bit odd at first. Why is it that diving two whole numbers (integers) by one another should produce a result that isn’t also an integer itself? Particularly when we’re dividing by one (well, negative one actually), which should give a number that’s effectively the same size as the original dividend.

The answer lies in considering how computers must handle signed integers, which are whole numbers that can be positive or negative. Since everything in most modern computer systems is stored in bits (ones or zeroes), these signed integers always boil down to base 2 values. This means that every increase in the potential size of an integer is always adding AT LEAST two possible values (another bit place, which can be either a zero or a one).

Extrapolating from there, let’s now imagine we’re creating our own programming language like PHP and we have a new integer data type that we need to set a limit on. We’ve decided on something small and simple for this example, so let’s go with a maximum of four bits that can represent our number in binary. Since we’re creating a signed integer here, one of those bits must be reserved and used exclusively for the positive or negative distinction of our numeric value. This leaves us with three bits to hold the absolute value.

There are a number of ways to represent signed numbers in binary, but we’ve decided it makes the most sense to only include a single binary value that corresponds to the value of zero. Therefore, we’re going to use a system called two's complement. It is so named because we simply invert all the bits of the number (and add one) when we want to flip the sign.

Let’s go through all our possible binary values and their corresponding interpreted numeric value:

Binary Value Signed Number
1000 -8
1001 -7
1010 -6
1011 -5
1100 -4
1101 -3
1110 -2
1111 -1
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7

There we have all the possible binary values we can make using four bits. What’s critical to note is that, as mentioned before, our number must always increase in sets of two since each new bit adds a one and zero. However, and this is the critical point, these values are not evenly distributed beyond the value of zero when dealing with signed numbers.

As we can see even from our simple example above, since the value of zero must take one binary value position (0000), this leaves us with only 15 remaining binary values to distribute. Using the two's complement system that we did, this means our total possible negative values will include one extra than the total possible positive values. We can go all the way down to -8, but only as high as positive 7 the other way.

Now let’s glance back at our broken intdiv() example before. Notice that the divisor we’re using is -1. This is critical and the key to the issue we’re having that is throwing an ArithmeticError. Take our simple 4-bit number above. Let’s take the minimum possible value (1000 or -8) and multiple it by -1:

Since we’re multiplying two negatives numbers we get a positive value of 8 as a result. This is a big problem! If our new programming language only allows for signed numbers to be represented by four bits, we can’t handle the value of positive eight! The largest number in our system is actually only positive seven.

This is exactly what’s going on in our intdiv() example, but on a much larger scale. PHP also uses a two's complement number system, with a maximum storage of 64 bits. PHP also provides global variables to quickly access these two extreme positive and negative values, PHP_INT_MIN and PHP_INT_MAX. Outputting those values shows just how big they are:

However, just like our 4-bit example above, the same problem applies here in PHP: The smallest negative value is one fewer than the maximum positive value if inversed. The result is that trying to divide -9223372036854775808 by -1 is effectively the same as multiplying 9223372036854775808 by 1, giving the obvious result of 9223372036854775808. Since the largest positive integer PHP can handle is one less than that, an ArithmeticError is thrown.

Check out the Airbrake-PHP library, designed to quickly and easily integrate into any PHP project, giving you and your team access to real-time error monitoring and reporting throughout your application’s entire life cycle. With automatic, instantaneous error and exception notifications at your fingertips, you’ll be constantly aware of your application’s health, including any issues that may arise. Best of all, with Airbrake’s robust web dashboard cataloging every error that occurs, you and your team can immediately dive into the exact details of what went wrong, making it easy to quickly recognize and resolve problems.