From 7777939b43579726c93d60057b22ed6e5820030f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 6 May 2025 10:54:50 +0200 Subject: [PATCH 1/2] gen_stub: Fix `ce_flags` generation for compatibility mode Fixes php/php-src#18506 --- build/gen_stub.php | 26 +++++++++++++++++++------- ext/zend_test/test.c | 3 +++ ext/zend_test/test.stub.php | 7 +++++++ ext/zend_test/test_arginfo.h | 29 ++++++++++++++++++++++++++++- 4 files changed, 57 insertions(+), 8 deletions(-) diff --git a/build/gen_stub.php b/build/gen_stub.php index 45641f4061b9c..4dbee4cfae778 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -3283,7 +3283,15 @@ public function getRegistration(array $allConstInfos): string $code .= "{\n"; $flagCodes = generateVersionDependentFlagCode("%s", $this->getFlagsByPhpVersion(), $this->phpVersionIdMinimumCompatibility); - $flags = implode("", $flagCodes); + if (count($flagCodes) > 1) { + // If we have more than one entry, there will be preprocessor conditions, + // thus we need to start with a newline. + $flags = "\n" . implode("\n", $flagCodes); + } else if (count($flagCodes) === 1) { + $flags = " " . $flagCodes[0]; + } else { + $flags = ""; + } $classMethods = ($this->funcInfos === []) ? 'NULL' : "class_{$escapedName}_methods"; if ($this->type === "enum") { @@ -3292,7 +3300,7 @@ public function getRegistration(array $allConstInfos): string ? $this->enumBackingType->toTypeCode() : "IS_UNDEF"; $code .= "\tzend_class_entry *class_entry = zend_register_internal_enum(\"$name\", $backingType, $classMethods);\n"; if ($flags !== "") { - $code .= "\tclass_entry->ce_flags |= $flags\n"; + $code .= "\tclass_entry->ce_flags |=$flags\n"; } } else { $code .= "\tzend_class_entry ce, *class_entry;\n\n"; @@ -3310,21 +3318,21 @@ public function getRegistration(array $allConstInfos): string $code .= "#if (PHP_VERSION_ID >= " . PHP_84_VERSION_ID . ")\n"; } - $code .= "\tclass_entry = zend_register_internal_class_with_flags(&ce, " . (isset($this->extends[0]) ? "class_entry_" . str_replace("\\", "_", $this->extends[0]->toString()) : "NULL") . ", " . ($flags ?: 0) . ");\n"; + $code .= "\tclass_entry = zend_register_internal_class_with_flags(&ce, " . (isset($this->extends[0]) ? "class_entry_" . str_replace("\\", "_", $this->extends[0]->toString()) : "NULL") . "," . ($flags ?: " 0") . ");\n"; if (!$php84MinimumCompatibility) { $code .= "#else\n"; $code .= "\tclass_entry = zend_register_internal_class_ex(&ce, " . (isset($this->extends[0]) ? "class_entry_" . str_replace("\\", "_", $this->extends[0]->toString()) : "NULL") . ");\n"; if ($flags !== "") { - $code .= "\tclass_entry->ce_flags |= $flags;\n"; + $code .= "\tclass_entry->ce_flags |=$flags;\n"; } $code .= "#endif\n"; } } else { $code .= "\tclass_entry = zend_register_internal_interface(&ce);\n"; if ($flags !== "") { - $code .= "\tclass_entry->ce_flags |= $flags\n"; + $code .= "\tclass_entry->ce_flags |=$flags\n"; } } } @@ -5391,12 +5399,16 @@ static function (array $value): bool { $code = ""; $if = $i === 0 ? "#if" : "#elif"; - $endif = $i === $flagCount - 1 ? "#endif\n" : ""; $code .= "$if (PHP_VERSION_ID >= $version)\n"; $code .= sprintf($codeTemplate, implode("|", $versionFlags)); - $code .= $endif; + if ($i === $flagCount - 1) { + if (!str_ends_with($code, "\n")) { + $code .= "\n"; + } + $code .= "#endif\n"; + } $result[] = $code; $i++; diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index dbad83fb0e843..d52b8aa0cd172 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -54,6 +54,7 @@ ZEND_DECLARE_MODULE_GLOBALS(zend_test) static zend_class_entry *zend_test_interface; static zend_class_entry *zend_test_class; static zend_class_entry *zend_test_child_class; +static zend_class_entry *zend_test_gen_stub_flag_compatibility_test; static zend_class_entry *zend_attribute_test_class; static zend_class_entry *zend_test_trait; static zend_class_entry *zend_test_attribute; @@ -1271,6 +1272,8 @@ PHP_MINIT_FUNCTION(zend_test) memcpy(&zend_test_class_handlers, &std_object_handlers, sizeof(zend_object_handlers)); zend_test_class_handlers.get_method = zend_test_class_method_get; + zend_test_gen_stub_flag_compatibility_test = register_class_ZendTestGenStubFlagCompatibilityTest(); + zend_attribute_test_class = register_class_ZendAttributeTest(); zend_test_trait = register_class__ZendTestTrait(); diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index fbfd93da40975..213c1b3f57476 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -85,6 +85,13 @@ class _ZendTestChildClass extends _ZendTestClass public function returnsThrowable(): Exception {} } + /** + * @not-serializable + */ + final class ZendTestGenStubFlagCompatibilityTest { + + } + class ZendAttributeTest { /** @var int */ #[ZendTestRepeatableAttribute] diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index eb83d34fa1f59..85b44dc7914a8 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 the .stub.php file instead. - * Stub hash: a7b5de3e4868f9a4aef78da6b98bbce882e129a9 */ + * Stub hash: 20a58913caf419fb60a3bc9cf275db605e2c3b0f */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -774,6 +774,33 @@ static zend_class_entry *register_class__ZendTestChildClass(zend_class_entry *cl return class_entry; } +static zend_class_entry *register_class_ZendTestGenStubFlagCompatibilityTest(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ZendTestGenStubFlagCompatibilityTest", NULL); +#if (PHP_VERSION_ID >= 80400) + class_entry = zend_register_internal_class_with_flags(&ce, NULL, +#if (PHP_VERSION_ID >= 80100) +ZEND_ACC_FINAL|ZEND_ACC_NOT_SERIALIZABLE +#elif (PHP_VERSION_ID >= 80000) +ZEND_ACC_FINAL +#endif +); +#else + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= +#if (PHP_VERSION_ID >= 80100) +ZEND_ACC_FINAL|ZEND_ACC_NOT_SERIALIZABLE +#elif (PHP_VERSION_ID >= 80000) +ZEND_ACC_FINAL +#endif +; +#endif + + return class_entry; +} + static zend_class_entry *register_class_ZendAttributeTest(void) { zend_class_entry ce, *class_entry; From 34c84a1368e4bdb201f33f940940f9b972c33f86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 6 May 2025 12:52:57 +0200 Subject: [PATCH 2/2] gen_stub: Improve output for ce_flags compatibility --- build/gen_stub.php | 40 ++++++++++++------------------------ ext/zend_test/test_arginfo.h | 14 +++---------- 2 files changed, 16 insertions(+), 38 deletions(-) diff --git a/build/gen_stub.php b/build/gen_stub.php index 4dbee4cfae778..f1d8b43862e62 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -3282,26 +3282,13 @@ public function getRegistration(array $allConstInfos): string $code .= "{\n"; - $flagCodes = generateVersionDependentFlagCode("%s", $this->getFlagsByPhpVersion(), $this->phpVersionIdMinimumCompatibility); - if (count($flagCodes) > 1) { - // If we have more than one entry, there will be preprocessor conditions, - // thus we need to start with a newline. - $flags = "\n" . implode("\n", $flagCodes); - } else if (count($flagCodes) === 1) { - $flags = " " . $flagCodes[0]; - } else { - $flags = ""; - } - $classMethods = ($this->funcInfos === []) ? 'NULL' : "class_{$escapedName}_methods"; if ($this->type === "enum") { $name = addslashes((string) $this->name); $backingType = $this->enumBackingType ? $this->enumBackingType->toTypeCode() : "IS_UNDEF"; $code .= "\tzend_class_entry *class_entry = zend_register_internal_enum(\"$name\", $backingType, $classMethods);\n"; - if ($flags !== "") { - $code .= "\tclass_entry->ce_flags |=$flags\n"; - } + $code .= implode("", generateVersionDependentFlagCode("\tclass_entry->ce_flags = %s;\n", $this->getFlagsByPhpVersion(), $this->phpVersionIdMinimumCompatibility)); } else { $code .= "\tzend_class_entry ce, *class_entry;\n\n"; if (count($this->name->getParts()) > 1) { @@ -3318,22 +3305,25 @@ public function getRegistration(array $allConstInfos): string $code .= "#if (PHP_VERSION_ID >= " . PHP_84_VERSION_ID . ")\n"; } - $code .= "\tclass_entry = zend_register_internal_class_with_flags(&ce, " . (isset($this->extends[0]) ? "class_entry_" . str_replace("\\", "_", $this->extends[0]->toString()) : "NULL") . "," . ($flags ?: " 0") . ");\n"; + $template = "\tclass_entry = zend_register_internal_class_with_flags(&ce, " . (isset($this->extends[0]) ? "class_entry_" . str_replace("\\", "_", $this->extends[0]->toString()) : "NULL") . ", %s);\n"; + $entries = generateVersionDependentFlagCode($template, $this->getFlagsByPhpVersion(), $this->phpVersionIdMinimumCompatibility ? max($this->phpVersionIdMinimumCompatibility, PHP_84_VERSION_ID) : null); + if ($entries !== []) { + $code .= implode("", $entries); + } else { + $code .= sprintf($template, "0"); + } if (!$php84MinimumCompatibility) { $code .= "#else\n"; $code .= "\tclass_entry = zend_register_internal_class_ex(&ce, " . (isset($this->extends[0]) ? "class_entry_" . str_replace("\\", "_", $this->extends[0]->toString()) : "NULL") . ");\n"; - if ($flags !== "") { - $code .= "\tclass_entry->ce_flags |=$flags;\n"; - } + $code .= implode("", generateVersionDependentFlagCode("\tclass_entry->ce_flags |= %s;\n", $this->getFlagsByPhpVersion(), $this->phpVersionIdMinimumCompatibility)); $code .= "#endif\n"; } } else { $code .= "\tclass_entry = zend_register_internal_interface(&ce);\n"; - if ($flags !== "") { - $code .= "\tclass_entry->ce_flags |=$flags\n"; - } + $code .= implode("", generateVersionDependentFlagCode("\tclass_entry->ce_flags |= %s;\n", $this->getFlagsByPhpVersion(), $this->phpVersionIdMinimumCompatibility)); + } } @@ -5399,16 +5389,12 @@ static function (array $value): bool { $code = ""; $if = $i === 0 ? "#if" : "#elif"; + $endif = $i === $flagCount - 1 ? "#endif\n" : ""; $code .= "$if (PHP_VERSION_ID >= $version)\n"; $code .= sprintf($codeTemplate, implode("|", $versionFlags)); - if ($i === $flagCount - 1) { - if (!str_ends_with($code, "\n")) { - $code .= "\n"; - } - $code .= "#endif\n"; - } + $code .= $endif; $result[] = $code; $i++; diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 85b44dc7914a8..9888ba39c14e4 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -780,22 +780,14 @@ static zend_class_entry *register_class_ZendTestGenStubFlagCompatibilityTest(voi INIT_CLASS_ENTRY(ce, "ZendTestGenStubFlagCompatibilityTest", NULL); #if (PHP_VERSION_ID >= 80400) - class_entry = zend_register_internal_class_with_flags(&ce, NULL, -#if (PHP_VERSION_ID >= 80100) -ZEND_ACC_FINAL|ZEND_ACC_NOT_SERIALIZABLE -#elif (PHP_VERSION_ID >= 80000) -ZEND_ACC_FINAL -#endif -); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NOT_SERIALIZABLE); #else class_entry = zend_register_internal_class_ex(&ce, NULL); - class_entry->ce_flags |= #if (PHP_VERSION_ID >= 80100) -ZEND_ACC_FINAL|ZEND_ACC_NOT_SERIALIZABLE + class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NOT_SERIALIZABLE; #elif (PHP_VERSION_ID >= 80000) -ZEND_ACC_FINAL + class_entry->ce_flags |= ZEND_ACC_FINAL; #endif -; #endif return class_entry;