Commit 463c5dc1f54 for php.net
commit 463c5dc1f54c1f47725a31b708e291769aa551c1
Author: Daniel Scherzer <daniel.e.scherzer@gmail.com>
Date: Sun May 10 10:06:13 2026 -0700
[RFC] Allow __debugInfo() on enums (#21425)
https://wiki.php.net/rfc/debugable-enums
diff --git a/Zend/tests/enum/__debugInfo.phpt b/Zend/tests/enum/__debugInfo.phpt
deleted file mode 100644
index 33e0f49851f..00000000000
--- a/Zend/tests/enum/__debugInfo.phpt
+++ /dev/null
@@ -1,16 +0,0 @@
---TEST--
-Enum __debugInfo
---FILE--
-<?php
-
-enum Foo {
- case Bar;
-
- public function __debugInfo(): array {
- return $this->cases();
- }
-}
-
-?>
---EXPECTF--
-Fatal error: Enum Foo cannot include magic method __debugInfo in %s on line %d
diff --git a/Zend/tests/enum/debugInfo/backed_enum_value.phpt b/Zend/tests/enum/debugInfo/backed_enum_value.phpt
new file mode 100644
index 00000000000..2bf56eb3a59
--- /dev/null
+++ b/Zend/tests/enum/debugInfo/backed_enum_value.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Enum with __debugInfo() magic method - backed enum value accessible
+--FILE--
+<?php
+
+enum Foo: string {
+ case Bar = "Baz";
+
+ public function __debugInfo() {
+ return [__CLASS__ . '::' . $this->name . ' = ' . $this->value];
+ }
+}
+
+var_dump(Foo::Bar);
+
+?>
+--EXPECT--
+enum(Foo::Bar) (1) {
+ [0]=>
+ string(14) "Foo::Bar = Baz"
+}
diff --git a/Zend/tests/enum/debugInfo/magic_method.phpt b/Zend/tests/enum/debugInfo/magic_method.phpt
new file mode 100644
index 00000000000..4c5963f73b2
--- /dev/null
+++ b/Zend/tests/enum/debugInfo/magic_method.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Enum supports __debugInfo() magic method
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+
+ public function __debugInfo() {
+ return [$this->name . ' is a case of the ' . __CLASS__ . ' enum'];
+ }
+}
+
+var_dump(Foo::Bar);
+
+?>
+--EXPECT--
+enum(Foo::Bar) (1) {
+ [0]=>
+ string(29) "Bar is a case of the Foo enum"
+}
diff --git a/Zend/tests/enum/debugInfo/missing_magic.phpt b/Zend/tests/enum/debugInfo/missing_magic.phpt
new file mode 100644
index 00000000000..9cb5b0c69e5
--- /dev/null
+++ b/Zend/tests/enum/debugInfo/missing_magic.phpt
@@ -0,0 +1,14 @@
+--TEST--
+When an enum does not have __debugInfo() it is printed nicely
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+}
+
+var_dump(Foo::Bar);
+
+?>
+--EXPECT--
+enum(Foo::Bar)
diff --git a/Zend/tests/enum/debugInfo/param_validation.phpt b/Zend/tests/enum/debugInfo/param_validation.phpt
new file mode 100644
index 00000000000..84c60730822
--- /dev/null
+++ b/Zend/tests/enum/debugInfo/param_validation.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Enum with __debugInfo() magic method - param validation
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+
+ public function __debugInfo(mixed $arg): array {
+ return [];
+ }
+}
+
+var_dump(Foo::Bar);
+
+?>
+--EXPECTF--
+Fatal error: Method Foo::__debugInfo() cannot take arguments in %s on line %d
diff --git a/Zend/tests/enum/debugInfo/return_validation.phpt b/Zend/tests/enum/debugInfo/return_validation.phpt
new file mode 100644
index 00000000000..5583dcadf40
--- /dev/null
+++ b/Zend/tests/enum/debugInfo/return_validation.phpt
@@ -0,0 +1,18 @@
+--TEST--
+Enum with __debugInfo() magic method - return validation
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+
+ public function __debugInfo(): int {
+ return 5;
+ }
+}
+
+var_dump(Foo::Bar);
+
+?>
+--EXPECTF--
+Fatal error: Foo::__debugInfo(): Return type must be ?array when declared in %s on line %d
diff --git a/Zend/tests/enum/debugInfo/visibility_validation.phpt b/Zend/tests/enum/debugInfo/visibility_validation.phpt
new file mode 100644
index 00000000000..97e2f2c7456
--- /dev/null
+++ b/Zend/tests/enum/debugInfo/visibility_validation.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Enum with __debugInfo() magic method - visibility validation
+--FILE--
+<?php
+
+enum Foo {
+ case Bar;
+
+ private function __debugInfo(): array {
+ return [];
+ }
+}
+
+var_dump(Foo::Bar);
+
+?>
+--EXPECTF--
+Warning: The magic method Foo::__debugInfo() must have public visibility in %s on line %d
+enum(Foo::Bar) (0) {
+}
diff --git a/Zend/zend_enum.c b/Zend/zend_enum.c
index 136f89d2a63..a5091f6c1b6 100644
--- a/Zend/zend_enum.c
+++ b/Zend/zend_enum.c
@@ -90,7 +90,7 @@ static void zend_verify_enum_properties(const zend_class_entry *ce)
static void zend_verify_enum_magic_methods(const zend_class_entry *ce)
{
- // Only __get, __call and __invoke are allowed
+ // Only __get, __call, __debugInfo and __invoke are allowed
ZEND_ENUM_DISALLOW_MAGIC_METHOD(constructor, "__construct");
ZEND_ENUM_DISALLOW_MAGIC_METHOD(destructor, "__destruct");
@@ -100,7 +100,6 @@ static void zend_verify_enum_magic_methods(const zend_class_entry *ce)
ZEND_ENUM_DISALLOW_MAGIC_METHOD(__unset, "__unset");
ZEND_ENUM_DISALLOW_MAGIC_METHOD(__isset, "__isset");
ZEND_ENUM_DISALLOW_MAGIC_METHOD(__tostring, "__toString");
- ZEND_ENUM_DISALLOW_MAGIC_METHOD(__debugInfo, "__debugInfo");
ZEND_ENUM_DISALLOW_MAGIC_METHOD(__serialize, "__serialize");
ZEND_ENUM_DISALLOW_MAGIC_METHOD(__unserialize, "__unserialize");
diff --git a/ext/standard/var.c b/ext/standard/var.c
index 7bf4e17a4cb..3137a270f66 100644
--- a/ext/standard/var.c
+++ b/ext/standard/var.c
@@ -166,7 +166,7 @@ PHPAPI void php_var_dump(zval *struc, int level) /* {{{ */
break;
case IS_OBJECT: {
zend_class_entry *ce = Z_OBJCE_P(struc);
- if (ce->ce_flags & ZEND_ACC_ENUM) {
+ if ((ce->ce_flags & ZEND_ACC_ENUM) && ce->__debugInfo == NULL) {
zval *case_name_zval = zend_enum_fetch_case_name(Z_OBJ_P(struc));
php_printf("%senum(%s::%s)\n", COMMON, ZSTR_VAL(ce->name), Z_STRVAL_P(case_name_zval));
return;
@@ -180,11 +180,16 @@ PHPAPI void php_var_dump(zval *struc, int level) /* {{{ */
ZEND_GUARD_OR_GC_PROTECT_RECURSION(guard, DEBUG, zobj);
myht = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_DEBUG);
- class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc));
- const char *prefix = php_var_dump_object_prefix(Z_OBJ_P(struc));
+ if (ce->ce_flags & ZEND_ACC_ENUM) {
+ zval *case_name_zval = zend_enum_fetch_case_name(Z_OBJ_P(struc));
+ php_printf("%senum(%s::%s) (%d) {\n", COMMON, ZSTR_VAL(ce->name), Z_STRVAL_P(case_name_zval), myht ? zend_array_count(myht) : 0);
+ } else {
+ class_name = Z_OBJ_HANDLER_P(struc, get_class_name)(Z_OBJ_P(struc));
+ const char *prefix = php_var_dump_object_prefix(Z_OBJ_P(struc));
- php_printf("%s%sobject(%s)#%d (%d) {\n", COMMON, prefix, ZSTR_VAL(class_name), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0);
- zend_string_release_ex(class_name, 0);
+ php_printf("%s%sobject(%s)#%d (%d) {\n", COMMON, prefix, ZSTR_VAL(class_name), Z_OBJ_HANDLE_P(struc), myht ? zend_array_count(myht) : 0);
+ zend_string_release_ex(class_name, 0);
+ }
if (myht) {
zend_ulong num;