On 11/03/2025 11:54, Gina P. Banyard wrote:
It also means that we need to do *multiple* passes, on the same code path,
resulting in somewhat of a code churn and possibly not using a common
abstraction.
Considering that we didn't even have the time to properly remove some
deprecations from PHP 7 with the PHP 8.0.0 release, nor convert all relevant
E_WARNINGs to Value/Type errors, I expect that we would be missing some of them
again when PHP 9 comes around.
I wonder if in some cases, we could automate this within the source code
- e.g. with a macro like WARNING_OR_ERROR(message,
first_version_for_error) which checks a compile-time constant for the
current version. I realise that still requires code cleanup later, but
it means we can't miss our chance when the appropriate version does come
around.
and this is ignoring the fact that PHP does not follow semver
This is true, but only in the sense that something under the MIT license
isn't under the BSD license. Officially, PHP's release policy is
extremely similar to SemVer.
https://semver.org/ says:
> Major version X (X.y.z | X > 0) MUST be incremented if any backward
incompatible changes are introduced to the public API. It MAY also
include minor and patch level changes
Meanwhile https://github.com/php/policies/blob/main/release-process.rst
says:
> Major Version Number
> x.y.z to x+1.0.0
> - Backward compatibility can be broken
> - API compatibility can be broken (internals and userland)
> Minor Version Number
> x.y.z to x.y+1.z
> - Backward compatibility must be kept
> - API compatibility must be kept (userland)
Now, what exactly constitutes "backward compatibility" and "userland API
compatibility" can be debated, but there is currently no agreed policy
allowing "small compatibility breaks".
The fact that everyone acts as though there is such a policy, probably
means a rewrite of that document is overdue. A policy that nobody
follows is no use to either contributors or users.
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 produced NULL as an error case only for invalid inputs might
start throwing ValueError
Some of the cases linked at the start of this thread might then meet the
agreed criteria, but some might not.
For instance, it looks like pathinfo does give reliable outputs if the
flag argument is treated as a bitmask, even if doing so makes no logical
sense. It's not strictly the wrong type, and the behaviour isn't
obviously attempting to indicate an error, or doing something
immediately fatal.
So to allow that as well, we'd need some broader criteria, like:
- Code that is clearly an error on the part of the programmer might
starting throwing Errors
But that's probably too broad and debatable to be meaningful policy.
Another alternative is to scrap x.y.z versioning completely, and give
every annual release equal status. This is the approach that PostgreSQL
has taken, I believe.
We'd probably still want some kind of deprecation policy - some changes
should be deprecated for X releases before removal/change. Which brings
us back to some kind of criteria for which changes need that, so doesn't
really solve the problem.
--
Rowan Tommins
[IMSoP]