5

I am currently trying to understand the behavior of the new error handling in PHP 7.

In the PHP Documentation for DivisionByZeroError, it states:

DivisionByZeroError is thrown when an attempt is made to divide a number by zero.

Fair enough, but it does not throw the DivisionByZeroError when the / operator is used.

In this example, I would expect both errors to be caught:

declare(strict_types=1);
function usesIntDiv(int $a, int $b) {
    return intdiv($a,$b);
}

function divide(int $a, int $b) {
    return $a / $b;
}

try {
    echo usesIntDiv(2,0);
} catch (DivisionByZeroError $e) {
    echo "Caught DivisionByZeroError!\n";
}

echo "\n";

try {
    echo divide(2,0);
} catch (DivisionByZeroError $e) {
    echo "Caught DivisionByZeroError!\n";
}

Instead only the first one is caught:

Caught DivisionByZeroError!

PHP Warning: Division by zero in TypeError.php on line 9...

Why? Are there other cases like this? My understanding is that if you catch Throwable you will catch anything that can be raised, which would make PHP error handling a bit more manageable. But in this case, if I use the / operator, it is the uncatchable PHP warning.

Is this specific to this error (and maybe because it was triggered by an operator), or am I misunderstanding the change in error handling?

bishop
  • 37,830
  • 11
  • 104
  • 139
Katie
  • 2,594
  • 3
  • 23
  • 31
  • A bit of clarification from [php](http://php.net/manual/en/migration70.incompatible.php) "Previously, when 0 was used as the divisor for either the divide (/) or modulus (%) operators, an E_WARNING would be emitted and false would be returned. Now, the divide operator returns a float as either +INF, -INF, or NAN, as specified by IEEE 754. The modulus operator E_WARNING has been removed and will throw a DivisionByZeroError exception. " – dpp Dec 08 '16 at 21:15
  • Literal syntax gives a warning, and `intdiv()` throws an exception. This is definitely inconsistent but at this point it's hard to change. Changing to throw an exception would break a lot of existing code that compares using `=== INF` (and turns off/ignores warnings). Backward compatibility is probably the reason for this inconsistency. – Tatsh Dec 08 '16 at 21:16
  • Appreciate the thoughtful comments indicating that this case might be for backward compatibility, so if it is just one-off, then at least the PHP documentation for the DivisionByZeroError should warn about this inconsistency. But it doesn't answer the other question, where I thought that if you catch Throwable, you catch everything, doesn't seem to be the case. The quagmire of PHP error processing has simply deepened further with PHP 7. – Katie Dec 09 '16 at 13:00
  • 1
    @Katie PHP 8 will now always throw an Exception upon division by zero. – bishop Sep 27 '19 at 02:52
  • @bishop - Awesome news :) – Katie Sep 27 '19 at 16:48

2 Answers2

2

NOTE: The Engine Warnings RFC has changed the behavior. PHP 8+ will now always throw an exception when dividing by zero. PHP 7- exhibits the behavior described below.


I don't think it's this way for backward compatibility. Rather, I feel it's implemented this way for consistency with other languages: the division operator conforms to the IEEE-754 definition of floating point division. In one, and only one, circumstance will it perform integer division, equivalent to intdiv:

The division operator ("/") returns a float value unless the two operands are integers (or strings that get converted to integers) and the numbers are evenly divisible, in which case an integer value will be returned. For integer division, see intdiv().

Importantly, and sadly not mentioned on the DivisionByZeroError doc, is that it's raised only when performing integer division. If a developer intends to have integer division, intdiv or % is called for. Otherwise, developers must still check dividend for near-zero conditions and handle accordingly (as is the case for all other floating-point operations).

It's not clear from the question what issue with Throwable occurs, but Throwable does catch the DivisionByZeroError:

try {                                                                            
    intdiv(2, 0);                                                                
} catch (\Throwable $t) {                                                        
    echo 'caught' . PHP_EOL;                                                     
}                                                                                

However, it won't catch the division by zero warning, precisely because it's a warning not an exception.

bishop
  • 37,830
  • 11
  • 104
  • 139
1

Use of the arithmetic operator / does not throw an exception in php 7, while it does in php 8. See below for the explanation in code.

<?php

try {
    echo intdiv(2, 0);
} catch (DivisionByZeroError $e) {
    echo "caught division by zero for intdiv()\n";
}

try {
    echo (2 / 0);
} catch (DivisionByZeroError $e) {
    echo "caught division by zero for /\n";
}
# php 7
$ php test.php
caught division by zero for intdiv()
PHP Warning:  Division by zero in test.php on line 10
PHP Stack trace:
PHP   1. {main}() test.php:0

Warning: Division by zero in test.php on line 10

Call Stack:
    0.0740     417272   1. {main}() test.php:0

# php 8
$ php test.php

caught division by zero for intdiv()
caught division by zero for /
8ctopus
  • 2,617
  • 2
  • 18
  • 25