[PHP-DEV] [RFC] [Discussion] Minor version compatibility

Thank you all for the feedback on the topic of BC breaks in argument validation https://news-web.php.net/php.internals/126706

I have collected all concerns, prepared an RFC for this change, and I’m opening discussion on its content: https://wiki.php.net/rfc/minor-version-compatibility

Since this touches on the broader topic of release policy and BC expectations, I’d also like to open the door for related improvements. Rowan raised several valid points that highlight the need for clearer communication and expectations around minor version changes. If the community feels it’s appropriate, I’m happy to include these suggestions in the voting. The more clearly BC policy is defined, the less time we’ll need to spend on debating individual cases in the future.

Rowan’s suggestions included examples like:

Maybe we could work on some criteria that could be applied (and publicised to users) about what is and isn’t allowed in minor versions.
For instance:

  • Code that already causes fatal behaviour might cause different fatal behaviour (e.g. throwing an Error instead of raising an E_ERROR)

  • Code that directly violates documented types might start throwing TypeError

  • Code that previously returned null for invalid inputs might start throwing ValueError

Kind regards,

Jorg

Hey Jorg,

On 13.4.2025 23:07:07, Jorg Sowa wrote:

I have collected all concerns, prepared an RFC for this change, and I'm opening discussion on its content: PHP: rfc:minor-version-compatibility

Let me address some parts of the RFC:

- The secondary vote #1 requires the same handling for everything. I don't think that's a wise choice.
There may be cases where keeping an E_WARNING (not E_DEPRECATED though) may be acceptable.
There may be recently (e.g. last minor) introduced APIs, which, by oversight, accept unexpected inputs. It may be decided that it would be too much of a compatibility break to "fix" this in a bugfix version. However, it should be possible to decide to fix this straight ahead in the next minor, rather than waiting for the next major. Especially for recently introduced features, having deprecation periods of possibly 4 years, when they were only introduced the year before, is a bit excessive.

- The secondary vote #2 requires that you either select E_DEPRECATED or E_WARNING. That's not right. E_DEPRECATED may be a precursor to E_WARNING.
There's no silver bullet on what exactly is to be chosen. E_DEPRECATED is for something which will change, but is generally not problematic in existing code. E_WARNING is for something which *may* change and is probably problematic in existing code.
This though means that there will be cases where there's ambiguity. That's fine. Reach a soft consensus and then go ahead with that.

I don't fundamentally disagree with the attempt to codify something here, but ultimately the PHP language does and will continue to profit from some flexibility in these regards.

I think, with the choices, we've actually ended up making in the last 12 years (essentially since PHP 5.5), users haven't been surprised too much.
At least also anecdotally from my experience - upgrading old code is perfectly fine to do with last minor -> first minor -> last minor -> ... upgrades.
New code is not fine to do such major jumps. Semantics of code using brand new features is improved sometimes. Bugs are fixed. Things end up slightly different. That's fine though. If you want to live on the edge, that's the price you pay for this. Being able to rectify freshly introduced mistakes and inaccuracies quickly is important for the health of the language.

Please, while it's sane sounding, let's not codify too much of black-and-white policies.

Thank you,

Bob

Hi,

On Sun, Apr 13, 2025 at 11:09 PM Jorg Sowa <jorg.sowa@gmail.com> wrote:

Thank you all for the feedback on the topic of BC breaks in argument validation https://news-web.php.net/php.internals/126706

I have collected all concerns, prepared an RFC for this change, and I’m opening discussion on its content: https://wiki.php.net/rfc/minor-version-compatibility

Since this touches on the broader topic of release policy and BC expectations, I’d also like to open the door for related improvements. Rowan raised several valid points that highlight the need for clearer communication and expectations around minor version changes. If the community feels it’s appropriate, I’m happy to include these suggestions in the voting. The more clearly BC policy is defined, the less time we’ll need to spend on debating individual cases in the future.

Rowan’s suggestions included examples like:

Maybe we could work on some criteria that could be applied (and publicised to users) about what is and isn’t allowed in minor versions.
For instance:

  • Code that already causes fatal behaviour might cause different fatal behaviour (e.g. throwing an Error instead of raising an E_ERROR)

  • Code that directly violates documented types might start throwing TypeError

  • Code that previously returned null for invalid inputs might start throwing ValueError

This looks a bit too generic to me and it might significantly differ based on use case as Bob noted. I think we should leave some flexibility and rather add rules for specific issues where we need some defined approach. In this case it is handling of errors for cases where only constants are documented.

Kind regards

Jakub

On Sunday, 13 April 2025 at 22:07, Jorg Sowa <jorg.sowa@gmail.com> wrote:

Thank you all for the feedback on the topic of BC breaks in argument validation php.internals: Consensus on argument validation for built-in functions

I have collected all concerns, prepared an RFC for this change, and I'm opening discussion on its content: PHP: rfc:minor-version-compatibility

Since this touches on the broader topic of release policy and BC expectations, I’d also like to open the door for related improvements. Rowan raised several valid points that highlight the need for clearer communication and expectations around minor version changes. If the community feels it's appropriate, I’m happy to include these suggestions in the voting. The more clearly BC policy is defined, the less time we’ll need to spend on debating individual cases in the future.

Rowan’s suggestions included examples like:

Maybe we could work on some criteria that could be applied (and publicised to users) about what is and isn't allowed in minor versions.
For instance:

-

Code that already causes fatal behaviour might cause different fatal behaviour (e.g. throwing an Error instead of raising an E_ERROR)

-

Code that directly violates documented types might start throwing TypeError

-

Code that previously returned null for invalid inputs might start throwing ValueError

I fundamentally reject the concept of this RFC to restrict our ability to do input validation.
Emitting an E_DEPRECATED for invalid inputs remains utter nonsense.
And this policy would severely impact incremental improvements to the language.

Moreover, I have yet to actually be shown a lack of "consensus" on this issue by the core dev team.
The following PR [1] was opened on the day this RFC was published (April 13), was reviewed and approved by 2 different core developers that have *not* participated in the previous discussion thread, and merged on the 15th.
This PR checks that the input string does not contain any NUL bytes, and if yes, now throws the standard ValueError.
A similar PR [2] was open by yet another, different, core developer, reviewed, and merged on April 22.
This, to me, indicates a clear consensus within the core dev team that these sorts of BC breaks are accepted and routine.

Let's go back to how this policy would impact incremental improvements to PHP.
And I will take one of your *own* prior RFCs as an example.
In this RFC, you point to the following PR [3] titled "round(): Validate the rounding mode", which was opened and merged about 2-3 weeks after you announced your RFC "Add 4 new rounding modes to round() function". [4][5]
That RFC states the following:

Backward Incompatible Changes
None

And this has been the case since the creation of the document by yourself, to the latest edit. [6]
According to the policy you are suggesting, the above statement is just plain false.
Because introducing *new* modes when the existing ones are not validates is a BC break, because if someone used the value of what would become e.g. PHP_ROUND_AWAY_FROM_ZERO they would have a change in behaviour (as it would stop rounding like PHP_ROUND_HALF_UP).
You could argue that this point is moot as we ended up not introducing new constants and use an enum for the new rounding modes. [7]
However, you were also against removing the newly introduced constants and to have the new rounding mode only be exposed via the new enum. [8]

The only reason you could state that there were no BC breaks in that RFC, was that validating the input mode is something we could do.
Accordingly, if this was not permitted, the only way to add new rounding modes would have to have been to wait for PHP 9.
Something that is completely ridiculous.

Validating the *value* of an argument is no different from validating the *type* of an argument passed to a function.
If PHP had a more advanced type system that include dependent types (which are types that depend on the *value*) then those ValueErrors would *be* TypeErrors.
And this is *not* a foreign concept to many people within the PHP community that use static analysis tools, as `non-empty-array`, `non-zero-int`, `positive-int`, `non-empty-string`, `numeric-string`, and many others are limited cases of dependent types. [9][10]
So attempting to create a policy where somehow introducing TypeErrors is OK but not ValueErrors is completely nonsensical.

I will repeat this once again, but error-ing on *invalid* inputs entered by the developer is an *advantage*.
I cannot comprehend the motivation to let users ship buggy code into production, that might end up hard to debug.
We throw Errors on non-existent constants, functions, classes, etc. for a reason.

As such, if this policy does get accepted, I will start to severely reduce my contributions to the project.
As it would be a clear sign to me that what people want from the project is something that I find completely nonsensical and thus I should direct my energy and time to something more inline with my own design beliefs.

Sincerely,
Gina P. Banyard

[1] throw on null bytes / resolve GH-13952 by divinity76 · Pull Request #18320 · php/php-src · GitHub
[2] ext/intl: Locale::* methods using param as path whenever relevant. by devnexen · Pull Request #18365 · php/php-src · GitHub

[3] round(): Validate the rounding mode by TimWolla · Pull Request #12252 · php/php-src · GitHub

[4] PHP: rfc:new_rounding_modes_to_round_function

[5] [RFC] [Discussion] Add 4 new rounding modes to round() function - Externals

[6] PHP: rfc:new_rounding_modes_to_round_function

[7] PHP: rfc:correctly_name_the_rounding_mode_and_make_it_an_enum

[8] [RFC] [discussion] Correctly name the rounding mode and make it an Enum - Externals
[9] PHPDoc Types | PHPStan
[10] Array types - Documentation

Thank you all for the feedback.

I don’t fundamentally disagree with the attempt to codify something
here, but ultimately the PHP language does and will continue to profit
from some flexibility in these regards.

@Bob, I agree that flexibility is desired. That’s why I would like to have rather guideline that instruction. It could be achieved with changing MUST to SHOULD.

This, to me, indicates a clear consensus within the core dev team that these sorts of BC breaks are accepted and routine.

@Gina, But that’s clearly against current policy which clearly states for both minor and patch versions [1]:

Backward compatibility must be kept-

  • Honestly everything comes from lack of clear ownership of the project. The only thing I wanted to achieve is that my PR (8 months after creation) and others similar that I wanted to create is merged. First through soft consensus, and when that failed through RFC. While at the same time similar PRs are already merged. It’s a bit inconsistent right?

  • Regarding to your investigation, I don’t argue with it. But according to the CURRENT policy my RFC should be rejected along with many others. However, this concern was not raised. The next RFC [2] fixing the problem was also based on the assumption what’s the direction of project (moving into the enums). Similar thing is about namespaces, there is no clear policy about moving out functions from global space to namespaces. New contributors are not aware of such tendencies, neither was I.

    And I don’t think that I’m suitable to proceed with the policy change as I barely contribute to the project and don’t have much experience and knowledge about all reasons behind past decisions. And if so I’m leaving this matter to the core developers to decide. I don’t care if it’s deprecation, warning, one version prior or the same. I don’t want to waste more time on such discussion. I just want clear, consistent rules that are respected by everyone.

  • Kind regards,

  • Jorg

  • [1] https://wiki.php.net/rfc/releaseprocess
    [2] https://wiki.php.net/rfc/correctly_name_the_rounding_mode_and_make_it_an_enum

Am 27.04.2025 um 20:22 schrieb Gina P. Banyard <internals@gpb.moe>:

I fundamentally reject the concept of this RFC to restrict our ability to do input validation.

I am sorry but I fail to see how the RFC restrict the ability to do input validation.

Emitting an E_DEPRECATED for invalid inputs remains utter nonsense.

I agree that it should most probably be an E_WARNING, not an E_DEPRECATED. Which is a voting option in the RFC.

I will repeat this once again, but error-ing on *invalid* inputs entered by the developer is an *advantage*.
I cannot comprehend the motivation to let users ship buggy code into production, that might end up hard to debug.
We throw Errors on non-existent constants, functions, classes, etc. for a reason.

I am *not* against error-ing on invalid input, and I don't think anyone else is.
It is all about the migration path for existing software and having guidelines for such changes.

Using intermediate warnings before going to an Exception allows updating an installation step-by-step instead of all at once. Which is important both for projects using a lot of composer packages as well as installations where PHP and applications are maintained by different people (e.g. hosting companies).

Case-studies regarding implicitly nullable parameters and the warning phase:
- Releases · hybridauth/hybridauth · GitHub with fixes for PHP 8.4 was released only last week
- GitHub - grpc/grpc-php: Repo for gRPC PHP still has not released fixes for PHP 8.4

Sure, this is from one minor version to the next and not about invalid argument values and it uses E_DEPRECATED instead of E_WARNING but my main points stays the same:
If this would have been done without warning phase then it would be a blocker for upgrading to PHP 8.4, now filtering this warning is enough while the packages are being updated.

And I am of the very strong opinion that the benefits of reducing friction for upgrading to the current PHP version outweighs the costs. Forgetting to change the warning to an Exception can be simply prevented by e.g. a helper function or even a greppable code comment.

As such, if this policy does get accepted, I will start to severely reduce my contributions to the project.
As it would be a clear sign to me that what people want from the project is something that I find completely nonsensical and thus I should direct my energy and time to something more inline with my own design beliefs.

I find it very unfortunate that you are feeling the need to pressure the community in this way as I can assure you that we all want the project to improve even it is sometimes in different ways.

Regards,
- Chris

On Wednesday, 30 April 2025 at 13:16, Christian Schneider <cschneid@cschneid.com> wrote:

Am 27.04.2025 um 20:22 schrieb Gina P. Banyard internals@gpb.moe:

> I fundamentally reject the concept of this RFC to restrict our ability to do input validation.

I am sorry but I fail to see how the RFC restrict the ability to do input validation.

[...]

> I will repeat this once again, but error-ing on invalid inputs entered by the developer is an advantage.
> I cannot comprehend the motivation to let users ship buggy code into production, that might end up hard to debug.
> We throw Errors on non-existent constants, functions, classes, etc. for a reason.

I am not against error-ing on invalid input, and I don't think anyone else is.
It is all about the migration path for existing software and having guidelines for such changes.

Using intermediate warnings before going to an Exception allows updating an installation step-by-step instead of all at once. Which is important both for projects using a lot of composer packages as well as installations where PHP and applications are maintained by different people (e.g. hosting companies).

Case-studies regarding implicitly nullable parameters and the warning phase:
- Releases · hybridauth/hybridauth · GitHub with fixes for PHP 8.4 was released only last week
- GitHub - grpc/grpc-php: Repo for gRPC PHP still has not released fixes for PHP 8.4

Sure, this is from one minor version to the next and not about invalid argument values and it uses E_DEPRECATED instead of E_WARNING but my main points stays the same:
If this would have been done without warning phase then it would be a blocker for upgrading to PHP 8.4, now filtering this warning is enough while the packages are being updated.

Comparing a core language change to be the same level as throwing a ValueError on invalid inputs of a bundled extension function is a bit of a straw man argument.
The motivation for this policy change is _explicitly_ about input validation.

And I am of the very strong opinion that the benefits of reducing friction for upgrading to the current PHP version outweighs the costs. Forgetting to change the warning to an Exception can be simply prevented by e.g. a helper function or even a greppable code comment.

I am not saying break all the things, however, breaking clearly buggy code is something I consider a feature.
Letting users ship buggy code in production is not something that I find at all reasonable.
And I am looking forward to your suggestion on how to simply implement this for every single unique validation error that we encounter.

> As such, if this policy does get accepted, I will start to severely reduce my contributions to the project.
> As it would be a clear sign to me that what people want from the project is something that I find completely nonsensical and thus I should direct my energy and time to something more inline with my own design beliefs.

I find it very unfortunate that you are feeling the need to pressure the community in this way as I can assure you that we all want the project to improve even it is sometimes in different ways.

I am entitled to my strong opinions as someone that has provided over 700 reviews to php-src PRs and over 800 reviews to doc PRs in 2024, on top of all my own code, documentation, and internal email contributions.
This gives me a very broad overview of the state the whole PHP project is in, and claiming that everything can "just be done at a later date easily" is a completely unrealistic worldview.
Moreover, I know that I am speaking for at least a few other core contributors that don't want to engage more than necessary on this mailing list, considering how draining it can be.

So yes, if the community thinks the way I've been reviewing and contributing to the project is wrong, then severely reducing my contributions is the only reasonable step.
You consider it pressure, I consider it communicating how I feel about contributing to the project, which has already been less enjoyable than before for a while.

Best regards,

Gina P. Banyard

Am 08.05.2025 um 00:36 schrieb Gina P. Banyard <internals@gpb.moe>:

If this would have been done without warning phase then it would be a blocker for upgrading to PHP 8.4, now filtering this warning is enough while the packages are being updated.

Comparing a core language change to be the same level as throwing a ValueError on invalid inputs of a bundled extension function is a bit of a straw man argument.
The motivation for this policy change is _explicitly_ about input validation.

You are right in principle but from an application / installation maintainers point of view it is irrelevant if an application breaks because old code used a more relaxed language definition (as was the case with implicitly nullable parameters) or invalid arguments (a function might have ignored unknown flags).

I am not saying break all the things, however, breaking clearly buggy code is something I consider a feature.

But now we have the problem of defining "clearly buggy code": If the output of some code was correct even though I gave some unknown flags to a function which were ignored I would call that "should be fixed" but not "clearly buggy".
That's why I'm advocating of treating it in two steps instead of one. That's all I'm saying.

Now there might be cases where you feel like the parameter value is clearly wrong but instead of having to decide that for each and every instance (and people might disagree) I find it easier to err on the delayed-BC-break side.

Letting users ship buggy code in production is not something that I find at all reasonable.
And I am looking forward to your suggestion on how to simply implement this for every single unique validation error that we encounter.

Either do something like
  zend_warn_or_throw_exception(E_WARNING, zend_ce_value_error, 80500, "Invalid argument ...", ...);
which automatically either outputs a warning or throw an exception depending on the PHP version or something like
  zend_error(E_WARNING, "Invalid argument ...", '...); /* FIXME PHP_VERSION_ID 80500 */
which can be grepped / warned about in CI/CD whenever the PHP version is changed and changed to a zend_throw_exception(...) at that point.

This assumes that it would be possible to define a clear point where the switch from E_WARNING to ValueError should happen but it seems doable to me without putting a lot of burden on core developers.

Regards,
- Chris

-----Original Message-----
You consider it pressure, I consider it communicating how I feel about
contributing to the project, which has already been less enjoyable than before
for a while.

Best regards,

Gina P. Banyard

Just a quick note, your contributions to the project are greatly appreciated by those of us who use the software. This probably isn't outright stated enough. I can understand how the bikeshedding and over-discussion of minutiae can get tedious, just remember there are a largely-silent majority of us out here who are impressed at the direction the language has taken as of late.

-Jeff