[PHP-DEV] [RFC] [Discussion] BackedEnum::values() - Seeking feedback before formal RFC

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

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

== 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

The break is justified because:

  1. Behavior is unchanged - native implementation does exactly what users
    already implemented
  2. Migration is trivial - simply remove the redundant method
  3. Precedent exists - PHP 8.1 native enums broke myclabs/php-enum
    (4.9k stars) similarly
  4. Long-term benefit - standardization, discoverability, elimination
    of boilerplate
  5. No alternative - virtual properties are technically infeasible;
    different name doesn’t match community expectations

== Questions for Discussion ==

  1. BC break acceptability: Given the scope and straightforward migration,
    is this break acceptable?

  2. Method name: values() matches community usage (3,860+ examples) and
    parallels cases(). Alternatives like getValues() or toArray() were
    considered but seem inferior. Thoughts?

  3. Target version: Currently targeting PHP 8.6 (master branch). Is this
    appropriate?

  4. Deprecation period: Should we emit E_DEPRECATED in 8.5 and fatal error
    in 9.0? Or accept the break immediately? (Deprecation adds engine
    complexity and delays benefit.)

== Prior Art ==

  • Symfony: Uses this pattern in core components
  • PHP.net Manual: Documents EnumValuesTrait approach
  • TypeScript: Object.values(Enum)
  • Python: [e.value for e in Enum]
  • myclabs/php-enum: Had values() method (4.9k stars)

== Next Steps ==

If feedback is generally positive, I will:

  1. Request RFC karma
  2. Create formal RFC on wiki.php.net
  3. Address any concerns raised in this discussion
  4. Move to formal voting after discussion period

== Implementation Details ==

For those interested in the technical details, the PR includes:

  • Core implementation in zend_enum.c
  • Stub file updates
  • Comprehensive test coverage (9 test files)
  • Reflection support
  • Documentation in NEWS and UPGRADING

PR: https://github.com/php/php-src/pull/20398

Looking forward to your feedback!

Best regards, Savin Mikhail
GitHub: @savinmikhail

Hi Savin,

Thanks for sharing your idea.

On 06.11.25 05:09, 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

Common use cases:
* Database migrations: $table->enum('status', Status::values())
* Form validation: $validator->rule('status', 'in', Status::values())
* API responses: ['allowed_statuses' => Status::values()]

I agree, this is a common feature I would like as well.

== 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

The break is justified because:

1. Behavior is unchanged - native implementation does exactly what users
already implemented
2. Migration is trivial - simply remove the redundant method
3. Precedent exists - PHP 8.1 native enums broke myclabs/php-enum
(4.9k stars) similarly
4. Long-term benefit - standardization, discoverability, elimination
of boilerplate
5. No alternative - virtual properties are technically infeasible;
different name doesn't match community expectations

Here I don't agree!

PHP application especially libraries and frameworks very often have to support multiple PHP versions. They can't just remove an already implemented function and drop support for all previous PHP versions at once.

So either, the new function will not be final - to allow getting overwritten by current implementations - in this case you would need to run another analytics to check for naming clashes with different incompatible signature or behavior.

Or it needs to kind of deprecation period or another name.

Just my 2 cents

Marc

On 6. Nov 2025, at 11:09, Mikhail Savin mikhail.d.savin@gmail.com 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

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

== 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

The break is justified because:

  1. Behavior is unchanged - native implementation does exactly what users
    already implemented
  2. Migration is trivial - simply remove the redundant method
  3. Precedent exists - PHP 8.1 native enums broke myclabs/php-enum
    (4.9k stars) similarly
  4. Long-term benefit - standardization, discoverability, elimination
    of boilerplate
  5. No alternative - virtual properties are technically infeasible;
    different name doesn’t match community expectations

== Questions for Discussion ==

  1. BC break acceptability: Given the scope and straightforward migration,
    is this break acceptable?

  2. Method name: values() matches community usage (3,860+ examples) and
    parallels cases(). Alternatives like getValues() or toArray() were
    considered but seem inferior. Thoughts?

  3. Target version: Currently targeting PHP 8.6 (master branch). Is this
    appropriate?

  4. Deprecation period: Should we emit E_DEPRECATED in 8.5 and fatal error
    in 9.0? Or accept the break immediately? (Deprecation adds engine
    complexity and delays benefit.)

== Prior Art ==

  • Symfony: Uses this pattern in core components
  • PHP.net Manual: Documents EnumValuesTrait approach
  • TypeScript: Object.values(Enum)
  • Python: [e.value for e in Enum]
  • myclabs/php-enum: Had values() method (4.9k stars)

== Next Steps ==

If feedback is generally positive, I will:

  1. Request RFC karma
  2. Create formal RFC on wiki.php.net
  3. Address any concerns raised in this discussion
  4. Move to formal voting after discussion period

== Implementation Details ==

For those interested in the technical details, the PR includes:

  • Core implementation in zend_enum.c
  • Stub file updates
  • Comprehensive test coverage (9 test files)
  • Reflection support
  • Documentation in NEWS and UPGRADING

PR: https://github.com/php/php-src/pull/20398

Looking forward to your feedback!

Best regards, Savin Mikhail
GitHub: @savinmikhail

Hey,

Thanks for this! I would love to see this in core.

  1. Method name: values() matches community usage (3,860+ examples) and
    parallels cases(). Alternatives like getValues() or toArray() were
    considered but seem inferior. Thoughts?

values()

  1. BC break acceptability: Given the scope and straightforward migration,
    is this break acceptable?
  1. Target version: Currently targeting PHP 8.6 (master branch). Is this
    appropriate?

  2. Deprecation period: Should we emit E_DEPRECATED in 8.5 and fatal error
    in 9.0? Or accept the break immediately? (Deprecation adds engine
    complexity and delays benefit.)

Perhaps the smoothest way would be to allow from 8.6 redeclaring values(), in the same time deprecate it and break it later (9.0)?

Cheers

On Thu, 6 Nov 2025 at 05:35, Marc B. <marc@mabe.berlin> wrote:

Hi Savin,

Thanks for sharing your idea.

On 06.11.25 05:09, 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
>
> Common use cases:
> * Database migrations: $table->enum('status', Status::values())
> * Form validation: $validator->rule('status', 'in', Status::values())
> * API responses: ['allowed_statuses' => Status::values()]

I agree, this is a common feature I would like as well.

>
> == 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
>
> The break is justified because:
>
> 1. Behavior is unchanged - native implementation does exactly what users
> already implemented
> 2. Migration is trivial - simply remove the redundant method
> 3. Precedent exists - PHP 8.1 native enums broke myclabs/php-enum
> (4.9k stars) similarly
> 4. Long-term benefit - standardization, discoverability, elimination
> of boilerplate
> 5. No alternative - virtual properties are technically infeasible;
> different name doesn't match community expectations

Here I don't agree!

PHP application especially libraries and frameworks very often have to
support multiple PHP versions. They can't just remove an already
implemented function and drop support for all previous PHP versions at once.

So either, the new function will not be final - to allow getting
overwritten by current implementations - in this case you would need to
run another analytics to check for naming clashes with different
incompatible signature or behavior.

Or it needs to kind of deprecation period or another name.

Just my 2 cents

Marc

I think it would be ok to break it but only in a major version like
PHP 9. No deprecation needed.

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: PHP: Traits - Manual)

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:
Add values() Method to BackedEnum by savinmikhail · Pull Request #20398 · php/php-src · GitHub

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

чт, 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

пт, 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:

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

пт, 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:

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)?

Hi Mikhail

On Fri, Nov 7, 2025 at 9:22 AM Mikhail Savin <mikhail.d.savin@gmail.com> wrote:

I am going to create RFC page, cuz badge "Status: requires RFC" was added to PR: Add values() Method to BackedEnum by savinmikhail · Pull Request #20398 · php/php-src · GitHub
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)?

RFC karma was granted. Good luck with the RFC.

Ilija