diff --git a/Zend/tests/e_strict-deprecated.phpt b/Zend/tests/e_strict-deprecated.phpt index 71666e75c22f4..5017d45a03a1d 100644 --- a/Zend/tests/e_strict-deprecated.phpt +++ b/Zend/tests/e_strict-deprecated.phpt @@ -10,5 +10,5 @@ var_dump(E_STRICT); --EXPECTF-- int(30719) -Deprecated: Constant E_STRICT is deprecated in %s on line %d +Deprecated: Constant E_STRICT is deprecated since 8.4, the error level was removed in %s on line %d int(2048) diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index eb464772c100c..a4d6b28c0094a 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -21,6 +21,7 @@ #define ZEND_ATTRIBUTES_H #include "zend_compile.h" +#include "zend_constants.h" #define ZEND_ATTRIBUTE_TARGET_CLASS (1<<0) #define ZEND_ATTRIBUTE_TARGET_FUNCTION (1<<1) @@ -126,6 +127,12 @@ static zend_always_inline zend_attribute *zend_add_class_constant_attribute(zend return zend_add_attribute(&c->attributes, name, argc, flags, 0, 0); } +static zend_always_inline zend_attribute *zend_add_global_constant_attribute(zend_constant *c, zend_string *name, uint32_t argc) +{ + uint32_t flags = ZEND_CONSTANT_MODULE_NUMBER(c) == PHP_USER_CONSTANT ? 0 : ZEND_ATTRIBUTE_PERSISTENT; + return zend_add_attribute(&c->attributes, name, argc, flags, 0, 0); +} + void zend_register_attribute_ce(void); void zend_attributes_shutdown(void); diff --git a/Zend/zend_constants.c b/Zend/zend_constants.c index 9a184bd931584..ffad8315ae40d 100644 --- a/Zend/zend_constants.c +++ b/Zend/zend_constants.c @@ -85,7 +85,8 @@ static void copy_zend_constant(zval *zv) c->filename = zend_string_copy(c->filename); } if (c->attributes != NULL) { - c->attributes = zend_array_dup(c->attributes); + // Use the same attributes table + GC_ADDREF(c->attributes); } if (Z_TYPE(c->value) == IS_STRING) { Z_STR(c->value) = zend_string_dup(Z_STR(c->value), 1); diff --git a/Zend/zend_constants.stub.php b/Zend/zend_constants.stub.php index ee67966c0151c..763b207641f69 100644 --- a/Zend/zend_constants.stub.php +++ b/Zend/zend_constants.stub.php @@ -71,9 +71,9 @@ /** * @var int * @cvalue E_STRICT - * @deprecated * @todo Remove in PHP 9.0 */ +#[\Deprecated(since: '8.4', message: 'the error level was removed')] const E_STRICT = UNKNOWN; /** diff --git a/Zend/zend_constants_arginfo.h b/Zend/zend_constants_arginfo.h index 8d42345ad69bc..d381bcf64ee18 100644 --- a/Zend/zend_constants_arginfo.h +++ b/Zend/zend_constants_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 65be08c1bdace83ad1fa1175fc824262e07eac2a */ + * Stub hash: 5e224893a5fb72b3f93249235c2a1634233ce505 */ static void register_zend_constants_symbols(int module_number) { @@ -26,4 +26,18 @@ static void register_zend_constants_symbols(int module_number) REGISTER_BOOL_CONSTANT("TRUE", true, CONST_PERSISTENT); REGISTER_BOOL_CONSTANT("FALSE", false, CONST_PERSISTENT); REGISTER_NULL_CONSTANT("NULL", CONST_PERSISTENT); + + zend_constant *const_E_STRICT = zend_hash_str_find_ptr(EG(zend_constants), "E_STRICT", sizeof("E_STRICT") - 1); + + zend_attribute *attribute_Deprecated_const_E_STRICT_0 = zend_add_global_constant_attribute(const_E_STRICT, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_E_STRICT_0_arg0; + zend_string *attribute_Deprecated_const_E_STRICT_0_arg0_str = zend_string_init("8.4", strlen("8.4"), 1); + ZVAL_STR(&attribute_Deprecated_const_E_STRICT_0_arg0, attribute_Deprecated_const_E_STRICT_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_E_STRICT_0->args[0].value, &attribute_Deprecated_const_E_STRICT_0_arg0); + attribute_Deprecated_const_E_STRICT_0->args[0].name = ZSTR_KNOWN(ZEND_STR_SINCE); + zval attribute_Deprecated_const_E_STRICT_0_arg1; + zend_string *attribute_Deprecated_const_E_STRICT_0_arg1_str = zend_string_init("the error level was removed", strlen("the error level was removed"), 1); + ZVAL_STR(&attribute_Deprecated_const_E_STRICT_0_arg1, attribute_Deprecated_const_E_STRICT_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_E_STRICT_0->args[1].value, &attribute_Deprecated_const_E_STRICT_0_arg1); + attribute_Deprecated_const_E_STRICT_0->args[1].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); } diff --git a/build/gen_stub.php b/build/gen_stub.php index 5e06a53172e07..13ef9e60f334d 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -2622,6 +2622,13 @@ public function __construct( ?ExposedDocComment $exposedDocComment, bool $isFileCacheAllowed ) { + foreach ($attributes as $attr) { + if ($attr->class === "Deprecated") { + $isDeprecated = true; + break; + } + } + $this->name = $name; $this->value = $value; $this->valueString = $valueString; @@ -2915,17 +2922,11 @@ protected function getFlagsByPhpVersion(): array { $flags = parent::getFlagsByPhpVersion(); + // $this->isDeprecated also accounts for any #[\Deprecated] attributes if ($this->isDeprecated) { $flags = $this->addFlagForVersionsAbove($flags, "ZEND_ACC_DEPRECATED", PHP_80_VERSION_ID); } - foreach ($this->attributes as $attr) { - if ($attr->class === "Deprecated") { - $flags = $this->addFlagForVersionsAbove($flags, "ZEND_ACC_DEPRECATED", PHP_80_VERSION_ID); - break; - } - } - if ($this->flags & Modifiers::FINAL) { $flags = $this->addFlagForVersionsAbove($flags, "ZEND_ACC_FINAL", PHP_81_VERSION_ID); } @@ -4340,7 +4341,7 @@ private function handleStatements(array $stmts, PrettyPrinterAbstract $prettyPri $cond, $this->isUndocumentable, $this->getMinimumPhpVersionIdCompatibility(), - [] + AttributeInfo::createFromGroups($stmt->attrGroups) ); } continue; @@ -5177,7 +5178,9 @@ static function (FuncInfo $funcInfo) use ($fileInfo, &$generatedFunctionDeclarat $php80MinimumCompatibility = $fileInfo->getMinimumPhpVersionIdCompatibility() === null || $fileInfo->getMinimumPhpVersionIdCompatibility() >= PHP_80_VERSION_ID; if ($fileInfo->generateClassEntries) { - if ($attributeInitializationCode = generateFunctionAttributeInitialization($fileInfo->funcInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null)) { + $attributeInitializationCode = generateFunctionAttributeInitialization($fileInfo->funcInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null); + $attributeInitializationCode .= generateGlobalConstantAttributeInitialization($fileInfo->constInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null); + if ($attributeInitializationCode) { if (!$php80MinimumCompatibility) { $attributeInitializationCode = "\n#if (PHP_VERSION_ID >= " . PHP_80_VERSION_ID . ")" . $attributeInitializationCode . "#endif\n"; } @@ -5289,6 +5292,51 @@ static function (FuncInfo $funcInfo) use ($allConstInfos, $phpVersionIdMinimumCo ); } +/** + * @param iterable $constInfos + * @param array $allConstInfos + */ +function generateGlobalConstantAttributeInitialization( + iterable $constInfos, + array $allConstInfos, + ?int $phpVersionIdMinimumCompatibility, + ?string $parentCond = null +): string { + $isConditional = false; + if ($phpVersionIdMinimumCompatibility !== null && $phpVersionIdMinimumCompatibility < PHP_85_VERSION_ID) { + $isConditional = true; + $phpVersionIdMinimumCompatibility = PHP_85_VERSION_ID; + } + $code = generateCodeWithConditions( + $constInfos, + "", + static function (ConstInfo $constInfo) use ($allConstInfos, $phpVersionIdMinimumCompatibility) { + if ($constInfo->attributes === []) { + return null; + } + $constName = str_replace('\\', '\\\\', $constInfo->name->__toString()); + $constVarName = 'const_' . $constName; + + $code .= "\tzend_constant *$constVarName = zend_hash_str_find_ptr(EG(zend_constants), \"" . $constName . "\", sizeof(\"" . $constName . "\") - 1);\n"; + foreach ($constInfo->attributes as $key => $attribute) { + $code .= $attribute->generateCode( + "zend_add_global_constant_attribute($constVarName", + $constVarName . "_$key", + $allConstInfos, + $phpVersionIdMinimumCompatibility + ); + } + + return $code; + }, + $parentCond + ); + if ($code && $isConditional) { + return "\n#if (PHP_VERSION_ID >= " . PHP_85_VERSION_ID . ")\n" . $code . "#endif\n"; + } + return $code; +} + /** * @param iterable $constInfos * @param array $allConstInfos @@ -6030,7 +6078,7 @@ function initPhpParser() { } $isInitialized = true; - $version = "5.3.1"; + $version = "5.5.0"; $phpParserDir = __DIR__ . "/PHP-Parser-$version"; if (!is_dir($phpParserDir)) { installPhpParser($version, $phpParserDir); diff --git a/ext/reflection/tests/ReflectionConstant_getAttributes_internal.phpt b/ext/reflection/tests/ReflectionConstant_getAttributes_internal.phpt new file mode 100644 index 0000000000000..ff96fa7cadb58 --- /dev/null +++ b/ext/reflection/tests/ReflectionConstant_getAttributes_internal.phpt @@ -0,0 +1,17 @@ +--TEST-- +ReflectionConstant::getAttributes() with attribute (internal constant) +--FILE-- +getAttributes()); + +?> +--EXPECTF-- +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(10) "Deprecated" + } +} diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 9923da6dceea0..b1debcae74030 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -17,6 +17,12 @@ /** @var string */ const ZEND_CONSTANT_A = "global"; + /** + * @var int + */ + #[\Deprecated(message: "use something else", since: "version 1.5")] + const ZEND_TEST_ATTRIBUTED_CONSTANT = 42; + interface _ZendTestInterface { /** @var int */ diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index d0d12eb66bab6..c49c032013e38 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: 1fd4c80ed74efcc50698748b2afc89391ed69c72 */ + * Stub hash: 37ac76dddea2da24d3275cccc748b8fea4c8d09c */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -579,6 +579,7 @@ static void register_test_symbols(int module_number) { REGISTER_LONG_CONSTANT("ZEND_TEST_DEPRECATED", 42, CONST_PERSISTENT | CONST_DEPRECATED); REGISTER_STRING_CONSTANT("ZEND_CONSTANT_A", "global", CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("ZEND_TEST_ATTRIBUTED_CONSTANT", 42, CONST_PERSISTENT | CONST_DEPRECATED); REGISTER_STRING_CONSTANT("ZendTestNS2\\ZEND_CONSTANT_A", "namespaced", CONST_PERSISTENT); REGISTER_STRING_CONSTANT("ZendTestNS2\\ZendSubNS\\ZEND_CONSTANT_A", "namespaced", CONST_PERSISTENT); @@ -635,6 +636,22 @@ static void register_test_symbols(int module_number) ZVAL_STR(&attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0_arg0, attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0_arg0_str); ZVAL_COPY_VALUE(&attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0->args[0].value, &attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0_arg0); attribute_ZendTestAttributeWithArguments_func_zend_test_attribute_with_named_argument_0->args[0].name = zend_string_init_interned("arg", sizeof("arg") - 1, 1); + +#if (PHP_VERSION_ID >= 80500) + zend_constant *const_ZEND_TEST_ATTRIBUTED_CONSTANT = zend_hash_str_find_ptr(EG(zend_constants), "ZEND_TEST_ATTRIBUTED_CONSTANT", sizeof("ZEND_TEST_ATTRIBUTED_CONSTANT") - 1); + + zend_attribute *attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0 = zend_add_global_constant_attribute(const_ZEND_TEST_ATTRIBUTED_CONSTANT, ZSTR_KNOWN(ZEND_STR_DEPRECATED_CAPITALIZED), 2); + zval attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg0; + zend_string *attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg0_str = zend_string_init("use something else", strlen("use something else"), 1); + ZVAL_STR(&attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg0, attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg0_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0->args[0].value, &attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg0); + attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0->args[0].name = ZSTR_KNOWN(ZEND_STR_MESSAGE); + zval attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg1; + zend_string *attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg1_str = zend_string_init("version 1.5", strlen("version 1.5"), 1); + ZVAL_STR(&attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg1, attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg1_str); + ZVAL_COPY_VALUE(&attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0->args[1].value, &attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0_arg1); + attribute_Deprecated_const_ZEND_TEST_ATTRIBUTED_CONSTANT_0->args[1].name = ZSTR_KNOWN(ZEND_STR_SINCE); +#endif } static zend_class_entry *register_class__ZendTestInterface(void) diff --git a/ext/zend_test/tests/attribute-deprecated.phpt b/ext/zend_test/tests/attribute-deprecated.phpt index a52d0e635c655..e076065cc91d2 100644 --- a/ext/zend_test/tests/attribute-deprecated.phpt +++ b/ext/zend_test/tests/attribute-deprecated.phpt @@ -17,6 +17,12 @@ $reflection = new ReflectionClassConstant('_ZendTestClass', 'ZEND_TEST_DEPRECATE var_dump($reflection->getAttributes()[0]->newInstance()); var_dump($reflection->isDeprecated()); +ZEND_TEST_ATTRIBUTED_CONSTANT; + +$reflection = new ReflectionConstant('ZEND_TEST_ATTRIBUTED_CONSTANT'); +var_dump($reflection->getAttributes()[0]->newInstance()); +var_dump($reflection->isDeprecated()); + ?> --EXPECTF-- Deprecated: Function zend_test_deprecated() is deprecated in %s on line %d @@ -38,3 +44,12 @@ object(Deprecated)#%d (2) { NULL } bool(true) + +Deprecated: Constant ZEND_TEST_ATTRIBUTED_CONSTANT is deprecated since version 1.5, use something else in %s on line %d +object(Deprecated)#%d (2) { + ["message"]=> + string(18) "use something else" + ["since"]=> + string(11) "version 1.5" +} +bool(true) diff --git a/ext/zend_test/tests/gh11423.phpt b/ext/zend_test/tests/gh11423.phpt index fd394126075df..7f70ee33015de 100644 --- a/ext/zend_test/tests/gh11423.phpt +++ b/ext/zend_test/tests/gh11423.phpt @@ -13,11 +13,13 @@ var_dump(get_defined_constants(true)["user"]); ?> --EXPECT-- -array(4) { +array(5) { ["ZEND_TEST_DEPRECATED"]=> int(42) ["ZEND_CONSTANT_A"]=> string(6) "global" + ["ZEND_TEST_ATTRIBUTED_CONSTANT"]=> + int(42) ["ZendTestNS2\ZEND_CONSTANT_A"]=> string(10) "namespaced" ["ZendTestNS2\ZendSubNS\ZEND_CONSTANT_A"]=>