PHP’s type system was designed from the ground up so that scalars auto-convert depending on the context. That feature became an inherent property of the language. This is especially useful as input coming from both the browser and the database is presented as an array of strings, and it has never been necessary to convert a value into a specific type before it can be used in an expression or a function. This is known as Type Juggling which allows PHP to coerce values of the wrong type into the expected scalar type declaration if possible. Thus the string “123.45” will be treated as a number in a numeric context whereas “27 foo” will not.
It is possible for either of the two arrays mentioned previously to contain NULL values, in which case these are handled differently depending on the context:
- In a string context NULL will be treated as ‘’ (an empty string).
- In a numeric context NULL will be treated as zero.
This behaviour is one of the reasons why PHP became so popular, yet this behaviour is slowly being eroded. I have recently upgraded to version 8.1 and my code is now disgorging huge volumes of deprecation notices as if it was suffering from a bad case of dysentery. The particular notices in question are:
Passing null to parameter #1 ($string) of type string is deprecated
Passing null to parameter #1 ($num) of type int|float is deprecated
The reason for this deprecation was given as “Scalar types for built-in functions are nullable by default, this behaviour is deprecated to align with the behaviour of user-defined functions, where scalar types need to be marked as nullable explicitly.”
The idea that scalar types for internal functions in the manual were never marked as nullable is a joke for the simple reason that when the manual was first written it was not possible to mark them as nullable as this capability did not exist until version 7.1. Neither was the ability to accept any value and coerce it into the expected type as this capability did not exist until version 8.0. When the behaviour of functions does not match the documentation there are two choices:
- Change the documentation to match the behaviour, or
- Change the behaviour to match the documentation.
The first option would not break backwards compatibility (BC), the second option most definitely does. The second option is therefore the wrong choice as it now requires huge numbers of developers to change huge numbers of applications.
Anther reason given in the RFC was “an explicit choice was made to not accept null values to non-nullable arguments, but changing the existing behavior of internal functions would have been too disruptive at the time.”
This argument is also a joke as the parameters on all internal functions have ALWAYS been nullable, as was documented in the manual. The idea that the effort required to maintain the status quo would have been too disruptive does not hold water as no code needed to be changed, only the documentation.
On top of that I have also discovered a place where an exception is being thrown for something which was never a problem before. I am now seeing the following error:
Uncaught exception from TypeError, message = abs(): Argument #1 ($num) must be of type int|float, string given
This goes against the traditional behaviour of this function. It also violates the Strict Typing RFC which stated that it would not be performed unless it was specifically turned ON using the declare(strict_types=1) directive.
These are serious BC breaks which are totally unjustified and which will be a huge obstacle when it comes to upgrading the PHP version. These are not “improvements” to the language, they are degradations.
What do YOU, my fellow developers, think of this situation?
More details can be found in this blog post.