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:
- Behavior is unchanged - native implementation does exactly what users
already implemented - Migration is trivial - simply remove the redundant method
- Precedent exists - PHP 8.1 native enums broke myclabs/php-enum
(4.9k stars) similarly - Long-term benefit - standardization, discoverability, elimination
of boilerplate - No alternative - virtual properties are technically infeasible;
different name doesn’t match community expectations
== Questions for Discussion ==
-
BC break acceptability: Given the scope and straightforward migration,
is this break acceptable? -
Method name: values() matches community usage (3,860+ examples) and
parallels cases(). Alternatives like getValues() or toArray() were
considered but seem inferior. Thoughts? -
Target version: Currently targeting PHP 8.6 (master branch). Is this
appropriate? -
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:
- Request RFC karma
- Create formal RFC on wiki.php.net
- Address any concerns raised in this discussion
- 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