Summary
Introduce a native BackedEnum::values() static method that returns the list of all backing values (int|string) of a backed enum’s cases in declaration order. This eliminates boilerplate commonly implemented across projects and aligns with the existing cases() API.
Target: master (PHP 8.6)
Motivation
The ecosystem repeatedly implements the same pattern to produce an array of enum values:
enum Status: string {
case Draft = 'draft';
case Published = 'published';
// Boilerplate implemented in countless codebases today
public static function values(): array {
return array_map(fn($case) => $case->value, self::cases());
}
}
This pattern appears widely across GitHub and frameworks, often implemented directly or via traits (which hides usage from code search). A conservative summary of real-world evidence:
Quantitative (code search + trait multiplier):
| Pattern |
GitHub search |
Results |
array_map(fn($case) => $case->value, self::cases()) |
click |
~330 |
return array_map + self::cases() + ->value |
click |
~1,900 |
return array_map + fn($case) => $case->value |
click |
~324 |
function values() + return array_map + self::cases() |
click |
~784 |
function toArray() + array_map + self::cases() + ->value |
click |
~236 |
function getValues() + array_map + self::cases() |
click |
~196 |
function values() + foreach + self::cases() + [] = $ + ->value |
click |
~90 |
| Total |
— |
~3,860 |
Trait pattern multiplier:
- Many projects factor this into a trait (e.g.,
EnumValuesTrait) and then use it in dozens of enums, so direct search counts significantly understate usage.
- PHP manual example shows trait approach
Qualitative (frameworks & libraries):
Providing a native values() method:
- Removes boilerplate and fragmentation (different traits/implementations).
- Improves discoverability and consistency (parallels
cases() nicely).
- Simplifies migrations from legacy enum libraries and existing project traits.
Proposal
Add the following method to the BackedEnum interface:
interface BackedEnum extends UnitEnum
{
public static function values(): array;
}
Semantics:
- Available only for backed enums (int|string). Not available on unit enums.
- Returns a 0-based, indexed array of the backing values in declaration order.
- For int-backed enums, returns
array<int>; for string-backed, array<string>.
Examples:
enum Priority: int { case Low = 1; case High = 10; }
Priority::values(); // [1, 10]
enum Color: string { case Red = 'red'; case Blue = 'blue'; }
Color::values(); // ['red', 'blue']
Implementation Details
Engine changes (Zend):
- Add stub and arginfo
Zend/zend_enum.stub.php: add BackedEnum::values() signature.
Zend/zend_enum_arginfo.h: regenerated (committed) to include arginfo_class_BackedEnum_values.
- Add interned string identifier
Zend/zend_string.h: add ZEND_STR_VALUES ("values").
- Implement and register the method
Zend/zend_enum.c:
- Implement
zend_enum_values_func(), mirroring cases() but extracting the value property of each case object.
- Register for backed enums in both the internal method table and
zend_enum_register_funcs() (user classes).
Tests:
- Reflection: ensure the method appears on backed enums
ext/reflection/tests/BackedEnum_values_reflection.phpt
- Update toString expectations for backed enums:
ext/reflection/tests/ReflectionEnum_toString_backed_int.phpt
ext/reflection/tests/ReflectionEnum_toString_backed_string.phpt
- Enum behavior tests
Zend/tests/enum/backed-values-int.phpt
Zend/tests/enum/backed-values-string.phpt
Zend/tests/enum/backed-values-empty.phpt
Zend/tests/enum/backed-values-order.phpt
Zend/tests/enum/backed-values-not-on-pure.phpt
Zend/tests/enum/backed-values-ignore-regular-consts.phpt
Documentation in-tree:
NEWS: announce the feature under Core.
UPGRADING: list under "New Features".
Manual (php/doc-en) to be added in a separate PR:
- BackedEnum interface page:
values() signature, description, examples.
- Enumerations guide: mention
values() alongside cases()/from()/tryFrom().
- Migration guide for 8.6: add an entry for
BackedEnum::values().
Backward Compatibility
No BC break.
- The engine registers the native
BackedEnum::values() only if the enum does not already declare a userland static values() method.
- If an enum (or a trait it uses) defines
values(), that method is preserved and continues to work unchanged.
- Reflection continues to show a
values() method; either the native one or the user-defined one.
- Unit enums remain unaffected; they do not expose
values().
Compatibility notes:
- Projects may optionally remove duplicate userland implementations and rely on the native method, but no migration is required.
- Behavior of existing custom
values() implementations is preserved; the engine does not override or redeclare them.
Thanks, @vudaltsov, for highlighting the risks—resolved by conditional registration.
Optional Cleanup
Projects that wish to standardize on the native API can remove their custom values() implementations and rely on BackedEnum::values(); behavior will remain the same.
Mitigations Considered
- Conditional registration (chosen): skip registering the native method if userland already defines
values(); preserves BC while providing a standard API where missing.
- Different method name (e.g.,
getValues(), valueList()): avoids collision but diverges from established conventions and symmetry with cases().
- Phased rollout (deprecation first): unnecessary given the conditional approach above.
Performance
- O(n) over the number of cases, similar to:
array_map(fn($case) => $case->value, self::cases())
- Native implementation avoids userland call overhead and enables future internal optimizations if needed.
Alternatives Considered
- Keep status quo: leaves significant, repeated boilerplate across the ecosystem.
- Traits / helpers in userland: remains fragmented and non-discoverable.
- Different naming (
toArray(), getValues()): ambiguous or verbose; values() best matches existing community conventions and parallels cases().
Prior Art
No previous RFCs proposing a BackedEnum::values() method were found.
A review of the PHP RFC index shows several enum-related proposals:
However, none of them address adding a convenience method returning the list of backing values.
This proposal introduces it for the first time.
Checklist
Thank you for reviewing!
Uh oh!
There was an error while loading. Please reload this page.
Summary
Introduce a native
BackedEnum::values()static method that returns the list of all backing values (int|string) of a backed enum’s cases in declaration order. This eliminates boilerplate commonly implemented across projects and aligns with the existingcases()API.Target: master (PHP 8.6)
Motivation
The ecosystem repeatedly implements the same pattern to produce an array of enum values:
This pattern appears widely across GitHub and frameworks, often implemented directly or via traits (which hides usage from code search). A conservative summary of real-world evidence:
Quantitative (code search + trait multiplier):
array_map(fn($case) => $case->value, self::cases())return array_map+self::cases()+->valuereturn array_map+fn($case) => $case->valuefunction values()+return array_map+self::cases()function toArray()+array_map+self::cases()+->valuefunction getValues()+array_map+self::cases()function values()+foreach+self::cases()+[] = $+->valueTrait pattern multiplier:
EnumValuesTrait) and thenuseit in dozens of enums, so direct search counts significantly understate usage.Qualitative (frameworks & libraries):
Providing a native
values()method:cases()nicely).Proposal
Add the following method to the
BackedEnuminterface:Semantics:
array<int>; for string-backed,array<string>.Examples:
Implementation Details
Engine changes (Zend):
Zend/zend_enum.stub.php: addBackedEnum::values()signature.Zend/zend_enum_arginfo.h: regenerated (committed) to includearginfo_class_BackedEnum_values.Zend/zend_string.h: addZEND_STR_VALUES("values").Zend/zend_enum.c:zend_enum_values_func(), mirroringcases()but extracting thevalueproperty of each case object.zend_enum_register_funcs()(user classes).Tests:
ext/reflection/tests/BackedEnum_values_reflection.phptext/reflection/tests/ReflectionEnum_toString_backed_int.phptext/reflection/tests/ReflectionEnum_toString_backed_string.phptZend/tests/enum/backed-values-int.phptZend/tests/enum/backed-values-string.phptZend/tests/enum/backed-values-empty.phptZend/tests/enum/backed-values-order.phptZend/tests/enum/backed-values-not-on-pure.phptZend/tests/enum/backed-values-ignore-regular-consts.phptDocumentation in-tree:
NEWS: announce the feature under Core.UPGRADING: list under "New Features".Manual (php/doc-en) to be added in a separate PR:
values()signature, description, examples.values()alongsidecases()/from()/tryFrom().BackedEnum::values().Backward Compatibility
No BC break.
BackedEnum::values()only if the enum does not already declare a userland staticvalues()method.values(), that method is preserved and continues to work unchanged.values()method; either the native one or the user-defined one.values().Compatibility notes:
values()implementations is preserved; the engine does not override or redeclare them.Thanks, @vudaltsov, for highlighting the risks—resolved by conditional registration.
Optional Cleanup
Projects that wish to standardize on the native API can remove their custom
values()implementations and rely onBackedEnum::values(); behavior will remain the same.Mitigations Considered
values(); preserves BC while providing a standard API where missing.getValues(),valueList()): avoids collision but diverges from established conventions and symmetry withcases().Performance
Alternatives Considered
toArray(),getValues()): ambiguous or verbose;values()best matches existing community conventions and parallelscases().Prior Art
No previous RFCs proposing a
BackedEnum::values()method were found.A review of the PHP RFC index shows several enum-related proposals:
However, none of them address adding a convenience method returning the list of backing values.
This proposal introduces it for the first time.
Checklist
Thank you for reviewing!