Skip to content

Commit 9549133

Browse files
committed
Adjust enum reflection according to the RFC
1 parent e20423c commit 9549133

8 files changed

+155
-59
lines changed

ext/reflection/php_reflection.c

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ PHPAPI zend_class_entry *reflection_reference_ptr;
9090
PHPAPI zend_class_entry *reflection_attribute_ptr;
9191
PHPAPI zend_class_entry *reflection_enum_ptr;
9292
PHPAPI zend_class_entry *reflection_enum_unit_case_ptr;
93+
PHPAPI zend_class_entry *reflection_enum_backed_case_ptr;
9394

9495
/* Exception throwing macro */
9596
#define _DO_THROW(msg) \
@@ -1448,11 +1449,14 @@ static void reflection_class_constant_factory(zend_string *name_str, zend_class_
14481449
}
14491450
/* }}} */
14501451

1451-
static void reflection_enum_case_factory(zend_string *name_str, zend_class_constant *constant, zval *object)
1452+
static void reflection_enum_case_factory(zend_class_entry *ce, zend_string *name_str, zend_class_constant *constant, zval *object)
14521453
{
14531454
reflection_object *intern;
14541455

1455-
reflection_instantiate(reflection_enum_unit_case_ptr, object);
1456+
zend_class_entry *case_reflection_class = ce->backed_enum_table == IS_UNDEF
1457+
? reflection_enum_unit_case_ptr
1458+
: reflection_enum_backed_case_ptr;
1459+
reflection_instantiate(case_reflection_class, object);
14561460
intern = Z_REFLECTION_P(object);
14571461
intern->ptr = constant;
14581462
intern->ref_type = REF_TYPE_CLASS_CONSTANT;
@@ -6595,7 +6599,7 @@ ZEND_METHOD(ReflectionEnum, getCase)
65956599
RETURN_FALSE;
65966600
}
65976601

6598-
reflection_enum_case_factory(name, constant, return_value);
6602+
reflection_enum_case_factory(ce, name, constant, return_value);
65996603
}
66006604

66016605
ZEND_METHOD(ReflectionEnum, getCases)
@@ -6615,7 +6619,7 @@ ZEND_METHOD(ReflectionEnum, getCases)
66156619
ZEND_HASH_FOREACH_STR_KEY_PTR(&ce->constants_table, name, constant) {
66166620
if (Z_ACCESS_FLAGS(constant->value) & ZEND_CLASS_CONST_IS_CASE) {
66176621
zval class_const;
6618-
reflection_enum_case_factory(name, constant, &class_const);
6622+
reflection_enum_case_factory(ce, name, constant, &class_const);
66196623
zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &class_const);
66206624
}
66216625
} ZEND_HASH_FOREACH_END();
@@ -6671,7 +6675,7 @@ ZEND_METHOD(ReflectionEnumUnitCase, __construct)
66716675
}
66726676
}
66736677

6674-
ZEND_METHOD(ReflectionEnumUnitCase, getBackingValue)
6678+
ZEND_METHOD(ReflectionEnumBackedCase, getBackingValue)
66756679
{
66766680
reflection_object *intern;
66776681
zend_class_constant *ref;
@@ -6685,10 +6689,7 @@ ZEND_METHOD(ReflectionEnumUnitCase, getBackingValue)
66856689
zval_update_constant_ex(&ref->value, ref->ce);
66866690
}
66876691

6688-
if (intern->ce->enum_backing_type == IS_UNDEF) {
6689-
RETURN_NULL();
6690-
}
6691-
6692+
ZEND_ASSERT(intern->ce->enum_backing_type != IS_UNDEF);
66926693
zval *member_p = zend_enum_fetch_case_value(Z_OBJ(ref->value));
66936694

66946695
ZVAL_COPY_OR_DUP(return_value, member_p);
@@ -6707,6 +6708,24 @@ ZEND_METHOD(ReflectionEnumUnitCase, getEnum)
67076708
zend_reflection_enum_factory(ref->ce, return_value);
67086709
}
67096710

6711+
ZEND_METHOD(ReflectionEnumBackedCase, __construct)
6712+
{
6713+
ZEND_MN(ReflectionEnumUnitCase___construct)(INTERNAL_FUNCTION_PARAM_PASSTHRU);
6714+
6715+
reflection_object *intern;
6716+
zend_class_constant *ref;
6717+
6718+
GET_REFLECTION_OBJECT_PTR(ref);
6719+
6720+
if (ref->ce->enum_backing_type == IS_UNDEF) {
6721+
if (!EG(exception)) {
6722+
zval *case_name = reflection_prop_name(ZEND_THIS);
6723+
zend_throw_exception_ex(reflection_exception_ptr, 0, "Enum case %s::%s is not a backed case", ZSTR_VAL(ref->ce->name), Z_STRVAL_P(case_name));
6724+
}
6725+
RETURN_THROWS();
6726+
}
6727+
}
6728+
67106729
/* {{{ _reflection_write_property */
67116730
static zval *_reflection_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot)
67126731
{
@@ -6821,7 +6840,10 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */
68216840

68226841
reflection_enum_unit_case_ptr = register_class_ReflectionEnumUnitCase(reflection_class_constant_ptr);
68236842
reflection_init_class_handlers(reflection_enum_unit_case_ptr);
6824-
REGISTER_REFLECTION_CLASS_CONST_LONG(enum_unit_case, "IS_FINAL", ZEND_ACC_FINAL);
6843+
6844+
reflection_enum_backed_case_ptr = register_class_ReflectionEnumBackedCase(reflection_enum_unit_case_ptr);
6845+
reflection_init_class_handlers(reflection_enum_backed_case_ptr);
6846+
REGISTER_REFLECTION_CLASS_CONST_LONG(enum_backed_case, "IS_FINAL", ZEND_ACC_FINAL);
68256847

68266848
REGISTER_REFLECTION_CLASS_CONST_LONG(attribute, "IS_INSTANCEOF", REFLECTION_ATTRIBUTE_IS_INSTANCEOF);
68276849

ext/reflection/php_reflection.stub.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -704,14 +704,19 @@ public function isBacked(): bool {}
704704
public function getBackingType(): ReflectionType|null {}
705705
}
706706

707-
final class ReflectionEnumUnitCase extends ReflectionClassConstant
707+
class ReflectionEnumUnitCase extends ReflectionClassConstant
708708
{
709709
public function __construct(object|string $class, string $constant) {}
710710

711-
public function getBackingValue(): int|string|null {}
712-
713711
public function getEnum(): ReflectionEnum {}
714712

715713
/** @implementation-alias ReflectionClassConstant::getValue */
716714
public function getValue(): UnitEnum {}
717715
}
716+
717+
final class ReflectionEnumBackedCase extends ReflectionEnumUnitCase
718+
{
719+
public function __construct(object|string $class, string $constant) {}
720+
721+
public function getBackingValue(): int|string {}
722+
}

ext/reflection/php_reflection_arginfo.h

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: 7a0e72d87c9d51907124bac58a1f902135575674 */
2+
* Stub hash: b4ec9ccf1844750cdc959918192efb3d22b257ce */
33

44
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 0, 1)
55
ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0)
@@ -512,15 +512,17 @@ ZEND_END_ARG_INFO()
512512

513513
#define arginfo_class_ReflectionEnumUnitCase___construct arginfo_class_ReflectionClassConstant___construct
514514

515-
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_ReflectionEnumUnitCase_getBackingValue, 0, 0, MAY_BE_LONG|MAY_BE_STRING|MAY_BE_NULL)
516-
ZEND_END_ARG_INFO()
517-
518515
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionEnumUnitCase_getEnum, 0, 0, ReflectionEnum, 0)
519516
ZEND_END_ARG_INFO()
520517

521518
ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ReflectionEnumUnitCase_getValue, 0, 0, UnitEnum, 0)
522519
ZEND_END_ARG_INFO()
523520

521+
#define arginfo_class_ReflectionEnumBackedCase___construct arginfo_class_ReflectionClassConstant___construct
522+
523+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_ReflectionEnumBackedCase_getBackingValue, 0, 0, MAY_BE_LONG|MAY_BE_STRING)
524+
ZEND_END_ARG_INFO()
525+
524526

525527
ZEND_METHOD(Reflection, getModifierNames);
526528
ZEND_METHOD(ReflectionClass, __clone);
@@ -731,8 +733,9 @@ ZEND_METHOD(ReflectionEnum, getCases);
731733
ZEND_METHOD(ReflectionEnum, isBacked);
732734
ZEND_METHOD(ReflectionEnum, getBackingType);
733735
ZEND_METHOD(ReflectionEnumUnitCase, __construct);
734-
ZEND_METHOD(ReflectionEnumUnitCase, getBackingValue);
735736
ZEND_METHOD(ReflectionEnumUnitCase, getEnum);
737+
ZEND_METHOD(ReflectionEnumBackedCase, __construct);
738+
ZEND_METHOD(ReflectionEnumBackedCase, getBackingValue);
736739

737740

738741
static const zend_function_entry class_ReflectionException_methods[] = {
@@ -1054,12 +1057,18 @@ static const zend_function_entry class_ReflectionEnum_methods[] = {
10541057

10551058
static const zend_function_entry class_ReflectionEnumUnitCase_methods[] = {
10561059
ZEND_ME(ReflectionEnumUnitCase, __construct, arginfo_class_ReflectionEnumUnitCase___construct, ZEND_ACC_PUBLIC)
1057-
ZEND_ME(ReflectionEnumUnitCase, getBackingValue, arginfo_class_ReflectionEnumUnitCase_getBackingValue, ZEND_ACC_PUBLIC)
10581060
ZEND_ME(ReflectionEnumUnitCase, getEnum, arginfo_class_ReflectionEnumUnitCase_getEnum, ZEND_ACC_PUBLIC)
10591061
ZEND_MALIAS(ReflectionClassConstant, getValue, getValue, arginfo_class_ReflectionEnumUnitCase_getValue, ZEND_ACC_PUBLIC)
10601062
ZEND_FE_END
10611063
};
10621064

1065+
1066+
static const zend_function_entry class_ReflectionEnumBackedCase_methods[] = {
1067+
ZEND_ME(ReflectionEnumBackedCase, __construct, arginfo_class_ReflectionEnumBackedCase___construct, ZEND_ACC_PUBLIC)
1068+
ZEND_ME(ReflectionEnumBackedCase, getBackingValue, arginfo_class_ReflectionEnumBackedCase_getBackingValue, ZEND_ACC_PUBLIC)
1069+
ZEND_FE_END
1070+
};
1071+
10631072
static zend_class_entry *register_class_ReflectionException(zend_class_entry *class_entry_Exception)
10641073
{
10651074
zend_class_entry ce, *class_entry;
@@ -1341,6 +1350,16 @@ static zend_class_entry *register_class_ReflectionEnumUnitCase(zend_class_entry
13411350

13421351
INIT_CLASS_ENTRY(ce, "ReflectionEnumUnitCase", class_ReflectionEnumUnitCase_methods);
13431352
class_entry = zend_register_internal_class_ex(&ce, class_entry_ReflectionClassConstant);
1353+
1354+
return class_entry;
1355+
}
1356+
1357+
static zend_class_entry *register_class_ReflectionEnumBackedCase(zend_class_entry *class_entry_ReflectionEnumUnitCase)
1358+
{
1359+
zend_class_entry ce, *class_entry;
1360+
1361+
INIT_CLASS_ENTRY(ce, "ReflectionEnumBackedCase", class_ReflectionEnumBackedCase_methods);
1362+
class_entry = zend_register_internal_class_ex(&ce, class_entry_ReflectionEnumUnitCase);
13441363
class_entry->ce_flags |= ZEND_ACC_FINAL;
13451364

13461365
return class_entry;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
ReflectionEnumBackedCase::getBackingValue()
3+
--FILE--
4+
<?php
5+
6+
enum Enum_ {
7+
case Foo;
8+
}
9+
10+
enum IntEnum: int {
11+
case Foo = 0;
12+
}
13+
14+
enum StringEnum: string {
15+
case Foo = 'Foo';
16+
}
17+
18+
try {
19+
var_dump((new ReflectionEnumBackedCase(Enum_::class, 'Foo'))->getBackingValue());
20+
} catch (ReflectionException $e) {
21+
echo $e->getMessage() . "\n";
22+
}
23+
24+
var_dump((new ReflectionEnumBackedCase(IntEnum::class, 'Foo'))->getBackingValue());
25+
var_dump((new ReflectionEnumBackedCase(StringEnum::class, 'Foo'))->getBackingValue());
26+
27+
?>
28+
--EXPECT--
29+
Enum case Enum_::Foo is not a backed case
30+
int(0)
31+
string(3) "Foo"

ext/reflection/tests/ReflectionEnumUnitCase_getBackingValue.phpt

Lines changed: 0 additions & 26 deletions
This file was deleted.

ext/reflection/tests/ReflectionEnum_getCase.phpt

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,41 @@ ReflectionEnum::getCases()
33
--FILE--
44
<?php
55

6-
enum Foo {
7-
case Bar;
8-
const Baz = self::Bar;
6+
enum Enum_ {
7+
case Foo;
8+
const Bar = self::Foo;
99
}
1010

11-
$reflectionEnum = new ReflectionEnum(Foo::class);
11+
enum IntEnum: int {
12+
case Foo = 0;
13+
const Bar = self::Foo;
14+
}
1215

16+
$reflectionEnum = new ReflectionEnum(Enum_::class);
17+
var_dump($reflectionEnum->getCase('Foo'));
18+
var_dump($reflectionEnum->getCase('Bar'));
19+
var_dump($reflectionEnum->getCase('Baz'));
20+
21+
$reflectionEnum = new ReflectionEnum(IntEnum::class);
22+
var_dump($reflectionEnum->getCase('Foo'));
1323
var_dump($reflectionEnum->getCase('Bar'));
1424
var_dump($reflectionEnum->getCase('Baz'));
15-
var_dump($reflectionEnum->getCase('Qux'));
1625

1726
?>
1827
--EXPECT--
1928
object(ReflectionEnumUnitCase)#2 (2) {
2029
["name"]=>
21-
string(3) "Bar"
30+
string(3) "Foo"
2231
["class"]=>
32+
string(5) "Enum_"
33+
}
34+
bool(false)
35+
bool(false)
36+
object(ReflectionEnumBackedCase)#1 (2) {
37+
["name"]=>
2338
string(3) "Foo"
39+
["class"]=>
40+
string(7) "IntEnum"
2441
}
2542
bool(false)
2643
bool(false)

ext/reflection/tests/ReflectionEnum_getCases.phpt

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,52 @@ ReflectionEnum::getCases()
33
--FILE--
44
<?php
55

6-
enum Foo {
6+
enum Enum_ {
7+
case Foo;
78
case Bar;
8-
case Baz;
9-
const Qux = self::Bar;
9+
const Baz = self::Bar;
1010
}
1111

12-
var_dump((new ReflectionEnum(Foo::class))->getCases());
12+
enum IntEnum: int {
13+
case Foo = 0;
14+
case Bar = 1;
15+
const Baz = self::Bar;
16+
}
17+
18+
var_dump((new ReflectionEnum(Enum_::class))->getCases());
19+
var_dump((new ReflectionEnum(IntEnum::class))->getCases());
1320

1421
?>
1522
--EXPECT--
1623
array(2) {
1724
[0]=>
1825
object(ReflectionEnumUnitCase)#2 (2) {
1926
["name"]=>
20-
string(3) "Bar"
21-
["class"]=>
2227
string(3) "Foo"
28+
["class"]=>
29+
string(5) "Enum_"
2330
}
2431
[1]=>
2532
object(ReflectionEnumUnitCase)#3 (2) {
2633
["name"]=>
27-
string(3) "Baz"
34+
string(3) "Bar"
2835
["class"]=>
36+
string(5) "Enum_"
37+
}
38+
}
39+
array(2) {
40+
[0]=>
41+
object(ReflectionEnumBackedCase)#2 (2) {
42+
["name"]=>
2943
string(3) "Foo"
44+
["class"]=>
45+
string(7) "IntEnum"
46+
}
47+
[1]=>
48+
object(ReflectionEnumBackedCase)#1 (2) {
49+
["name"]=>
50+
string(3) "Bar"
51+
["class"]=>
52+
string(7) "IntEnum"
3053
}
3154
}

ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ $ext = new ReflectionExtension('reflection');
88
var_dump($ext->getClasses());
99
?>
1010
--EXPECT--
11-
array(21) {
11+
array(22) {
1212
["ReflectionException"]=>
1313
object(ReflectionClass)#2 (1) {
1414
["name"]=>
@@ -114,4 +114,9 @@ array(21) {
114114
["name"]=>
115115
string(22) "ReflectionEnumUnitCase"
116116
}
117+
["ReflectionEnumBackedCase"]=>
118+
object(ReflectionClass)#23 (1) {
119+
["name"]=>
120+
string(24) "ReflectionEnumBackedCase"
121+
}
117122
}

0 commit comments

Comments
 (0)