Re: [RFC] [Discussion] BackedEnum::values() - Seeking feedback before formal RFC
| From: | Mikhail Savin | Date: | Fri, 07 Nov 2025 08:22:10 +0000 |
| Subject: | Re: [RFC] [Discussion] BackedEnum::values() - Seeking feedback before formal RFC | ||
| References: | 1 2 3 4 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-129127@lists.php.net to get a copy of this message | ||
пт, 7 нояб. 2025 г. в 07:53, Mikhail Savin <mikhail.d.savin@gmail.com>:
>
>
> пт, 7 нояб. 2025 г. в 07:08, Valentin Udaltsov <
> udaltsov.valentin@gmail.com>:
>
>> чт, 6 нояб. 2025 г., 19:30 Larry Garfield <larry@garfieldtech.com>:
>>
>>> On Wed, Nov 5, 2025, at 10:09 PM, Mikhail Savin wrote:
>>> > Hi internals,
>>> >
>>> > I would like to propose adding a native values() method to the
>>> BackedEnum
>>> > interface that returns an array of all backing values. Before creating
>>> a
>>> > formal RFC, I'm seeking feedback on the concept and approach.
>>> >
>>> > == Summary ==
>>> >
>>> > The proposal adds:
>>> >
>>> > interface BackedEnum {
>>> > public static function values(): array;
>>> > }
>>> >
>>> > This would allow:
>>> >
>>> > enum Status: string {
>>> > case Active = 'active';
>>> > case Inactive = 'inactive';
>>> > }
>>> >
>>> > Status::values(); // ['active', 'inactive']
>>> >
>>> > == Motivation ==
>>> >
>>> > This pattern is extremely common in the wild. Based on GitHub code
>>> search:
>>> >
>>> > * ~3,860+ direct implementations of this exact pattern
>>> > * ~20,000-40,000 estimated real usage when accounting for shared
>>> traits
>>> > * Used in major frameworks: Symfony core (TypeIdentifier.php),
>>> > Laravel ecosystem
>>> > * Documented by PHP.net: The manual itself shows EnumValuesTrait as
>>> > an example
>>>
>>> Correction: The manual does not show an EnumValuesTrait that I can
>>> find. There is a *comment* in the manual that includes this method, but
>>> that's not part of the manual proper, and frankly 90% of comments in the
>>> manual should be removed. (cf:
>>>
>>> https://www.php.net/manual/en/language.enumerations.traits.php#129250)
>>>
>>> > Common use cases:
>>> > * Database migrations: $table->enum('status', Status::values())
>>> > * Form validation: $validator->rule('status', 'in',
>>> > Status::values())
>>> > * API responses: ['allowed_statuses' => Status::values()]
>>> >
>>> > == Implementation ==
>>> >
>>> > I have a working implementation with tests:
>>> > https://github.com/php/php-src/pull/20398
>>> >
>>> > The implementation:
>>> > * Mirrors the existing cases() method structure
>>> > * Extracts the value property from each case
>>> > * Returns an indexed array (0, 1, 2, ...)
>>> > * Only available on BackedEnum, not UnitEnum
>>> > * All tests pass
>>>
>>> I am unclear why this is a major advantage over
>>> array_column(Status::cases(), 'value');
>>>
>>> > == Backward Compatibility - Important Discussion Point ==
>>> >
>>> > This is a breaking change. Enums that already define a values() method
>>> > will fail with:
>>> >
>>> > Fatal error: Cannot redeclare BackedEnum::values()
>>> >
>>> > Based on ecosystem research:
>>> > * ~24,000-44,000 enum instances will break
>>> > * All implementations are functionally identical to what's being
>>> proposed
>>> > * Migration is mechanical: just delete the user-defined method
>>>
>>> This is a hard-stop. There are hundreds of thousands of packages in the
>>> wild that need to support multiple PHP versions. Nearly all packaglist
>>> packages (which I presume is where you're drawing the research from; either
>>> that or GitHub which will give a similar result set) support at least two
>>> consecutive versions, if not 4, 5, or 6.
>>>
>>> A hard break like this would essentially mean the packages containing
>>> those 40,000 enums would be unable to support both PHP 8.5 and 8.6 at the
>>> same time. That's simply not an acceptable impact on the ecosystem,
>>> regardless of how nice the feature may or may not be.
>>>
>>> --Larry Garfield
>>>
>>
>> We could add a virtual $values property. Since enum properties are not
>> allowed in userland, it will not break any existing code.
>>
>> —
>> Valentin
>>
>
> Hi all,
>
> Thank you for the thoughtful feedback. Based on the discussion so far, the
> consensus seems to be: "the feature is useful, but the BC break is too
> large."
>
> To address this, I've adjusted the proposal so that user code is allowed
> to
> redeclare values() on backed enums. This keeps existing projects working
> unchanged while providing the native implementation for new code.
>
> The native values() will only be added when not already defined:
>
> ```c
> if (!zend_hash_exists(&ce->function_table, ZSTR_KNOWN(ZEND_STR_VALUES)))
> {
> ...
> }
> ```
>
> Result:
> * Zero BC break - existing code unchanged
> * New enums get values() automatically
> * Libraries can maintain their implementation for older PHP support
>
> Trade-off:
> I recognize this makes values() the only overridable enum intrinsic
> (unlike cases/from/tryFrom). I'll document this clearly and add tests
> to lock down the behavior. If needed, we can deprecate user-defined
> values() in a later 8.x and make it an error in PHP 9.0.
>
> Questions:
> 1. Is allowing values() override technically acceptable?
> 2. Is documenting the inconsistency sufficient?
> 3. Should we add deprecation?
> 4. Can I submit RFC for this feature?
> 5. Should I rather implement it via virtual property, as Valentin
> suggested above?
>
> I updated the PR, and also added a few tests for this behavior.
>
> Thoughts?
>
> Best regards,
> Savin Mikhail
>
Hi
I am going to create RFC page, cuz badge "Status: requires RFC" was added
to PR: https://github.com/php/php-src/pull/20398
Could someone please grant me RFC wiki karma so I can create the page
https://wiki.php.net/rfc/add_values_method_to_backed_enum
from my account
(login - msavin)?