From d3bb6128f111a3efa3bd1f8fee2f8bf91506877b Mon Sep 17 00:00:00 2001 From: lazerg Date: Mon, 9 Nov 2026 20:31:51 +0500 Subject: [PATCH 1/3] Fix GH-25340: ReflectionEnumBackedCase::getBackingValue() on non-backed case --- ext/reflection/php_reflection.c | 7 ++++++- ext/reflection/tests/gh25340.phpt | 33 +++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 ext/reflection/tests/gh25340.phpt diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 2c94a6e80d18..dc2700a024fc 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -7807,6 +7807,12 @@ ZEND_METHOD(ReflectionEnumBackedCase, getBackingValue) } GET_REFLECTION_OBJECT_PTR(ref); + if (!(ZEND_CLASS_CONST_FLAGS(ref) & ZEND_CLASS_CONST_IS_CASE) || ref->ce->enum_backing_type == IS_UNDEF) { + zval *case_name = reflection_prop_name(ZEND_THIS); + 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)); + RETURN_THROWS(); + } + if (Z_TYPE(ref->value) == IS_CONSTANT_AST) { zval_update_constant_ex(&ref->value, ref->ce); if (EG(exception)) { @@ -7814,7 +7820,6 @@ ZEND_METHOD(ReflectionEnumBackedCase, getBackingValue) } } - ZEND_ASSERT(intern->ce->enum_backing_type != IS_UNDEF); zval *member_p = zend_enum_fetch_case_value(Z_OBJ(ref->value)); ZVAL_COPY_OR_DUP(return_value, member_p); diff --git a/ext/reflection/tests/gh25340.phpt b/ext/reflection/tests/gh25340.phpt new file mode 100644 index 000000000000..bb89a948e7e8 --- /dev/null +++ b/ext/reflection/tests/gh25340.phpt @@ -0,0 +1,33 @@ +--TEST-- +GH-25340 (ReflectionEnumBackedCase can be used on a non-backed enum via a constructor trick) +--CREDITS-- +ndossche +--FILE-- +getBackingValue()); + } catch (ReflectionException $e) { + echo $e->getMessage(), "\n"; + } +} + +?> +--EXPECT-- +Enum case NotBacked::A is not a backed case +Enum case Backed::C is not a backed case +int(1) From 55aaf38c8ff1b735808d652a8d44014b451f2a46 Mon Sep 17 00:00:00 2001 From: lazerg Date: Mon, 9 Nov 2026 20:35:36 +0500 Subject: [PATCH 2/3] Use the not-a-case message for non-case constants --- ext/reflection/php_reflection.c | 8 +++++++- ext/reflection/tests/gh25340.phpt | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index dc2700a024fc..fa2165219444 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -7807,7 +7807,13 @@ ZEND_METHOD(ReflectionEnumBackedCase, getBackingValue) } GET_REFLECTION_OBJECT_PTR(ref); - if (!(ZEND_CLASS_CONST_FLAGS(ref) & ZEND_CLASS_CONST_IS_CASE) || ref->ce->enum_backing_type == IS_UNDEF) { + if (!(ZEND_CLASS_CONST_FLAGS(ref) & ZEND_CLASS_CONST_IS_CASE)) { + zval *case_name = reflection_prop_name(ZEND_THIS); + zend_throw_exception_ex(reflection_exception_ptr, 0, "Constant %s::%s is not a case", ZSTR_VAL(ref->ce->name), Z_STRVAL_P(case_name)); + RETURN_THROWS(); + } + + if (ref->ce->enum_backing_type == IS_UNDEF) { zval *case_name = reflection_prop_name(ZEND_THIS); 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)); RETURN_THROWS(); diff --git a/ext/reflection/tests/gh25340.phpt b/ext/reflection/tests/gh25340.phpt index bb89a948e7e8..7f1f14a956c8 100644 --- a/ext/reflection/tests/gh25340.phpt +++ b/ext/reflection/tests/gh25340.phpt @@ -29,5 +29,5 @@ foreach ([['NotBacked', 'A'], ['Backed', 'C'], ['Backed', 'A']] as [$class, $con ?> --EXPECT-- Enum case NotBacked::A is not a backed case -Enum case Backed::C is not a backed case +Constant Backed::C is not a case int(1) From f2d694ade167c22dc4df171fedd313532f2978e5 Mon Sep 17 00:00:00 2001 From: lazerg Date: Mon, 9 Nov 2026 23:11:02 +0500 Subject: [PATCH 3/3] Check enum cases in ReflectionClassConstant::__construct() instead --- ext/reflection/php_reflection.c | 55 ++++++++----------------------- ext/reflection/tests/gh25340.phpt | 29 ++++++++++++++-- 2 files changed, 40 insertions(+), 44 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index fa2165219444..123ff1e2429a 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -3975,6 +3975,18 @@ ZEND_METHOD(ReflectionClassConstant, __construct) RETURN_THROWS(); } + if (instanceof_function(Z_OBJCE_P(object), reflection_enum_unit_case_ptr)) { + if (!(ZEND_CLASS_CONST_FLAGS(constant) & ZEND_CLASS_CONST_IS_CASE)) { + zend_throw_exception_ex(reflection_exception_ptr, 0, "Constant %s::%s is not a case", ZSTR_VAL(constant->ce->name), ZSTR_VAL(constname)); + RETURN_THROWS(); + } + + if (instanceof_function(Z_OBJCE_P(object), reflection_enum_backed_case_ptr) && constant->ce->enum_backing_type == IS_UNDEF) { + zend_throw_exception_ex(reflection_exception_ptr, 0, "Enum case %s::%s is not a backed case", ZSTR_VAL(constant->ce->name), ZSTR_VAL(constname)); + RETURN_THROWS(); + } + } + if (intern->ptr) { zval_ptr_dtor(reflection_prop_name(object)); zval_ptr_dtor(reflection_prop_class(object)); @@ -7749,20 +7761,6 @@ ZEND_METHOD(ReflectionEnum, getBackingType) ZEND_METHOD(ReflectionEnumUnitCase, __construct) { ZEND_MN(ReflectionClassConstant___construct)(INTERNAL_FUNCTION_PARAM_PASSTHRU); - if (EG(exception)) { - RETURN_THROWS(); - } - - reflection_object *intern; - zend_class_constant *ref; - - GET_REFLECTION_OBJECT_PTR(ref); - - if (!(ZEND_CLASS_CONST_FLAGS(ref) & ZEND_CLASS_CONST_IS_CASE)) { - zval *case_name = reflection_prop_name(ZEND_THIS); - zend_throw_exception_ex(reflection_exception_ptr, 0, "Constant %s::%s is not a case", ZSTR_VAL(ref->ce->name), Z_STRVAL_P(case_name)); - RETURN_THROWS(); - } } ZEND_METHOD(ReflectionEnumUnitCase, getEnum) @@ -7780,21 +7778,7 @@ ZEND_METHOD(ReflectionEnumUnitCase, getEnum) ZEND_METHOD(ReflectionEnumBackedCase, __construct) { - ZEND_MN(ReflectionEnumUnitCase___construct)(INTERNAL_FUNCTION_PARAM_PASSTHRU); - if (EG(exception)) { - RETURN_THROWS(); - } - - reflection_object *intern; - zend_class_constant *ref; - - GET_REFLECTION_OBJECT_PTR(ref); - - if (ref->ce->enum_backing_type == IS_UNDEF) { - zval *case_name = reflection_prop_name(ZEND_THIS); - 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)); - RETURN_THROWS(); - } + ZEND_MN(ReflectionClassConstant___construct)(INTERNAL_FUNCTION_PARAM_PASSTHRU); } ZEND_METHOD(ReflectionEnumBackedCase, getBackingValue) @@ -7807,18 +7791,6 @@ ZEND_METHOD(ReflectionEnumBackedCase, getBackingValue) } GET_REFLECTION_OBJECT_PTR(ref); - if (!(ZEND_CLASS_CONST_FLAGS(ref) & ZEND_CLASS_CONST_IS_CASE)) { - zval *case_name = reflection_prop_name(ZEND_THIS); - zend_throw_exception_ex(reflection_exception_ptr, 0, "Constant %s::%s is not a case", ZSTR_VAL(ref->ce->name), Z_STRVAL_P(case_name)); - RETURN_THROWS(); - } - - if (ref->ce->enum_backing_type == IS_UNDEF) { - zval *case_name = reflection_prop_name(ZEND_THIS); - 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)); - RETURN_THROWS(); - } - if (Z_TYPE(ref->value) == IS_CONSTANT_AST) { zval_update_constant_ex(&ref->value, ref->ce); if (EG(exception)) { @@ -7826,6 +7798,7 @@ ZEND_METHOD(ReflectionEnumBackedCase, getBackingValue) } } + ZEND_ASSERT(intern->ce->enum_backing_type != IS_UNDEF); zval *member_p = zend_enum_fetch_case_value(Z_OBJ(ref->value)); ZVAL_COPY_OR_DUP(return_value, member_p); diff --git a/ext/reflection/tests/gh25340.phpt b/ext/reflection/tests/gh25340.phpt index 7f1f14a956c8..f4752b5391a4 100644 --- a/ext/reflection/tests/gh25340.phpt +++ b/ext/reflection/tests/gh25340.phpt @@ -5,6 +5,8 @@ ndossche --FILE-- getBackingValue()); + new $reflector($class, $const); } catch (ReflectionException $e) { echo $e->getMessage(), "\n"; } } +$r = new B('Backed', 'A'); +var_dump($r->getEnum()->name, $r->getValue(), $r->getBackingValue()); + +try { + $r->__construct('NotBacked', 'A'); +} catch (ReflectionException $e) { + echo $e->getMessage(), "\n"; +} +var_dump($r->getBackingValue()); + ?> --EXPECT-- +Constant Plain::X is not a case Enum case NotBacked::A is not a backed case Constant Backed::C is not a case +string(6) "Backed" +enum(Backed::A) +int(1) +Enum case NotBacked::A is not a backed case int(1)