On 4-10-2024 13:44, Nicolas Grekas wrote:
Hi Nicolas,
# Introduction of the new function: get_declared_enums()
About this proposal, I shared a one-liner in the previous thread that
shows listing only enums is trivial already.
IMHO we don't need this function since the engine already provides
everything one needs if they want to list enums. I won't object
either, I'm just "-0".
The upside of a PHP native function would be one of performance.
# Deprecation of using class_exists() on enum names
This is a big NO on my side. This will break perfectly fine code for
the sake of some high level ideas that matter less is practice than
ensuring stability of the PHP platform. A BC break has to be worth it
and this is clearly not the case to me. The canonical examples are
checks like `class_exists($c) || interface_exists($c, false) ||
trait_exists($c, false)`. This is common code to check if a symbol
exists in current PHP. Yet, with your RFC, all such checks would
become broken immediately.
Well, this statement made me curious to see just _how_ common this type
of code is, so I've done a scan of the Packagist top 2000 projects [1].
The check you mention is used a total of 36 times in ~20 packages out of
2000 (includes some duplication, mostly within Symfony, which also
contains the fast majority of the uses of the above mentioned combination).
The full break down of the scan results [2] are as follows:
Combination of the following function calls within a set of parentheses
(typically: control structure condition):
4 out of 4: class_exists() + interface_exists() + trait_exists() +
enum_exists() 3
3 out of 4: class_exists() + interface_exists() +
trait_exists() 36
2 out of 4: class_exists() +
interface_exists() 131
2 out of 4: class_exists() +
trait_exists() 8
2 out of 4: class_exists() +
enum_exists() 1
2 out of 4: interface_exists() +
trait_exists() 1
Combination of the following function calls within the same function
scope (not necessarily in the same statement, excluding the above):
4 out of 4: class_exists() + interface_exists() + trait_exists() +
enum_exists() 2
3 out of 4: class_exists() + interface_exists() +
trait_exists() 17
3 out of 4: class_exists() + interface_exists() +
enum_exists() 3
2 out of 4: class_exists() +
interface_exists() 32
2 out of 4: class_exists() +
enum_exists() 4
2 out of 4: class_exists() +
trait_exists() 2
Please note that not all of this code would need updating.
BTW, this makes me wonder if we could have a new symbol_exists()
function, that'd basically do the above checks in one go? Mixing this
idea with Claude's, the signature could be symbol_exists($class,
$filter = -1, $autoload = true), where $filter is a bitfield that'd
allow listing only classes, abstract classes, interfaces, enums,
traits at will?
# Change of the return value of get_declared_classes()
This proposal is problematic on two aspects:
1. The planned BC break feels needless to me. Its motivation is very
moot compared to its impact on the PHP community, which will be
forced to update perfectly fine code.
Again, let's look at the real world impact based on a scan of the
Packagist top 2000.
In the top 2000 packages, there are only 47 calls to the
`get_declared_classes()` function. These calls are found in 29 packages.
[1][3]
And for the record, there are only 16 calls each to
`get_declared_interfaces()` and `get_declared_traits()`.
Also note that not all of this code would need updating, only that code
which is also targeting enums.
1. The BC break is planned without any ahead-of-change deprecation
notice (except doc of course). From a deprecation policy POV, we
reject this practice in the Symfony community. We don't do "hard
BC breaks", or "unannounced" ones: we mandate that any BC break is
first announced in the current major. This ensures the community
won't miss the notice, and won't discover the BC break when it's
very late and thus costly. There is always a way to follow that
policy, so I'd strongly recommend adopting this practice in PHP
itself, and in this RFC today. Here, this could be done by either
keeping the function as is in PHP 9, or just deprecating it (in
favor of get_declared_symbols()?)
I hear what you are saying and largely agree with you. The problem is,
of course, that it seems unlikely that we can find a way to throw a
deprecation for this, as PHP would then also throw a deprecation for
code which needs no changes (which only _intends_ to look at classes,
not enums).
In the RFC, we mention an alternative approach [4], building upon the
suggestion by Claude. This alternative approach would allow for
deprecations to be thrown, but would, in my estimation, need a longer
lead-time. Something like: introduce the new function in PHP 8.5,
deprecate use of the old functions in PHP 9.last and remove in PHP 10.0.
I can imagine combining the alternative approach via
get_declared_symbols() with a new symbol_exists() function like you
suggest above (with a similar slow path to deprecate and remove the old
functions).
On the plus side, the alternative approach makes for much more versatile
functionality. In a number of the cases I looked at, the results from
various get_declared_*() functions are combined before further
processing, so having a `get-declared_symbols()` function would allow
for simplifying that code. The same can be said for the *_exists()
functions.
On the downside, the alternative approach makes for a larger BC break
(if combined with deprecation and eventual removal of the old functions).
Would you consider that approach preferable ?
You've been a rightfully vocal criticizer of BC breaks / deprecations
in PHP. Please help the vast part of the community that favors
stability 🙏
I evaluate each RFC on their merit and weight the impact and the effort
which would be needed to handle the deprecation (and always have). For
short: do the benefits outweigh the costs ?
There are plenty of deprecations I've stayed silent about as those (IMO)
weren't particularly problematic or even if they were, the arguments to
deprecate were particularly strong.
Just saying this, as I want to make sure people aren't confusing
"speaking up when I see a problematic change" (which I do) with "being
against deprecations" (which I'm not).
Smile,
Juliette
1: Based on the latest published release of each package and providing
the package contained source code and not just a PHAR file. Excluding
vendor and test directories. May contain some duplicates. Total number
of files scanned: 233,968.
2: Detailed results of the scan for combinations of the *_exists()
functions: https://gist.github.com/jrfnl/5ac6cf10e4553a5e2174cf0f58d5e305
3: Detailed results of the scan for usage of the get_declared_*()
functions: https://gist.github.com/jrfnl/ccb11e73d804cc5bb7fe25c6899c43d1
4:
https://wiki.php.net/rfc/get_declared_enums#were_any_alternative_approaches_considered