From d51401d9e2946a3a497622430806957b4b0835cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Fri, 10 Apr 2026 23:06:27 +0200 Subject: [PATCH] gen_stub: fix invalid C variable name for namespaced types in union/intersection type list When generating a union or intersection type list with multiple class types, the variable holding each zend_string* was declared using toVarEscapedName() (backslashes replaced by underscores), but the subsequent ZEND_TYPE_INIT_CLASS() reference used toEscapedName() (backslashes escaped as \\), producing an invalid C identifier. --- build/gen_stub.php | 4 +-- ext/zend_test/test.c | 2 ++ ext/zend_test/test.stub.php | 4 +++ ext/zend_test/test_arginfo.h | 38 ++++++++++++++++++++++- ext/zend_test/test_decl.h | 8 ++--- ext/zend_test/test_legacy_arginfo.h | 24 +++++++++++++- ext/zend_test/tests/gen_stub_test_01.phpt | 8 +++++ 7 files changed, 80 insertions(+), 8 deletions(-) diff --git a/build/gen_stub.php b/build/gen_stub.php index ce1a23866610..c189abd03471 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -2375,8 +2375,8 @@ protected function getTypeCode(string $variableLikeName, string &$code): string $code .= "\t{$variableLikeType}_{$variableLikeName}_type_list->num_types = $classTypeCount;\n"; foreach ($arginfoType->classTypes as $k => $classType) { - $escapedClassName = $classType->toEscapedName(); - $code .= "\t{$variableLikeType}_{$variableLikeName}_type_list->types[$k] = (zend_type) ZEND_TYPE_INIT_CLASS({$variableLikeType}_{$variableLikeName}_class_{$escapedClassName}, 0, 0);\n"; + $varEscapedClassName = $classType->toVarEscapedName(); + $code .= "\t{$variableLikeType}_{$variableLikeName}_type_list->types[$k] = (zend_type) ZEND_TYPE_INIT_CLASS({$variableLikeType}_{$variableLikeName}_class_{$varEscapedClassName}, 0, 0);\n"; } $typeMaskCode = $this->type->toArginfoType()->toTypeMask(); diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 7ab2272dd168..ae1c17e5f192 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -69,6 +69,7 @@ static zend_class_entry *zend_test_forbid_dynamic_call; static zend_class_entry *zend_test_ns_foo_class; static zend_class_entry *zend_test_ns_unlikely_compile_error_class; static zend_class_entry *zend_test_ns_not_unlikely_compile_error_class; +static zend_class_entry *zend_test_ns_bar_class; static zend_class_entry *zend_test_ns2_foo_class; static zend_class_entry *zend_test_ns2_ns_foo_class; static zend_class_entry *zend_test_unit_enum; @@ -1571,6 +1572,7 @@ PHP_MINIT_FUNCTION(zend_test) zend_test_ns_foo_class = register_class_ZendTestNS_Foo(); zend_test_ns_unlikely_compile_error_class = register_class_ZendTestNS_UnlikelyCompileError(); zend_test_ns_not_unlikely_compile_error_class = register_class_ZendTestNS_NotUnlikelyCompileError(); + zend_test_ns_bar_class = register_class_ZendTestNS_Bar(); zend_test_ns2_foo_class = register_class_ZendTestNS2_Foo(); zend_test_ns2_ns_foo_class = register_class_ZendTestNS2_ZendSubNS_Foo(); diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index e102082c6a95..f4844eb0681f 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -363,6 +363,8 @@ class Foo { public function method(): int {} } + interface Bar {} + class UnlikelyCompileError { /* This method signature would create a compile error due to the string * "ZendTestNS\UnlikelyCompileError" in the generated macro call */ @@ -383,6 +385,8 @@ public function method(): ?NotUnlikelyCompileError {} class Foo { public ZendSubNS\Foo $foo; + public ZendSubNS\Foo&\ZendTestNS\Bar $intersectionProp; + public ZendSubNS\Foo|\ZendTestNS\Bar $unionProp; public function method(): void {} } diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 30297234fc87..d2bdbd72db47 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: 46178f5fa88681da91d831250f2f00c45e914624 + * Stub hash: 9a23b7d5305982930579428a345ded725ff5145f * Has decl header: yes */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_trigger_bailout, 0, 0, IS_NEVER, 0) @@ -1294,6 +1294,16 @@ static zend_class_entry *register_class_ZendTestNS_Foo(void) return class_entry; } +static zend_class_entry *register_class_ZendTestNS_Bar(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "ZendTestNS", "Bar", NULL); + class_entry = zend_register_internal_interface(&ce); + + return class_entry; +} + static zend_class_entry *register_class_ZendTestNS_UnlikelyCompileError(void) { zend_class_entry ce, *class_entry; @@ -1340,6 +1350,32 @@ static zend_class_entry *register_class_ZendTestNS2_Foo(void) zend_declare_typed_property(class_entry, property_foo_name, &property_foo_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_foo_class_ZendTestNS2_ZendSubNS_Foo, 0, 0)); zend_string_release_ex(property_foo_name, true); + zval property_intersectionProp_default_value; + ZVAL_UNDEF(&property_intersectionProp_default_value); + zend_string *property_intersectionProp_name = zend_string_init("intersectionProp", sizeof("intersectionProp") - 1, true); + zend_string *property_intersectionProp_class_ZendTestNS2_ZendSubNS_Foo = zend_string_init("ZendTestNS2\\ZendSubNS\\Foo", sizeof("ZendTestNS2\\ZendSubNS\\Foo") - 1, 1); + zend_string *property_intersectionProp_class_ZendTestNS_Bar = zend_string_init("ZendTestNS\\Bar", sizeof("ZendTestNS\\Bar") - 1, 1); + zend_type_list *property_intersectionProp_type_list = malloc(ZEND_TYPE_LIST_SIZE(2)); + property_intersectionProp_type_list->num_types = 2; + property_intersectionProp_type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS(property_intersectionProp_class_ZendTestNS2_ZendSubNS_Foo, 0, 0); + property_intersectionProp_type_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS(property_intersectionProp_class_ZendTestNS_Bar, 0, 0); + zend_type property_intersectionProp_type = ZEND_TYPE_INIT_INTERSECTION(property_intersectionProp_type_list, 0); + zend_declare_typed_property(class_entry, property_intersectionProp_name, &property_intersectionProp_default_value, ZEND_ACC_PUBLIC, NULL, property_intersectionProp_type); + zend_string_release_ex(property_intersectionProp_name, true); + + zval property_unionProp_default_value; + ZVAL_UNDEF(&property_unionProp_default_value); + zend_string *property_unionProp_name = zend_string_init("unionProp", sizeof("unionProp") - 1, true); + zend_string *property_unionProp_class_ZendTestNS2_ZendSubNS_Foo = zend_string_init("ZendTestNS2\\ZendSubNS\\Foo", sizeof("ZendTestNS2\\ZendSubNS\\Foo") - 1, 1); + zend_string *property_unionProp_class_ZendTestNS_Bar = zend_string_init("ZendTestNS\\Bar", sizeof("ZendTestNS\\Bar") - 1, 1); + zend_type_list *property_unionProp_type_list = malloc(ZEND_TYPE_LIST_SIZE(2)); + property_unionProp_type_list->num_types = 2; + property_unionProp_type_list->types[0] = (zend_type) ZEND_TYPE_INIT_CLASS(property_unionProp_class_ZendTestNS2_ZendSubNS_Foo, 0, 0); + property_unionProp_type_list->types[1] = (zend_type) ZEND_TYPE_INIT_CLASS(property_unionProp_class_ZendTestNS_Bar, 0, 0); + zend_type property_unionProp_type = ZEND_TYPE_INIT_UNION(property_unionProp_type_list, 0); + zend_declare_typed_property(class_entry, property_unionProp_name, &property_unionProp_default_value, ZEND_ACC_PUBLIC, NULL, property_unionProp_type); + zend_string_release_ex(property_unionProp_name, true); + return class_entry; } diff --git a/ext/zend_test/test_decl.h b/ext/zend_test/test_decl.h index ed0874f51e72..816e03919a13 100644 --- a/ext/zend_test/test_decl.h +++ b/ext/zend_test/test_decl.h @@ -1,8 +1,8 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: 46178f5fa88681da91d831250f2f00c45e914624 */ + * Stub hash: 9a23b7d5305982930579428a345ded725ff5145f */ -#ifndef ZEND_TEST_DECL_46178f5fa88681da91d831250f2f00c45e914624_H -#define ZEND_TEST_DECL_46178f5fa88681da91d831250f2f00c45e914624_H +#ifndef ZEND_TEST_DECL_9a23b7d5305982930579428a345ded725ff5145f_H +#define ZEND_TEST_DECL_9a23b7d5305982930579428a345ded725ff5145f_H typedef enum zend_enum_ZendTestUnitEnum { ZEND_ENUM_ZendTestUnitEnum_Foo = 1, @@ -27,4 +27,4 @@ typedef enum zend_enum_ZendTestEnumWithInterface { ZEND_ENUM_ZendTestEnumWithInterface_Bar = 2, } zend_enum_ZendTestEnumWithInterface; -#endif /* ZEND_TEST_DECL_46178f5fa88681da91d831250f2f00c45e914624_H */ +#endif /* ZEND_TEST_DECL_9a23b7d5305982930579428a345ded725ff5145f_H */ diff --git a/ext/zend_test/test_legacy_arginfo.h b/ext/zend_test/test_legacy_arginfo.h index 3dcdad8ff6c7..b4a07503ad0f 100644 --- a/ext/zend_test/test_legacy_arginfo.h +++ b/ext/zend_test/test_legacy_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit test.stub.php instead. - * Stub hash: 46178f5fa88681da91d831250f2f00c45e914624 + * Stub hash: 9a23b7d5305982930579428a345ded725ff5145f * Has decl header: yes */ ZEND_BEGIN_ARG_INFO_EX(arginfo_zend_trigger_bailout, 0, 0, 0) @@ -1029,6 +1029,16 @@ static zend_class_entry *register_class_ZendTestNS_Foo(void) return class_entry; } +static zend_class_entry *register_class_ZendTestNS_Bar(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "ZendTestNS", "Bar", NULL); + class_entry = zend_register_internal_interface(&ce); + + return class_entry; +} + static zend_class_entry *register_class_ZendTestNS_UnlikelyCompileError(void) { zend_class_entry ce, *class_entry; @@ -1074,6 +1084,18 @@ static zend_class_entry *register_class_ZendTestNS2_Foo(void) zend_declare_property_ex(class_entry, property_foo_name, &property_foo_default_value, ZEND_ACC_PUBLIC, NULL); zend_string_release_ex(property_foo_name, true); + zval property_intersectionProp_default_value; + ZVAL_NULL(&property_intersectionProp_default_value); + zend_string *property_intersectionProp_name = zend_string_init("intersectionProp", sizeof("intersectionProp") - 1, true); + zend_declare_property_ex(class_entry, property_intersectionProp_name, &property_intersectionProp_default_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release_ex(property_intersectionProp_name, true); + + zval property_unionProp_default_value; + ZVAL_NULL(&property_unionProp_default_value); + zend_string *property_unionProp_name = zend_string_init("unionProp", sizeof("unionProp") - 1, true); + zend_declare_property_ex(class_entry, property_unionProp_name, &property_unionProp_default_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release_ex(property_unionProp_name, true); + return class_entry; } diff --git a/ext/zend_test/tests/gen_stub_test_01.phpt b/ext/zend_test/tests/gen_stub_test_01.phpt index 0f5d525322d6..3126eb61631f 100644 --- a/ext/zend_test/tests/gen_stub_test_01.phpt +++ b/ext/zend_test/tests/gen_stub_test_01.phpt @@ -18,11 +18,19 @@ var_dump($foo); object(ZendTestNS2\Foo)#%d (%d) { ["foo"]=> uninitialized(ZendTestNS2\ZendSubNS\Foo) + ["intersectionProp"]=> + uninitialized(ZendTestNS2\ZendSubNS\Foo&ZendTestNS\Bar) + ["unionProp"]=> + uninitialized(ZendTestNS2\ZendSubNS\Foo|ZendTestNS\Bar) } object(ZendTestNS2\Foo)#%d (%d) { ["foo"]=> object(ZendTestNS2\ZendSubNS\Foo)#%d (%d) { } + ["intersectionProp"]=> + uninitialized(ZendTestNS2\ZendSubNS\Foo&ZendTestNS\Bar) + ["unionProp"]=> + uninitialized(ZendTestNS2\ZendSubNS\Foo|ZendTestNS\Bar) } object(ZendTestNS\UnlikelyCompileError)#%d (%d) { }