This RFC proposes adding a native values() method to the BackedEnum interface that returns an indexed array of all backing values. The native implementation is added conditionally - only when the enum doesn't already define its own values() method, ensuring zero backward compatibility breaks.
<?php enum Status: string { case Active = 'active'; case Inactive = 'inactive'; case Archived = 'archived'; } // Automatically available - no manual implementation needed: var_dump(Status::values()); // array(3) { [0]=> string(6) "active" [1]=> string(8) "inactive" [2]=> string(8) "archived" } ?>
Common use cases:
$table->enum('status', Status::values())$validator->rule('status', 'in', Status::values())['allowed_statuses' => Status::values()]
Add a native values() static method to the BackedEnum interface that is conditionally registered based on whether the user has already defined it:
<?php interface BackedEnum extends UnitEnum { /** * Returns an indexed array of all backing values for the enum cases. * * This method is automatically available unless the enum defines its own * values() method, in which case the user-defined implementation is used. * * @return int[]|string[] */ public static function values(): array; } ?>
The native implementation is only registered when the enum does not already define a values() method:
<?php // Case 1: No user-defined values() - native implementation added automatically enum Status: string { case Active = 'active'; case Inactive = 'inactive'; } Status::values(); // Native implementation: ['active', 'inactive'] // Case 2: User-defined values() - native implementation NOT added, user's version is used enum Priority: int { case Low = 1; case High = 10; public static function values(): array { // Custom implementation - maybe sorted $values = array_map(fn($c) => $c->value, self::cases()); sort($values); return $values; } } Priority::values(); // User's implementation: [1, 10] (sorted) ?>
This approach ensures:
values() continues working unchangedvalues()When the native implementation is used:
cases())array<int> for int-backed enums, array<string> for string-backed enums[]BackedEnum, not on UnitEnum (pure enums)Basic usage (native implementation):
<?php enum Priority: int { case Low = 1; case Medium = 5; case High = 10; } var_dump(Priority::values()); // array(3) { [0]=> int(1) [1]=> int(5) [2]=> int(10) } ?>
Database migrations:
<?php enum OrderStatus: string { case Pending = 'pending'; case Processing = 'processing'; case Completed = 'completed'; case Cancelled = 'cancelled'; } // Laravel migration Schema::create('orders', function (Blueprint $table) { $table->enum('status', OrderStatus::values()); // ['pending', 'processing', 'completed', 'cancelled'] }); ?>
Form validation:
<?php enum Country: string { case USA = 'us'; case Canada = 'ca'; case Mexico = 'mx'; } // Symfony validation use Symfony\Component\Validator\Constraints as Assert; class Address { #[Assert\Choice(callback: [Country::class, 'values'])] public string $countryCode; } // Laravel validation $validator = Validator::make($data, [ 'country' => ['required', 'in:' . implode(',', Country::values())] ]); ?>
API responses:
<?php enum Feature: string { case BasicPlan = 'basic'; case ProPlan = 'pro'; case EnterprisePlan = 'enterprise'; } // OpenAPI / JSON Schema return response()->json([ 'available_plans' => Feature::values(), // ['basic', 'pro', 'enterprise'] ]); ?>
User-defined implementation (respects custom behavior):
<?php enum Color: string { case Red = 'red'; case Green = 'green'; case Blue = 'blue'; // Custom implementation - returns uppercase values public static function values(): array { return array_map( fn($c) => strtoupper($c->value), self::cases() ); } } var_dump(Color::values()); // array(3) { [0]=> string(3) "RED" [1]=> string(5) "GREEN" [2]=> string(4) "BLUE" } ?>
Library compatibility example:
<?php // Library code that needs to support PHP 8.4+ enum LibraryEnum: string { case Option1 = 'opt1'; case Option2 = 'opt2'; // Defined for PHP 8.4/8.5 compatibility // In PHP 8.6+, this is used instead of native (no conflict) public static function values(): array { return array_map(fn($c) => $c->value, self::cases()); } } // Works in both PHP 8.4 and PHP 8.6+ var_dump(LibraryEnum::values()); // array(2) { [0]=> string(4) "opt1" [1]=> string(4) "opt2" } ?>
Trait-based implementations work unchanged:
<?php trait EnumValues { public static function values(): array { return array_map(fn($c) => $c->value, self::cases()); } } enum Status: string { use EnumValues; // User's trait takes precedence case Draft = 'draft'; case Published = 'published'; } // Uses trait implementation, native is not added var_dump(Status::values()); ?>
Empty enum edge case:
<?php enum EmptyEnum: string {} var_dump(EmptyEnum::values()); // array(0) { } ?>
This RFC introduces ZERO backward compatibility breaks.
The conditional registration approach ensures that:
values() methods continue working unchanged
During enum registration, the engine checks if a values() method already exists:
// Simplified concept (actual implementation in zend_enum.c) if (user_defined_values_exists(enum_class)) { // User has values() - respect their implementation return; } // No user-defined values() - register native implementation register_native_values(enum_class);
This means:
values() works identically in PHP 8.6values() without any codePositive impacts:
values() - continue working (no changes needed)values() without boilerplate
Optional cleanup opportunity:
Over time, projects can optionally remove redundant values() implementations when they drop support for PHP < 8.6:
<?php // PHP 8.4/8.5: Need custom implementation enum Status: string { case Active = 'active'; public static function values(): array { return array_map(fn($c) => $c->value, self::cases()); } } // PHP 8.6+: Can remove (but not required) enum Status: string { case Active = 'active'; // Native values() available automatically } ?>
This approach makes values() the only enum method that can be user-defined:
| Method | User-definable? |
| -------- | ---------------- |
cases() | ❌ No (always native) |
from() | ❌ No (always native) |
tryFrom() | ❌ No (always native) |
values() | ✅ Yes (conditional) |
Rationale for this trade-off:
Future path to full consistency: If desired, a future RFC could:
1. Emit ''E_DEPRECATED'' for user-defined ''values()'' in PHP 8.x 2. Make it non-overridable in PHP 9.0 3. Achieve full consistency with other enum methods
This RFC deliberately prioritizes pragmatic value delivery over perfect API consistency.
Next PHP 8.x (PHP 8.6)
This is a feature addition with zero BC breaks, appropriate for a minor version.
Positive impacts:
values()values() availability more reliablyNo negative impacts:
No impact to existing extensions. This is a core enum feature with no extension dependencies.
No impact. This is a language-level feature that behaves identically across all SAPIs (CLI, FPM, embedded, etc.).
None. The conditional approach addresses the BC concerns raised during initial discussion.
If the community later prefers full consistency (non-overridable values() like other enum methods):
Phase 1 (PHP 8.x): Emit E_DEPRECATED for user-defined values()
<?php enum Status: string { case Active = 'active'; public static function values(): array { ... } // Deprecated: Status::values() is provided natively and should not be redeclared } ?>
Phase 2 (PHP 9.0): Make user-defined values() an error
This gives the ecosystem years to migrate while eventually achieving full API consistency.
This future scope is NOT part of the current RFC - just documenting the possibility.
Future RFC could add optional parameter to control array keys:
<?php // Hypothetical future enhancement (NOT part of this RFC) Status::values(preserveKeys: true); // ['Active' => 'active', 'Inactive' => 'inactive'] Status::values(preserveKeys: false); // ['active', 'inactive'] (default) ?>
This RFC deliberately keeps the API simple with indexed arrays matching cases() behavior.
Future RFCs could add related methods:
names(): Return array of case namestoArray(): Return associative array of name => value pairsThese are intentionally excluded from this RFC to keep scope focused.
This is a simple yes/no vote requiring 2/3 majority as it's a language feature addition.
Vote will open 2 weeks after RFC announcement and remain open for 2 weeks.
Pull Request: https://github.com/php/php-src/pull/20398
Implementation includes:
Zend/zend_enum.c with conditional registration logiczend_enum.stub.php)values())values())Key implementation detail:
The conditional check happens during enum registration:
// In zend_enum_register_funcs() - simplified zend_function *existing = zend_hash_str_find_ptr( &ce->function_table, "values", sizeof("values")-1 ); if (existing && existing->common.scope == ce) { // User defined values() on this enum - respect it return; } // No user-defined values() - register native implementation zend_internal_function *values_function = ...; zend_enum_register_func(ce, ZEND_STR_VALUES, values_function);
All tests pass. Implementation is ready for merge pending RFC approval.
After the RFC is approved, this section will contain:
Research and Evidence:
symfony/symfony/src/Symfony/Component/TypeInfo/TypeIdentifier.phpEnumValuesTrait patternSearch queries performed:
Prior Art:
Object.values(EnumType)[e.value for e in EnumType]values() method (4,900 stars)Related RFCs:
values() method
Initially considered making values() always native and non-overridable (like cases()/from()/tryFrom()).
Rejected because:
Conditional approach chosen instead: Provides benefit without breaking changes.
getValues(): More verbose, doesn't match cases() style
toArray(): Ambiguous - case objects or values? Names or values?
valueList(): Unnecessarily verbose
extractValues(): Too long, unclear
Decision: values() best matches:
cases()
Suggestion to use Status::$values instead of Status::values().
Rejected because:
cases(), from(), tryFrom() (all methods)Suggestion to provide standard library trait instead of native method.
Rejected because:
use statement in every enum (boilerplate persists)
Suggestion that array_column(Status::cases(), 'value') is sufficient.
Rejected because:
Considered allowing customization: values(sorted: true) or values(unique: true).
Rejected because:
array_unique() or sort() if neededcases()<?php // Users can customize as needed $sorted = Status::values(); sort($sorted); $unique = array_unique(Status::values()); ?>
Considered emitting E_DEPRECATED for user-defined values() immediately.
Rejected because: