[RFC] [Discussion] Add get_declared_enums() function

php.internals

Unnamed Person

1 year ago
L.S., After the earlier discussion [1] regarding this topic in August, it is our pleasure to present the RFC to add a `get_declared_enums()` function to PHP for discussion. https://wiki.php.net/rfc/get_declared_enums We look forward to your feedback and hope for a constructive discussion. Smile, Ayesh and Juliette 1: https://externals.io/message/124937#124937

Alexandru Pătrănescu

1 year ago
On Wed, Oct 2, 2024 at 1:48 AM Juliette Reinders Folmer < php-internals_nospam@adviesenzo.nl> wrote:
> L.S., > > After the earlier discussion [1] regarding this topic in August, it is our > pleasure to present the RFC to add a `get_declared_enums()` function to PHP > for discussion. > > https://wiki.php.net/rfc/get_declared_enums > > We look forward to your feedback and hope for a constructive discussion. > >
I'm all for `get_declared_enums()`. However, I don't accept that enums are not classes and going with the assumption that they are not will bring more problems than it solves. I noticed how you analyzed things internally, that everything is a class internally, including an interface or a trait. But this is an API for the userland, and in userland this is not true. The 3 structures: classes, interfaces and traits and distinct things that you can use in different ways in (generated) code. I believe that in userland, the common understanding is that enums are just a specific type of class, final ones. I would find it surprising if `get_declared_classes()` would not return enums. Avoiding a BC break would be nice also if there is no reason for it. One would be able to get only the enums by using something like: ```php function get_declared_enums_only() { return array_diff(get_declared_classes(), get_declared_enums()); } ```
-- Alex

Rob Landers

1 year ago
On Wed, Oct 2, 2024, at 09:20, Alexandru Pătrănescu wrote:
> > On Wed, Oct 2, 2024 at 1:48 AM Juliette Reinders Folmer <php-internals_nospam@adviesenzo.nl> wrote: >> L.S., >> >> After the earlier discussion [1] regarding this topic in August, it is our pleasure to present the RFC to add a `get_declared_enums()` function to PHP for discussion. >> >> https://wiki.php.net/rfc/get_declared_enums >> >> We look forward to your feedback and hope for a constructive discussion. > > I'm all for `get_declared_enums()`. > However, I don't accept that enums are not classes and going with the assumption that they are not will bring more problems than it solves. > > I noticed how you analyzed things internally, that everything is a class internally, including an interface or a trait. > But this is an API for the userland, and in userland this is not true. > The 3 structures: classes, interfaces and traits and distinct things that you can use in different ways in (generated) code. > > I believe that in userland, the common understanding is that enums are just a specific type of class, final ones. > I would find it surprising if `get_declared_classes()` would not return enums. > > Avoiding a BC break would be nice also if there is no reason for it. > One would be able to get only the enums by using something like: > ```php > function get_declared_enums_only() { > return array_diff(get_declared_classes(), get_declared_enums()); > } > ``` > > -- > Alex >
Interesting observation Alexandru, I find it weird that class_exists("MyEnum") would return true, but there is only one symbol table—you can’t name a class and enum with the same name, so it makes some sense. However, while enums have “some” similarities with classes in PHP, they are not the same. For example, you cannot implement arbitrary interfaces (ie, Stringable), you cannot extend another class, you cannot instantiate them with “new”, you cannot have state, you cannot clone them, magic methods are forbidden, etc. You cannot build a “class” with these rules in PHP, so I don’t think enums quack sufficiently like classes to be called a class. I would argue that it is an object, however. I think it would be worth implementing a get_declared_objects() that behaves like get_declared_classes() currently does. — Rob

Derick Rethans

1 year ago
On 2 October 2024 09:06:20 BST, Rob Landers <rob@bottled.codes> wrote:
>You cannot build a “class” with these rules in PHP, so I don’t think enums quack sufficiently like classes to be called a class. I would argue that it is an object, however. I think it would be worth implementing a get_declared_objects() that behaves like get_declared_classes() currently does.
Objects are instantiated classes, and not declared elements, so that name wouldn't make sense. cheers Derick

Alexandru Pătrănescu

1 year ago
On Wed, Oct 2, 2024 at 11:06 AM Rob Landers <rob@bottled.codes> wrote:
> On Wed, Oct 2, 2024, at 09:20, Alexandru Pătrănescu wrote: > > > On Wed, Oct 2, 2024 at 1:48 AM Juliette Reinders Folmer < > php-internals_nospam@adviesenzo.nl> wrote: > > L.S., > > After the earlier discussion [1] regarding this topic in August, it is our > pleasure to present the RFC to add a `get_declared_enums()` function to PHP > for discussion. > > https://wiki.php.net/rfc/get_declared_enums > > We look forward to your feedback and hope for a constructive discussion. > > > I'm all for `get_declared_enums()`. > However, I don't accept that enums are not classes and going with the > assumption that they are not will bring more problems than it solves. > > I noticed how you analyzed things internally, that everything is a class > internally, including an interface or a trait. > But this is an API for the userland, and in userland this is not true. > The 3 structures: classes, interfaces and traits and distinct things that > you can use in different ways in (generated) code. > > I believe that in userland, the common understanding is that enums are > just a specific type of class, final ones. > I would find it surprising if `get_declared_classes()` would not return > enums. > > Avoiding a BC break would be nice also if there is no reason for it. > One would be able to get only the enums by using something like: > ```php > function get_declared_enums_only() { > return array_diff(get_declared_classes(), get_declared_enums()); > } > ``` > > -- > Alex > > > Interesting observation Alexandru, > > I find it weird that class_exists("MyEnum") would return true, but there > is only one symbol table—you can’t name a class and enum with the same > name, so it makes some sense. However, while enums have “some” similarities > with classes in PHP, they are not the same. For example, you cannot > implement arbitrary interfaces (ie, Stringable), you cannot extend another > class, you cannot instantiate them with “new”, you cannot have state, you > cannot clone them, magic methods are forbidden, etc. > > You cannot build a “class” with these rules in PHP, so I don’t think enums > quack sufficiently like classes to be called a class. I would argue that it > is an object, however. I think it would be worth implementing a > get_declared_objects() that behaves like get_declared_classes() currently > does. > > — Rob >
Yes, that's a good point as well. I think my view comes from when I initially dug (15+ years ago) into what an enum in Java is, and learned that it is just a syntactic sugar, and a final class would be generated implementing `Comparable` and extending an abstract class `Enum`. And I think in PHP that could be a similar view. And most of the limitations we have are not impossible to create with a standard class. I think in time we might get to remove some limitations. I, personally, don't agree with the `Stringable` related limitation. And also with the limitation on no state. In Java this is the simple way to create a singleton: an enum with one case; and we can't have this in PHP. If we remove the limitations, should we reclassify at that point enums as classes?
-- Alex

Larry Garfield

1 year ago
On Wed, Oct 2, 2024, at 7:27 AM, Alexandru Pătrănescu wrote:
> I think my view comes from when I initially dug (15+ years ago) into > what an enum in Java is, and learned that it is just a syntactic sugar, > and a final class would be generated implementing `Comparable` and > extending an abstract class `Enum`. > > And I think in PHP that could be a similar view. And most of the > limitations we have are not impossible to create with a standard class.
That is roughly the way enums are implemented in several languages, including PHP.
> I think in time we might get to remove some limitations. > I, personally, don't agree with the `Stringable` related limitation.
This has been discussed numerous times already. Please don't bring it up here, it's not relevant to this thread.
> And also with the limitation on no state. In Java this is the simple > way to create a singleton: an enum with one case; and we can't have > this in PHP.
That is what we mean by ADTs or Tagged Unions (various names for the same thing), and it's something Ilija and I have on the roadmap. It's been on the roadmap since the first enum release, but we deliberately punted it for later to keep the initial RFC manageable. It's also dependent on the pattern matching RFC, which hopefully we'll be able to bring up for discussion for 8.5.
> If we remove the limitations, should we reclassify at that point enums > as classes?
Enums already are classes. I don't know what reclassification you're talking about. The RFC is about adding a "get me a list of only those classes that are enums" function to PHP. Changing how Enums behave or are implemented is wildly out of scope. --Larry Garfield

Alexandru Pătrănescu

1 year ago
On Wed, Oct 2, 2024, 21:32 Larry Garfield <larry@garfieldtech.com> wrote:
> On Wed, Oct 2, 2024, at 7:27 AM, Alexandru Pătrănescu wrote: > > > If we remove the limitations, should we reclassify at that point enums > > as classes? > > Enums already are classes. I don't know what reclassification you're > talking about. The RFC is about adding a "get me a list of only those > classes that are enums" function to PHP. Changing how Enums behave or are > implemented is wildly out of scope. > > >
Sorry if my reply wasn't clear enough and somehow ambiguous. I agree. Enums are classes. And my reply was just a way to explain this view in a friendly manner. Maybe reading previous replies in the thread could be more clear on my stance. Comming back to the RFC, I think it needs to be changed as I believe it focuses too much on internals, ignoring userland view. Just adding the new function should be enough. -- Alex

Rob Landers

1 year ago
On Wed, Oct 2, 2024, at 14:27, Alexandru Pătrănescu wrote:
> > On Wed, Oct 2, 2024 at 11:06 AM Rob Landers <rob@bottled.codes> wrote: >> __ >> On Wed, Oct 2, 2024, at 09:20, Alexandru Pătrănescu wrote: >>> >>> On Wed, Oct 2, 2024 at 1:48 AM Juliette Reinders Folmer <php-internals_nospam@adviesenzo.nl> wrote: >>>> L.S., >>>> >>>> After the earlier discussion [1] regarding this topic in August, it is our pleasure to present the RFC to add a `get_declared_enums()` function to PHP for discussion. >>>> >>>> https://wiki.php.net/rfc/get_declared_enums >>>> >>>> We look forward to your feedback and hope for a constructive discussion. >>> >>> I'm all for `get_declared_enums()`. >>> However, I don't accept that enums are not classes and going with the assumption that they are not will bring more problems than it solves. >>> >>> I noticed how you analyzed things internally, that everything is a class internally, including an interface or a trait. >>> But this is an API for the userland, and in userland this is not true. >>> The 3 structures: classes, interfaces and traits and distinct things that you can use in different ways in (generated) code. >>> >>> I believe that in userland, the common understanding is that enums are just a specific type of class, final ones. >>> I would find it surprising if `get_declared_classes()` would not return enums. >>> >>> Avoiding a BC break would be nice also if there is no reason for it. >>> One would be able to get only the enums by using something like: >>> ```php >>> function get_declared_enums_only() { >>> return array_diff(get_declared_classes(), get_declared_enums()); >>> } >>> ``` >>> >>> -- >>> Alex >>> >> >> Interesting observation Alexandru, >> >> I find it weird that class_exists("MyEnum") would return true, but there is only one symbol table—you can’t name a class and enum with the same name, so it makes some sense. However, while enums have “some” similarities with classes in PHP, they are not the same. For example, you cannot implement arbitrary interfaces (ie, Stringable), you cannot extend another class, you cannot instantiate them with “new”, you cannot have state, you cannot clone them, magic methods are forbidden, etc. >> >> You cannot build a “class” with these rules in PHP, so I don’t think enums quack sufficiently like classes to be called a class. I would argue that it is an object, however. I think it would be worth implementing a get_declared_objects() that behaves like get_declared_classes() currently does. >> >> — Rob > > Yes, that's a good point as well. > > I think my view comes from when I initially dug (15+ years ago) into what an enum in Java is, and learned that it is just a syntactic sugar, and a final class would be generated implementing `Comparable` and extending an abstract class `Enum`. > > And I think in PHP that could be a similar view. And most of the limitations we have are not impossible to create with a standard class. > > I think in time we might get to remove some limitations. > I, personally, don't agree with the `Stringable` related limitation. > And also with the limitation on no state. In Java this is the simple way to create a singleton: an enum with one case; and we can't have this in PHP. > > If we remove the limitations, should we reclassify at that point enums as classes? > > -- > Alex >
Hey Alex, IMHO, the only thing keeping an enum from being "sugared classes" is the ability to define state. I'm not even sure why it is a limitation because it is relatively easy to "hack it in": https://3v4l.org/nqZfK Being able to define state and behavior are fundamental parts of what makes a class a class. If you can't have state, then I'm not sure it is a class. We're getting into theoretical computer science here, but that is, I think, a generally accepted basic definition in Object Oriented Programming.[1] The rest could be explained as just sugar for: 1. final class 2. extend base enum class (hence why it can't extend another class) 3. final clone method in base class which prevents cloning 4. hidden magic method implementations that throw (hence unable to implement stringable) 5. singletons for each case 6. etc (and before someone argues that it "isn't how it works", I am merely describing the sugar) So, yes, once that restriction is lifted, I think we could officially call them classes. Until then though, I think they're just "enums?" On Wed, Oct 2, 2024, at 14:19, Derick Rethans wrote:
> On 2 October 2024 09:06:20 BST, Rob Landers <rob@bottled.codes> wrote: > > >You cannot build a “class” with these rules in PHP, so I don’t think enums quack sufficiently like classes to be called a class. I would argue that it is an object, however. I think it would be worth implementing a get_declared_objects() that behaves like get_declared_classes() currently does. > > Objects are instantiated classes, and not declared elements, so that name wouldn't make sense. > > cheers > Derick >
This is also why I suggested calling them objects instead of classes. Enum cases are instantiated enums which are objects, but they aren't classes. We could be more specific and call it get_declared_object_types() I'd absolutely love to hear a reason, rooted in computer science, why PHP enums are classes. I'm no expert on the subject, but this has bothered me for years... so I would love to learn more or discuss the topic further. [1]: https://books.google.com/books?id=9NGWq3K1RwUC&pg=PA18 Foundations of Object Oriented Languages: types and semantics, Bruce, 2002 -- just had to break out an old text book :) — Rob

Alexandru Pătrănescu

1 year ago
On Wed, Oct 2, 2024 at 10:20 AM Alexandru Pătrănescu <drealecs@gmail.com> wrote:
> > One would be able to get only the enums by using something like: > ```php > function get_declared_enums_only() { > return array_diff(get_declared_classes(), get_declared_enums()); > } > ``` > >
Sorry, I somehow got confused, and I meant to say "to get only the class that are not enums" and it would be like this: ``` function get_declared_class_without_enum() { return array_diff(get_declared_classes(), get_declared_enums()); } ``` -- Alex

Gina P. Banyard

1 year ago
On Tuesday, 1 October 2024 at 23:45, Juliette Reinders Folmer <php-internals_nospam@adviesenzo.nl> wrote: > L.S., > > After the earlier discussion [1] regarding this topic in August, it is our pleasure to present the RFC to add a `get_declared_enums()` function to PHP for discussion. > > https://wiki.php.net/rfc/get_declared_enums > > We look forward to your feedback and hope for a constructive discussion. > > Smile, > Ayesh and Juliette > > 1: https://externals.io/message/124937#124937 The RFC, and its course of action, makes total sense to me. As a counterpoint to the argument presented on the list that Enums should still be returned by get_declared_list() because they are "classes": Interfaces and traits are also "classes" internally, and yet they are not returned. We make a distinction in userland, and this should be followed by the provided functions. Best regards, Gina P. Banyard

Tim Düsterhus

1 year ago
Hi Am 2024-10-03 14:01, schrieb Gina P. Banyard:
> As a counterpoint to the argument presented on the list that Enums > should still be returned by get_declared_list() because they are > "classes": > Interfaces and traits are also "classes" internally, and yet they are > not returned. > We make a distinction in userland, and this should be followed by the > provided functions.
From my userland developer mental model PoV `enum`s are just special classes. Leaving aside Reflection or other introspection functionality and enum object behaves just like a "regular" class object. The same is not true for interfaces and traits, which are structures that help you build a class (including enum classes, which support both interfaces and traits). Best regards Tim Düsterhus

Derick Rethans

1 year ago
On Wed, 2 Oct 2024, Juliette Reinders Folmer wrote:
> L.S., > > After the earlier discussion [1] regarding this topic in August, it is our > pleasure to present the RFC to add a `get_declared_enums()` function to PHP > for discussion. > > https://wiki.php.net/rfc/get_declared_enums > > We look forward to your feedback and hope for a constructive discussion.
My only comment is that I don't think you should have three different votes, but just the single one for the whole proposal. cheers, Derick

Nicolas Grekas

1 year ago
Hi Juliette, L.S.,
> > After the earlier discussion [1] regarding this topic in August, it is our > pleasure to present the RFC to add a `get_declared_enums()` function to PHP > for discussion. > > https://wiki.php.net/rfc/get_declared_enums > > We look forward to your feedback and hope for a constructive discussion. > >
# 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". # 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. 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. 2. 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()?) You've been a rightfully vocal criticizer of BC breaks / deprecations in PHP. Please help the vast part of the community that favors stability 🙏 Nicolas

Tim Düsterhus

1 year ago
Hi Am 2024-10-04 13:44, schrieb Nicolas Grekas:
> # 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".
Agreed.
> # 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.
Also agreed here. I don't think that the RFC lists particularly strong arguments in favor of the breaking change. From what I see the main argument is perceived consistency. But as can be seen in this discussion, it's possible to make the argument of consistency in both directions. I also believe that consistency is a strong argument to make before a feature is introduced to not make PHP any more inconsistent, but once the "damage is done", making *behavioral* changes would just make the damage larger. I'm specifically highlighting *behavioral* here. Deprecating something and then later removing it will very visibly break the code if one ignores the deprecation notice, but when making a behavioral change, code will instead silently misbehave. As Nicolas also mentioned this is especially true for the change to `get_declared_classes()` which would be unable to emit the deprecation notice at all! Best regards Tim Düsterhus

Unnamed Person

1 year ago
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

Larry Garfield

1 year ago
On Sun, Oct 6, 2024, at 2:25 AM, Juliette Reinders Folmer wrote:
> 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.
FWIW, I'd love to have get_declared_symbols() and symbol_exists() functions, regardless of what we do with enums in particular. That's arguably a topic for another RFC, though. (And debatable if functions would count as symbols, but again, topic for another time.) --Larry Garfield

Mike Schinkel

1 year ago
> On Oct 6, 2024, at 3:25 AM, Juliette Reinders Folmer <php-internals_nospam@adviesenzo.nl> wrote: > > 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: >> 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. > >> 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).
Given the argued downsides of the current proposal, would introducing `get_declared_symbols()` + `symbols_exists()` really require deprecating the other functions? It seems adding a `get_current_enums()` and `get_declared_symbols()` + `symbols_exists()` functions would address all the same userland developer needs as the proposed RFC (and more) with the only caveat being `get_current_classes()` remains confusing. To address that one remaining concern why not use add a highly visible recommendation at the top of the `get_current_classes()` docs page informing people to use `get_declared_symbols()` when they need only classes. Is there a reason this approach would not be the best way forward compared to the other options currently being discussed? -Mike P.S. And if it is deemed critically important to get rid of the potential confusion regarding the existing `get_current_classes()` behavior it could be "ultrasoft" deprecated, meaning removed in PHP 11, or just left in forever with a warning (although for the life of me I still do not understand why some people are against leaving a deprecated function in forever, especially for things that are changed for clarity, not because they cause harm by themselves.)