A massive BC break

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.

In all professional companies I worked as a developer in the last 10 years, PHP was banned because it is a not type safe language. This needs a „programmer who knows what he does“ and that is expensive. So the trend is to go to „idiot safe“ programming to be able to give this task to cheap people.

That’s why no company is using PHP any more and that’s why PHP needs to change something to not have a slow upcoming death.
So they decided to move towards type safety more and more which is not possible without massive BCs. I guess 8.0 is only the beginning and in 9.0 no old code will run in any way.

That’s why I stopped developing PHP backends a few years ago too and moved to NodeJs. Luckily JavaScript is not going this way because we have on JavaScript depending “languages“ like typescript, angular, react etc.

You are missing the point. Strict typing is now available in PHP, but it is supposed to be OFF by default unless it is turned ON using the declare(strict_types=1) directive. Making the internal functions perform strict typing when the option has not been turned ON is therefore wrong.

Not accepting nulls also violates the same principle - the language should operate in coercive typing mode - just as it has done for the past 20 years - unless strict typing is explicitly turned ON.

People have been saying “PHP is dying” for about as long as i’ve been using PHP. I started in 4. “upcoming death” is kind of like saying you’re going to die - yes, but i plan on doing it in about 50 years.

Strict typing of scalars has been available since PHP 7.1 (2015), so I dont know that “strict typing is now available in PHP” is a particularly topical statement either.

The issue is not that strict typing is available in PHP, but that it is supposed to be optional. This means that weak typing, where scalar values are coerced into the correct type, is still supposed to be in operation. However, my code which has worked for years, has started to throw a TypeError because I passed a string value to the abs() function instead of a number. This is wrong.

The other issue is that null values are being rejected instead of being coerced into the relevant type according to the context, as has been the standard behaviour for the last 20 years. This is a massive BC break which is going to upset huge numbers of developers.

Well to be clear, that’s not strict_typing at all - that’s a change of the abs function. Take a look at the changelog on the function’s page.

It’s also not exactly new either - 8.0 has been out since 2020.

It is strict typing because it is a change from the weak typing which was performed in earlier versions of PHP where values of any type, including NULL, were automatically coerced into the expected type. Strict typing is supposed to be optional and not performed unless it is specifically turned ON.

The fact that 8.0 has been out for 2.5 years is nothing to boast about - less than 10% of the world’s websites have upgraded to this version, and it is BC breaks like this one which is standing in the way.

So when you query your database and the connection had failed, you expect it should work anyway, because you should weakly convert false to a database object? That’s definitely what PHP does, right? No? Oh… so the Query command strongly types its database parameter? It cant be! I didnt turn strict typing on!
If i say $n = 3; $n[1] = "wark";, that should work, right? Weak typing! turn it into an array! What? No? oh…

It’s always been there. They didnt enforce it on everything, but its always been there. The RFC’s you cite in your article dont say “every in-built PHP function must have all parameters weakly typed”, it says some variation on “IF they are weakly typed, then the system must accept and coerce weakly.”

And if PHP is moving towards strict typing, or as was put in your opening post, have decided to “Change the behviour to match the documentation”… that’s what PHP has decided to do. Closing what, based on the documentation, is a bug. Just because you wrote your code based on what is now deemed a bug doesnt make their decision wrong.

You updated to a new MAJOR version of a piece of software and didnt check to see if something broke. shrug

I’m hoping that they add an option in php.ini to globally enforce strict typing rather than having to declare it in every script

3 Likes

Your example showing bad code that no-one would ever use is a joke. It shows that you clearly don’t understand how weak typing has worked for the past 20 years. Although it allows you to pass anything as a parameter it’s value is checked within the function to see if it is the correct type or can be converted to the correct type. If not it is rejected. Strict typing means that the value’s type is checked by the PHP engine BEFORE it is passed to the function using the type hint for that parameter in the function’s signature.

Weak typing has been the default since PHP was created. It says so in the documentation. It has always coerced NULL into either an empty string or the value zero depending on the context. It says so in the documentation. Strict typing is supposed to be an option which is OFF unless it is specifically turned ON.

The changes made in version 8 are a complete reversal of 20 years’ tradition. They are not improving the language, they are destroying it. People are not upgrading to the latest version simply because their code, which has worked for years, suddenly stops working. It is not THEIR code which is broken, it is PHP itself.

Closing what, based on the documentation, is a bug.

It was not a bug. All the internal functions worked AS DOCUMENTED, which was in weak typing mode. The function signature for the abs() function used to read:
number abs( mixed number )
but is has been changed to read
abs(int|float $num): int|float
That fits the definition of a BC break.

Any new feature which is added to the language, such as type hinting and strict typing, is supposed to be optional, not mandatory. Enforcing it whether you like it or not is plain WRONG!

Fine. You’re right. You are the god of PHP, handing down commandments. All Hail Your Decision. You never make poor code, and everything you create must always work forever. Your determination of what is right and wrong is absolute.

Or… PHP is an evolving language, that has decided to change how it handles this parameter in this function. I would point out that the RFC you hilighted as making the choice was reviewed by over 40 people who are responsible for making the decisions, and every single one of them voted yes.

You dont have to like what the language evolves into. Your options are to evolve with it, freeze yourself to an old version that conforms to your beliefs, or find another language.

was.

The function documentation was changed. Yup. It happens.

Sure.
So did removing mysql_ as a library. It still happened. The world didnt end; you changed your code to mysqli_ or PDO…

Keep going.

I am not the God of PHP, I am not handing down commandments, I am stating facts which are backed up by the documentation which I started reading in 2002.The behaviour of internal functions has been consistent with the documentation for over 20 years. This behavior is now being changed, and that is going to break virtually all existing applications which have relied on the traditional and documented behavior.

PHP has always been dynamically typed - it says so in the manual.

PHP has always been weakly typed - it says so in the manual in Type Juggling.

PHP has always coerced NULL into either an empty string or the the value zero depending on the context - it say so in the manual.

The expectation that code which I wrote 5, 10 or 15 years ago should carry on working in the latest release is not an unreasonable expectation. Backwards compatibility is a feature, not an option that can be discarded on a whim by dogmatic programmers who had no part in the creation of the language.

So the decision to break the language was made by just 40 people? What about the millions of developers who use the language to earn a living? Don’t their opinions count?

You clearly do not understand the difference between evolving and devolving. The language can only be improved by adding functionality, not by removing it

It was not a bug that PHP has operated in weak typing mode for over 20 years. This means that any expression or function ccould accept a value of any type and, using the rules explained in Type Juggling attempt to coerce it into the expected type.

While some changes have been necessary, such as replacing the old MySQL extension with a new one, changes such as those mentioned in this post are NOT necessary, they are simply the whims of a bunch of dogmatists who want to enforce their warped views of code “purity” on the entire world.

You kind of left off the second sentence in that paragraph:

So yes, it’s in the manual. So’s the second half.

Just because it’s the default doesnt mean everything uses it.
also, there’s the big red block underneath it that says

Again, go find anyone that coded the mysql_ functions into their code and see if they’re still doing it. Or ternaries with unqualified nesting. or anyone using ldap_sort(), or… or…or…
Not the first time. Not the last time. Sometimes things go away. It happens. It sucks, sure. But you change your code and you move forward.

1 Like

Possible, yes. Mandatory, no. It is an option which is OFF unless turned ON.

In this context the value must be a value of the type.

This text did not exist in the manual for version 7, it was changed for version 8. This change in the documentation matches the change in PHP’s behaviour. It is still a change, and a breaking change at that.

Just because it’s the default doesnt mean everything uses it.

It was used everywhere in the language up to and including version 7. It has changed in version 8. It is still a breaking change.

this behaviour is DEPRECATED as of PHP 8.1.0.

Just because a breaking change is documented does not make it a non-breaking change.

Again, go find anyone that coded the mysql_ functions into their code and see if they’re still doing it.

I don’t know how many people are still using the mysql_ functions, and neither do you. I changed to the mysqli_ functions when the new version of mysql was released. I am not averse to change when that change is for the better or has benefits. What I am against are breaking changes to established behaviour for NO GOOD REASON. I am not against type hinting or stricy typing provided that they are optional and not compulsory.

Just wanted to point out that deprecated does not mean it is an error. The code still works as before. Therefore, by definition, it is not a breaking change. You could capture the notices and that would be that.

You could contrast this with the time when count(null) was fixed to prevent null from being passed. That was an actual change which broke quite a bit of code though technically it was a bug fix.

Of course in PHP 9 the functions would no longer work. So you will need to eventually update your code. Or just stick with your current PHP version.

2 Likes

Even though with version 8.1 it will only issue deprecation notices and not fail, it WILL fail in version 9. Whether it’s now or later it is still a breaking change.

It is not a breaking change to fix a security issue or anything vitally important, it is just a virtue-signalling issue to satisfy the whims of a buch of dogmatic developers who think they rule the world. Millions of other application developers who use PHP would disagree.

I know exactly how many people are using the mysql_ functions in PHP 8. 0. Because they were removed in PHP 7.0.

Do I really need to go through the entire changelog of PHP and point out every time a function was removed from the language to you before you can grasp the concept that it is an evolution, not a devolvement. You dont like it. We get that. EVERYBODY gets that. You made that clear from word 1. The thing you relied on has been replaced by a different version. Like going from mysql_ to mysqli_. The language has changed.

Change your code.
Stay on an old version.
Find a new language.

Or keep shouting at clouds i guess. You do you. I’m done talking to a wall.

1 Like

I’m hoping that they add an option in php.ini to globally enforce strict typing rather than having to declare it in every script

At least you have the ability to exercise your choice by simply adding a single line of code to each script. Us poor schmucks who have large codebases which have run quite happily for years and wish to continue using the weak typing option now have to examine every script and fix EVERY call to a PHP function.