Commit a2114a3b505 for php.net
commit a2114a3b50524b889817d3611e3146b5da157147
Author: Jorg Adam Sowa <jorg.sowa@gmail.com>
Date: Sun Jun 14 11:11:24 2026 +0200
Zend: fix canonical case of magic-method name macros (#22275)
Fixed the canonical case of the magic method `__debugInfo()` + organized all macros of magic methods with the separation of canonical and lowercase versions.
diff --git a/Zend/tests/debug_info/debug_info-error-0.0.phpt b/Zend/tests/debug_info/debug_info-error-0.0.phpt
index ab41b440fc8..4df25c4c95a 100644
--- a/Zend/tests/debug_info/debug_info-error-0.0.phpt
+++ b/Zend/tests/debug_info/debug_info-error-0.0.phpt
@@ -17,4 +17,4 @@ public function __construct($val) {
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-0.phpt b/Zend/tests/debug_info/debug_info-error-0.phpt
index 37289c6468f..c0cedf3b88e 100644
--- a/Zend/tests/debug_info/debug_info-error-0.phpt
+++ b/Zend/tests/debug_info/debug_info-error-0.phpt
@@ -17,4 +17,4 @@ public function __construct($val) {
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-1.0.phpt b/Zend/tests/debug_info/debug_info-error-1.0.phpt
index 9b168621b5e..6af945cb0e3 100644
--- a/Zend/tests/debug_info/debug_info-error-1.0.phpt
+++ b/Zend/tests/debug_info/debug_info-error-1.0.phpt
@@ -17,4 +17,4 @@ public function __construct($val) {
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-1.phpt b/Zend/tests/debug_info/debug_info-error-1.phpt
index ae01a6055c0..7cadd485118 100644
--- a/Zend/tests/debug_info/debug_info-error-1.phpt
+++ b/Zend/tests/debug_info/debug_info-error-1.phpt
@@ -17,4 +17,4 @@ public function __construct($val) {
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-case-insensitive.phpt b/Zend/tests/debug_info/debug_info-error-case-insensitive.phpt
new file mode 100644
index 00000000000..c2a6f4357a5
--- /dev/null
+++ b/Zend/tests/debug_info/debug_info-error-case-insensitive.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Testing __debugInfo() magic method declared with non-canonical case
+--FILE--
+<?php
+
+class C {
+ public $val;
+ public function __DEBUGINFO() {
+ return $this->val;
+ }
+ public function __construct($val) {
+ $this->val = $val;
+ }
+}
+
+$c = new C(1);
+var_dump($c);
+?>
+--EXPECTF--
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-empty_str.phpt b/Zend/tests/debug_info/debug_info-error-empty_str.phpt
index bbab78cd820..21611cc9bde 100644
--- a/Zend/tests/debug_info/debug_info-error-empty_str.phpt
+++ b/Zend/tests/debug_info/debug_info-error-empty_str.phpt
@@ -17,4 +17,4 @@ public function __construct($val) {
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-false.phpt b/Zend/tests/debug_info/debug_info-error-false.phpt
index 3e48372c420..fb5a35c2d1e 100644
--- a/Zend/tests/debug_info/debug_info-error-false.phpt
+++ b/Zend/tests/debug_info/debug_info-error-false.phpt
@@ -17,4 +17,4 @@ public function __construct($val) {
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-object.phpt b/Zend/tests/debug_info/debug_info-error-object.phpt
index 42e07399990..5cdc6b289e6 100644
--- a/Zend/tests/debug_info/debug_info-error-object.phpt
+++ b/Zend/tests/debug_info/debug_info-error-object.phpt
@@ -17,4 +17,4 @@ public function __construct($val) {
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-resource.phpt b/Zend/tests/debug_info/debug_info-error-resource.phpt
index ccacce7a74b..1fc84fe83d8 100644
--- a/Zend/tests/debug_info/debug_info-error-resource.phpt
+++ b/Zend/tests/debug_info/debug_info-error-resource.phpt
@@ -19,4 +19,4 @@ public function __construct($val) {
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-str.phpt b/Zend/tests/debug_info/debug_info-error-str.phpt
index 85d3f63b97e..fe4201de1c9 100644
--- a/Zend/tests/debug_info/debug_info-error-str.phpt
+++ b/Zend/tests/debug_info/debug_info-error-str.phpt
@@ -17,4 +17,4 @@ public function __construct($val) {
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/debug_info/debug_info-error-true.phpt b/Zend/tests/debug_info/debug_info-error-true.phpt
index 3843c6a7a14..12bb7329a85 100644
--- a/Zend/tests/debug_info/debug_info-error-true.phpt
+++ b/Zend/tests/debug_info/debug_info-error-true.phpt
@@ -17,4 +17,4 @@ public function __construct($val) {
var_dump($c);
?>
--EXPECTF--
-Fatal error: __debuginfo() must return an array in %s on line %d
+Fatal error: __debugInfo() must return an array in %s on line %d
diff --git a/Zend/tests/magic_methods/stringable_automatic_implementation_case_insensitive.phpt b/Zend/tests/magic_methods/stringable_automatic_implementation_case_insensitive.phpt
new file mode 100644
index 00000000000..7f456479445
--- /dev/null
+++ b/Zend/tests/magic_methods/stringable_automatic_implementation_case_insensitive.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Stringable is automatically implemented for __toString() declared with non-canonical case
+--FILE--
+<?php
+
+class Test {
+ public function __TOSTRING() {
+ return "foo";
+ }
+}
+
+var_dump(new Test instanceof Stringable);
+var_dump((new ReflectionClass(Test::class))->getInterfaceNames());
+var_dump((string) new Test);
+
+?>
+--EXPECT--
+bool(true)
+array(1) {
+ [0]=>
+ string(10) "Stringable"
+}
+string(3) "foo"
diff --git a/Zend/zend_API.c b/Zend/zend_API.c
index 789b3ac2642..46b62dcc7c4 100644
--- a/Zend/zend_API.c
+++ b/Zend/zend_API.c
@@ -2809,18 +2809,18 @@ ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce,
zend_check_magic_method_public(ce, fptr);
zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_STRING);
zend_check_magic_method_arg_type(1, ce, fptr, error_type, MAY_BE_ARRAY);
- } else if (zend_string_equals_literal(lcname, ZEND_CALLSTATIC_FUNC_NAME)) {
+ } else if (zend_string_equals_literal(lcname, ZEND_CALLSTATIC_FUNC_LCNAME)) {
zend_check_magic_method_args(2, ce, fptr, error_type);
zend_check_magic_method_static(ce, fptr, error_type);
zend_check_magic_method_public(ce, fptr);
zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_STRING);
zend_check_magic_method_arg_type(1, ce, fptr, error_type, MAY_BE_ARRAY);
- } else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME)) {
+ } else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_LCNAME)) {
zend_check_magic_method_args(0, ce, fptr, error_type);
zend_check_magic_method_non_static(ce, fptr, error_type);
zend_check_magic_method_public(ce, fptr);
zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_STRING);
- } else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_NAME)) {
+ } else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_LCNAME)) {
zend_check_magic_method_args(0, ce, fptr, error_type);
zend_check_magic_method_non_static(ce, fptr, error_type);
zend_check_magic_method_public(ce, fptr);
@@ -2829,18 +2829,18 @@ ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce,
zend_error(E_DEPRECATED, "Returning null from %s::__debugInfo() is deprecated, make the return type non-nullable and return an empty array instead",
ZSTR_VAL(ce->name));
}
- } else if (zend_string_equals_literal(lcname, "__serialize")) {
+ } else if (zend_string_equals_literal(lcname, ZEND_SERIALIZE_FUNC_NAME)) {
zend_check_magic_method_args(0, ce, fptr, error_type);
zend_check_magic_method_non_static(ce, fptr, error_type);
zend_check_magic_method_public(ce, fptr);
zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_ARRAY);
- } else if (zend_string_equals_literal(lcname, "__unserialize")) {
+ } else if (zend_string_equals_literal(lcname, ZEND_UNSERIALIZE_FUNC_NAME)) {
zend_check_magic_method_args(1, ce, fptr, error_type);
zend_check_magic_method_non_static(ce, fptr, error_type);
zend_check_magic_method_public(ce, fptr);
zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_ARRAY);
zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_VOID);
- } else if (zend_string_equals_literal(lcname, "__set_state")) {
+ } else if (zend_string_equals_literal(lcname, ZEND_SET_STATE_FUNC_NAME)) {
zend_check_magic_method_args(1, ce, fptr, error_type);
zend_check_magic_method_static(ce, fptr, error_type);
zend_check_magic_method_public(ce, fptr);
@@ -2888,16 +2888,16 @@ ZEND_API void zend_add_magic_method(zend_class_entry *ce, zend_function *fptr, c
} else if (zend_string_equals_literal(lcname, ZEND_ISSET_FUNC_NAME)) {
ce->__isset = fptr;
ce->ce_flags |= ZEND_ACC_USE_GUARDS;
- } else if (zend_string_equals_literal(lcname, ZEND_CALLSTATIC_FUNC_NAME)) {
+ } else if (zend_string_equals_literal(lcname, ZEND_CALLSTATIC_FUNC_LCNAME)) {
ce->__callstatic = fptr;
- } else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME)) {
+ } else if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_LCNAME)) {
ce->__tostring = fptr;
- } else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_NAME)) {
+ } else if (zend_string_equals_literal(lcname, ZEND_DEBUGINFO_FUNC_LCNAME)) {
ce->__debugInfo = fptr;
ce->ce_flags |= ZEND_ACC_USE_GUARDS;
- } else if (zend_string_equals_literal(lcname, "__serialize")) {
+ } else if (zend_string_equals_literal(lcname, ZEND_SERIALIZE_FUNC_NAME)) {
ce->__serialize = fptr;
- } else if (zend_string_equals_literal(lcname, "__unserialize")) {
+ } else if (zend_string_equals_literal(lcname, ZEND_UNSERIALIZE_FUNC_NAME)) {
ce->__unserialize = fptr;
}
}
@@ -3111,7 +3111,7 @@ ZEND_API zend_result zend_register_functions(zend_class_entry *scope, const zend
/* If not specified, add __toString() return type for compatibility with Stringable
* interface. */
- if (scope && zend_string_equals_literal_ci(internal_function->function_name, "__tostring") &&
+ if (scope && zend_string_equals_literal_ci(internal_function->function_name, ZEND_TOSTRING_FUNC_LCNAME) &&
!(internal_function->fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
zend_error(E_CORE_WARNING, "%s::__toString() implemented without string return type",
ZSTR_VAL(scope->name));
diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 105f99d2417..7e9f7ceac8d 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -8610,7 +8610,7 @@ static zend_string *zend_begin_method_decl(zend_op_array *op_array, zend_string
}
zend_add_magic_method(ce, (zend_function *) op_array, lcname);
- if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME)
+ if (zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_LCNAME)
&& !(ce->ce_flags & ZEND_ACC_TRAIT)) {
add_stringable_interface(ce);
}
@@ -8841,7 +8841,7 @@ static zend_op_array *zend_compile_func_decl_ex(
}
zend_compile_params(params_ast, return_type_ast,
- is_method && zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_NAME) ? IS_STRING : 0);
+ is_method && zend_string_equals_literal(lcname, ZEND_TOSTRING_FUNC_LCNAME) ? IS_STRING : 0);
if (CG(active_op_array)->fn_flags & ZEND_ACC_GENERATOR) {
zend_mark_function_as_generator();
zend_emit_op(NULL, ZEND_GENERATOR_CREATE, NULL, NULL);
diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h
index 0e31332c97f..2351882a560 100644
--- a/Zend/zend_compile.h
+++ b/Zend/zend_compile.h
@@ -368,7 +368,7 @@ typedef struct _zend_oparray_context {
#define ZEND_ACC_USES_THIS (1 << 17) /* | X | | */
/* | | | */
/* call through user function trampoline. e.g. | | | */
-/* __call, __callstatic | | | */
+/* __call, __callStatic | | | */
#define ZEND_ACC_CALL_VIA_TRAMPOLINE (1 << 18) /* | X | | */
/* | | | */
/* disable inline caching | | | */
@@ -1249,10 +1249,18 @@ END_EXTERN_C()
#define ZEND_UNSET_FUNC_NAME "__unset"
#define ZEND_ISSET_FUNC_NAME "__isset"
#define ZEND_CALL_FUNC_NAME "__call"
-#define ZEND_CALLSTATIC_FUNC_NAME "__callstatic"
-#define ZEND_TOSTRING_FUNC_NAME "__tostring"
+#define ZEND_CALLSTATIC_FUNC_NAME "__callStatic"
+#define ZEND_CALLSTATIC_FUNC_LCNAME "__callstatic"
+#define ZEND_TOSTRING_FUNC_NAME "__toString"
+#define ZEND_TOSTRING_FUNC_LCNAME "__tostring"
#define ZEND_INVOKE_FUNC_NAME "__invoke"
-#define ZEND_DEBUGINFO_FUNC_NAME "__debuginfo"
+#define ZEND_DEBUGINFO_FUNC_NAME "__debugInfo"
+#define ZEND_DEBUGINFO_FUNC_LCNAME "__debuginfo"
+#define ZEND_SLEEP_FUNC_NAME "__sleep"
+#define ZEND_WAKEUP_FUNC_NAME "__wakeup"
+#define ZEND_SERIALIZE_FUNC_NAME "__serialize"
+#define ZEND_UNSERIALIZE_FUNC_NAME "__unserialize"
+#define ZEND_SET_STATE_FUNC_NAME "__set_state"
/* The following constants may be combined in CG(compiler_options)
* to change the default compiler behavior */
diff --git a/Zend/zend_enum.c b/Zend/zend_enum.c
index a5091f6c1b6..7f0b5857538 100644
--- a/Zend/zend_enum.c
+++ b/Zend/zend_enum.c
@@ -92,21 +92,21 @@ static void zend_verify_enum_magic_methods(const zend_class_entry *ce)
{
// Only __get, __call, __debugInfo and __invoke are allowed
- ZEND_ENUM_DISALLOW_MAGIC_METHOD(constructor, "__construct");
- ZEND_ENUM_DISALLOW_MAGIC_METHOD(destructor, "__destruct");
- ZEND_ENUM_DISALLOW_MAGIC_METHOD(clone, "__clone");
- ZEND_ENUM_DISALLOW_MAGIC_METHOD(__get, "__get");
- ZEND_ENUM_DISALLOW_MAGIC_METHOD(__set, "__set");
- 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(__serialize, "__serialize");
- ZEND_ENUM_DISALLOW_MAGIC_METHOD(__unserialize, "__unserialize");
+ ZEND_ENUM_DISALLOW_MAGIC_METHOD(constructor, ZEND_CONSTRUCTOR_FUNC_NAME);
+ ZEND_ENUM_DISALLOW_MAGIC_METHOD(destructor, ZEND_DESTRUCTOR_FUNC_NAME);
+ ZEND_ENUM_DISALLOW_MAGIC_METHOD(clone, ZEND_CLONE_FUNC_NAME);
+ ZEND_ENUM_DISALLOW_MAGIC_METHOD(__get, ZEND_GET_FUNC_NAME);
+ ZEND_ENUM_DISALLOW_MAGIC_METHOD(__set, ZEND_SET_FUNC_NAME);
+ ZEND_ENUM_DISALLOW_MAGIC_METHOD(__unset, ZEND_UNSET_FUNC_NAME);
+ ZEND_ENUM_DISALLOW_MAGIC_METHOD(__isset, ZEND_ISSET_FUNC_NAME);
+ ZEND_ENUM_DISALLOW_MAGIC_METHOD(__tostring, ZEND_TOSTRING_FUNC_NAME);
+ ZEND_ENUM_DISALLOW_MAGIC_METHOD(__serialize, ZEND_SERIALIZE_FUNC_NAME);
+ ZEND_ENUM_DISALLOW_MAGIC_METHOD(__unserialize, ZEND_UNSERIALIZE_FUNC_NAME);
static const char *const forbidden_methods[] = {
- "__sleep",
- "__wakeup",
- "__set_state",
+ ZEND_SLEEP_FUNC_NAME,
+ ZEND_WAKEUP_FUNC_NAME,
+ ZEND_SET_STATE_FUNC_NAME,
};
uint32_t forbidden_methods_length = sizeof(forbidden_methods) / sizeof(forbidden_methods[0]);