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