PHP 7 Gotcha - Division By Zero

Over the past few weeks, I have had to priviledge to start working with PHP 7 on a daily basis. A lot of the major changes between PHP 7 and lower versions have been well communicated in various forms of media. But it is impossible for every single little change to be mentioned everywhere.

Of course you can find the complete list on the PHP website (here, in fact), but, you are still probably going to be surprised as you run your code on the new version. As you can probably tell from the title, I came across such a situation this week.

In our code base, we have the equivalent of the following situation:

echo '£'.(15/0);

You may immediately be thinking "Why the hell would you divide by 0?!". A valid question. I would add, that in our code, the 15 and the 0 are dynamic and it isn't always a division by 0 - I have oversimplified the scenario. I should also add that these are created by references to long multi-dimensional arrays (long in the sense that the keys are long) and with functions within functions.

So in the world of PHP 5 this would output £0. This was fine in this situation. If it wasn't being divided by 0, then it would be an actual value, if it was then it would just cancel out and be 0. There was no need to add code to check if it was 0 before doing the division.

But, the output wasn't really just 0.

What is really going on here, is that 15/0 was returning false and a PHP Warning (which was being hidden and ignored like all good Warnings are). When you echo false, you get 0, you also get 0 if you use it in any further mathematical operation. So this is actually an example of taking advantage of PHP loose types. One could argue that this made the code cleaner as it allows one to just output 0 without having to check any values.

PHP 7 has changed this behaviour.

To quote the change log:

Fixed weird operators behavior. Division by zero now emits warning and returns +/-INF

I am pretty sure that I was getting a Warning before, but the point is that if you do var_dump(15/0) in PHP 7, you now get float(INF). I have also seen float(NAN) if you do 0/0. The implications of this is that now:

echo '£'.(15/0);

gives:

£INF

Whoops!

I suppose this makes sense, especially with PHP 7's push on stricter typing and PHPs general direction of discouraging "lazy programming". But it does mean that if you have been taking advantage of this (or didn't even realise you were dividing by 0!) you will have to make some changes in your code.

By the way, if you were more prudent and checking if the result is equal to false, then you will also be affected as INF does not equal false.

Workarounds?

Not really. This is just one of those language changes that you will have to embrace and change your code to handle it.

There are three things that you can do to handle this new behaviour:

  1. Stop dividing by 0. Probably should do this anyway in whatever version of PHP that you are using.
  2. If, for some reason, you insist on dividing by 0, you could always use intval on the result. This would return the integer 0 for INF and NAN. So no need for an if statement. The problem is that if you sometimes expect values between 0 and 1, such as 0.5, then these would also return 0.
  3. For a foolproof way of checking the result, there are two functions: is_infinite and is_nan. These return true for INF and NAN respectively.

So there you have it. I hope we have all learnt something new today.


© 2012-2017