Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ PHP NEWS
request. (ilutov)
. It is now possible to use reference assign on WeakMap without the key
needing to be present beforehand. (nielsdos)
. Added BackedEnum::values() to retrieve an indexed array of all backing
values for enum cases. (RFC: Add values() Method to BackedEnum)

- Hash:
. Upgrade xxHash to 0.8.2. (timwolla)
Expand Down
2 changes: 2 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ PHP 8.6 UPGRADE NOTES
- Core:
. It is now possible to use reference assign on WeakMap without the key
needing to be present beforehand.
. Added BackedEnum::values() static method to retrieve an indexed array of
all backing values for enum cases.

- Intl:
. Added IntlNumberRangeFormatter class to format an interval of two numbers with a given skeleton, locale, IntlNumberRangeFormatter::COLLAPSE_AUTO, IntlNumberRangeFormatter::COLLAPSE_NONE, IntlNumberRangeFormatter::COLLAPSE_UNIT, IntlNumberRangeFormatter::COLLAPSE_ALL collapse and
Expand Down
14 changes: 14 additions & 0 deletions Zend/tests/enum/backed-values-empty.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--TEST--
Backed enums: values() on empty enum returns []
--FILE--
<?php

enum A: string {}

var_dump(A::values());

?>
--EXPECT--
array(0) {
}

20 changes: 20 additions & 0 deletions Zend/tests/enum/backed-values-ignore-regular-consts.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
--TEST--
BackedEnum::values() only returns case values, not regular constants
--FILE--
<?php
enum Mixed_: string {
case A = 'a';
case B = 'b';

public const REGULAR_CONST = 'not_a_case';
}
var_dump(Mixed_::values());
?>
--EXPECT--
array(2) {
[0]=>
string(1) "a"
[1]=>
string(1) "b"
}

24 changes: 24 additions & 0 deletions Zend/tests/enum/backed-values-int.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--TEST--
Backed enums: values() returns ints
--FILE--
<?php

enum Priority: int {
case Low = 1;
case Medium = 5;
case High = 10;
}

var_dump(Priority::values());

?>
--EXPECT--
array(3) {
[0]=>
int(1)
[1]=>
int(5)
[2]=>
int(10)
}

15 changes: 15 additions & 0 deletions Zend/tests/enum/backed-values-not-on-pure.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
Unit enums do not have values()
--FILE--
<?php

enum U {
case A;
}

var_dump(method_exists(U::class, 'values'));

?>
--EXPECT--
bool(false)

21 changes: 21 additions & 0 deletions Zend/tests/enum/backed-values-order.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
BackedEnum::values() preserves declaration order
--FILE--
<?php
enum Order: int {
case Third = 3;
case First = 1;
case Second = 2;
}
var_dump(Order::values());
?>
--EXPECT--
array(3) {
[0]=>
int(3)
[1]=>
int(1)
[2]=>
int(2)
}

24 changes: 24 additions & 0 deletions Zend/tests/enum/backed-values-string.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--TEST--
Backed enums: values() returns strings
--FILE--
<?php

enum Color: string {
case Red = 'red';
case Green = 'green';
case Blue = 'blue';
}

var_dump(Color::values());

?>
--EXPECT--
array(3) {
[0]=>
string(3) "red"
[1]=>
string(5) "green"
[2]=>
string(4) "blue"
}

23 changes: 23 additions & 0 deletions Zend/tests/enum/backed-values-user-defined.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
Backed enums: user-defined values() overrides native
--FILE--
<?php

enum E: int {
case A = 1;
case B = 2;

public static function values(): array {
return ['custom'];
}
}

var_dump(E::values());

?>
--EXPECT--
array(1) {
[0]=>
string(6) "custom"
}

48 changes: 43 additions & 5 deletions Zend/zend_enum.c
Original file line number Diff line number Diff line change
Expand Up @@ -410,9 +410,33 @@ static ZEND_NAMED_FUNCTION(zend_enum_from_func)

static ZEND_NAMED_FUNCTION(zend_enum_try_from_func)
{
zend_enum_from_base(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
zend_enum_from_base(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}

static ZEND_NAMED_FUNCTION(zend_enum_values_func)
{
zend_class_entry *ce = EX(func)->common.scope;
zend_class_constant *c;

ZEND_PARSE_PARAMETERS_NONE();

array_init(return_value);

ZEND_HASH_MAP_FOREACH_PTR(CE_CONSTANTS_TABLE(ce), c) {
if (!(ZEND_CLASS_CONST_FLAGS(c) & ZEND_CLASS_CONST_IS_CASE)) {
continue;
}
zval *zv = &c->value;
if (Z_TYPE_P(zv) == IS_CONSTANT_AST) {
if (zval_update_constant_ex(zv, c->ce) == FAILURE) {
RETURN_THROWS();
}
}
zval *prop = zend_enum_fetch_case_value(Z_OBJ_P(zv));
Z_TRY_ADDREF_P(prop);
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), prop);
} ZEND_HASH_FOREACH_END();
}
static void zend_enum_register_func(zend_class_entry *ce, zend_known_string_id name_id, zend_internal_function *zif) {
zend_string *name = ZSTR_KNOWN(name_id);
zif->type = ZEND_INTERNAL_FUNCTION;
Expand Down Expand Up @@ -469,6 +493,19 @@ void zend_enum_register_funcs(zend_class_entry *ce)
try_from_function->required_num_args = 1;
try_from_function->arg_info = (zend_internal_arg_info *) (arginfo_class_BackedEnum_tryFrom + 1);
zend_enum_register_func(ce, ZEND_STR_TRYFROM_LOWERCASE, try_from_function);

/* Avoid BC break: if user already declared values(), do not register native */
if (!zend_hash_exists(&ce->function_table, ZSTR_KNOWN(ZEND_STR_VALUES))) {
zend_internal_function *values_function = zend_arena_calloc(&CG(arena), sizeof(zend_internal_function), 1);
values_function->handler = zend_enum_values_func;
values_function->function_name = ZSTR_KNOWN(ZEND_STR_VALUES);
values_function->fn_flags = fn_flags;
values_function->doc_comment = NULL;
values_function->num_args = 0;
values_function->required_num_args = 0;
values_function->arg_info = (zend_internal_arg_info *) (arginfo_class_BackedEnum_values + 1);
zend_enum_register_func(ce, ZEND_STR_VALUES, values_function);
}
}
}

Expand All @@ -495,10 +532,11 @@ static const zend_function_entry unit_enum_methods[] = {
};

static const zend_function_entry backed_enum_methods[] = {
ZEND_NAMED_ME(cases, zend_enum_cases_func, arginfo_class_UnitEnum_cases, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
ZEND_NAMED_ME(from, zend_enum_from_func, arginfo_class_BackedEnum_from, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
ZEND_NAMED_ME(tryFrom, zend_enum_try_from_func, arginfo_class_BackedEnum_tryFrom, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
ZEND_FE_END
ZEND_NAMED_ME(cases, zend_enum_cases_func, arginfo_class_UnitEnum_cases, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
ZEND_NAMED_ME(from, zend_enum_from_func, arginfo_class_BackedEnum_from, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
ZEND_NAMED_ME(tryFrom, zend_enum_try_from_func, arginfo_class_BackedEnum_tryFrom, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
ZEND_NAMED_ME(values, zend_enum_values_func, arginfo_class_BackedEnum_values, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
ZEND_FE_END
};

ZEND_API zend_class_entry *zend_register_internal_enum(
Expand Down
7 changes: 7 additions & 0 deletions Zend/zend_enum.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,11 @@ interface BackedEnum extends UnitEnum
public static function from(int|string $value): static;

public static function tryFrom(int|string $value): ?static;

/**
* Returns an indexed array of all backing values for the enum cases.
*
* @return int[]|string[]
*/
public static function values(): array;
}
5 changes: 4 additions & 1 deletion Zend/zend_enum_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Zend/zend_string.h
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,7 @@ EMPTY_SWITCH_DEFAULT_CASE()
_(ZEND_STR_CASES, "cases") \
_(ZEND_STR_FROM, "from") \
_(ZEND_STR_TRYFROM, "tryFrom") \
_(ZEND_STR_VALUES, "values") \
_(ZEND_STR_TRYFROM_LOWERCASE, "tryfrom") \
_(ZEND_STR_AUTOGLOBAL_SERVER, "_SERVER") \
_(ZEND_STR_AUTOGLOBAL_ENV, "_ENV") \
Expand Down
17 changes: 17 additions & 0 deletions ext/reflection/tests/BackedEnum_values_reflection.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
--TEST--
BackedEnum::values() appears in reflection
--FILE--
<?php
enum Status: string {
case Active = 'active';
}
$method = new ReflectionMethod(Status::class, 'values');
var_dump($method->isStatic());
var_dump($method->isPublic());
var_dump($method->getNumberOfParameters());
?>
--EXPECT--
bool(true)
bool(true)
int(0)

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
--TEST--
BackedEnum: reflection shows whether values() is native or user-defined
--FILE--
<?php

// Native implementation
enum NativeEnum: string {
case A = 'a';
}

// User-defined implementation
enum UserEnum: string {
case B = 'b';

public static function values(): array {
return array_map(fn($c) => $c->value, self::cases());
}
}

$nativeMethod = new ReflectionMethod(NativeEnum::class, 'values');
$userMethod = new ReflectionMethod(UserEnum::class, 'values');

echo "Native is internal: " . ($nativeMethod->isInternal() ? 'yes' : 'no') . "\n";
echo "User is internal: " . ($userMethod->isInternal() ? 'yes' : 'no') . "\n";

?>
--EXPECT--
Native is internal: yes
User is internal: no
18 changes: 16 additions & 2 deletions ext/reflection/tests/ReflectionEnum_toString_backed_int.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Enum [ <user> enum MyBool: int implements MyStringable, UnitEnum, BackedEnum ] {
- Static properties [0] {
}

- Static methods [3] {
- Static methods [4] {
Method [ <internal, prototype UnitEnum> static public method cases ] {

- Parameters [0] {
Expand All @@ -66,6 +66,13 @@ Enum [ <user> enum MyBool: int implements MyStringable, UnitEnum, BackedEnum ] {
}
- Return [ ?static ]
}

Method [ <internal, prototype BackedEnum> static public method values ] {

- Parameters [0] {
}
- Return [ array ]
}
}

- Properties [2] {
Expand Down Expand Up @@ -99,7 +106,7 @@ Enum [ <user> enum MyBool: int implements MyStringable, UnitEnum, BackedEnum ] {
- Static properties [0] {
}

- Static methods [3] {
- Static methods [4] {
Method [ <internal, prototype UnitEnum> static public method cases ] {

- Parameters [0] {
Expand All @@ -122,6 +129,13 @@ Enum [ <user> enum MyBool: int implements MyStringable, UnitEnum, BackedEnum ] {
}
- Return [ ?static ]
}

Method [ <internal, prototype BackedEnum> static public method values ] {

- Parameters [0] {
}
- Return [ array ]
}
}

- Properties [2] {
Expand Down
Loading