Skip to content

Commit 8f3cdf6

Browse files
gen_stub: Add support for attributes on constants in stubs (#18735)
Update to PHP-Parser 5.5.0 and add support for attributes on constants in stubs. For now, I have only migrated over E_STRICT, once the support is in place I'll do a larger migration of the existing deprecated constants. In the process, fix the logic in `copy_zend_constant()` for copying attributes when a constant is copied; just increase the reference count for the attributes table rather than trying to duplicate the contents.
1 parent 99d5624 commit 8f3cdf6

11 files changed

+143
-16
lines changed

Zend/tests/e_strict-deprecated.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ var_dump(E_STRICT);
1010
--EXPECTF--
1111
int(30719)
1212

13-
Deprecated: Constant E_STRICT is deprecated in %s on line %d
13+
Deprecated: Constant E_STRICT is deprecated since 8.4, the error level was removed in %s on line %d
1414
int(2048)

Zend/zend_attributes.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#define ZEND_ATTRIBUTES_H
2222

2323
#include "zend_compile.h"
24+
#include "zend_constants.h"
2425

2526
#define ZEND_ATTRIBUTE_TARGET_CLASS (1<<0)
2627
#define ZEND_ATTRIBUTE_TARGET_FUNCTION (1<<1)
@@ -126,6 +127,12 @@ static zend_always_inline zend_attribute *zend_add_class_constant_attribute(zend
126127
return zend_add_attribute(&c->attributes, name, argc, flags, 0, 0);
127128
}
128129

130+
static zend_always_inline zend_attribute *zend_add_global_constant_attribute(zend_constant *c, zend_string *name, uint32_t argc)
131+
{
132+
uint32_t flags = ZEND_CONSTANT_MODULE_NUMBER(c) == PHP_USER_CONSTANT ? 0 : ZEND_ATTRIBUTE_PERSISTENT;
133+
return zend_add_attribute(&c->attributes, name, argc, flags, 0, 0);
134+
}
135+
129136
void zend_register_attribute_ce(void);
130137
void zend_attributes_shutdown(void);
131138

Zend/zend_constants.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ static void copy_zend_constant(zval *zv)
8585
c->filename = zend_string_copy(c->filename);
8686
}
8787
if (c->attributes != NULL) {
88-
c->attributes = zend_array_dup(c->attributes);
88+
// Use the same attributes table
89+
GC_ADDREF(c->attributes);
8990
}
9091
if (Z_TYPE(c->value) == IS_STRING) {
9192
Z_STR(c->value) = zend_string_dup(Z_STR(c->value), 1);

Zend/zend_constants.stub.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@
7171
/**
7272
* @var int
7373
* @cvalue E_STRICT
74-
* @deprecated
7574
* @todo Remove in PHP 9.0
7675
*/
76+
#[\Deprecated(since: '8.4', message: 'the error level was removed')]
7777
const E_STRICT = UNKNOWN;
7878

7979
/**

Zend/zend_constants_arginfo.h

Lines changed: 15 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build/gen_stub.php

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2622,6 +2622,13 @@ public function __construct(
26222622
?ExposedDocComment $exposedDocComment,
26232623
bool $isFileCacheAllowed
26242624
) {
2625+
foreach ($attributes as $attr) {
2626+
if ($attr->class === "Deprecated") {
2627+
$isDeprecated = true;
2628+
break;
2629+
}
2630+
}
2631+
26252632
$this->name = $name;
26262633
$this->value = $value;
26272634
$this->valueString = $valueString;
@@ -2915,17 +2922,11 @@ protected function getFlagsByPhpVersion(): array
29152922
{
29162923
$flags = parent::getFlagsByPhpVersion();
29172924

2925+
// $this->isDeprecated also accounts for any #[\Deprecated] attributes
29182926
if ($this->isDeprecated) {
29192927
$flags = $this->addFlagForVersionsAbove($flags, "ZEND_ACC_DEPRECATED", PHP_80_VERSION_ID);
29202928
}
29212929

2922-
foreach ($this->attributes as $attr) {
2923-
if ($attr->class === "Deprecated") {
2924-
$flags = $this->addFlagForVersionsAbove($flags, "ZEND_ACC_DEPRECATED", PHP_80_VERSION_ID);
2925-
break;
2926-
}
2927-
}
2928-
29292930
if ($this->flags & Modifiers::FINAL) {
29302931
$flags = $this->addFlagForVersionsAbove($flags, "ZEND_ACC_FINAL", PHP_81_VERSION_ID);
29312932
}
@@ -4340,7 +4341,7 @@ private function handleStatements(array $stmts, PrettyPrinterAbstract $prettyPri
43404341
$cond,
43414342
$this->isUndocumentable,
43424343
$this->getMinimumPhpVersionIdCompatibility(),
4343-
[]
4344+
AttributeInfo::createFromGroups($stmt->attrGroups)
43444345
);
43454346
}
43464347
continue;
@@ -5177,7 +5178,9 @@ static function (FuncInfo $funcInfo) use ($fileInfo, &$generatedFunctionDeclarat
51775178
$php80MinimumCompatibility = $fileInfo->getMinimumPhpVersionIdCompatibility() === null || $fileInfo->getMinimumPhpVersionIdCompatibility() >= PHP_80_VERSION_ID;
51785179

51795180
if ($fileInfo->generateClassEntries) {
5180-
if ($attributeInitializationCode = generateFunctionAttributeInitialization($fileInfo->funcInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null)) {
5181+
$attributeInitializationCode = generateFunctionAttributeInitialization($fileInfo->funcInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null);
5182+
$attributeInitializationCode .= generateGlobalConstantAttributeInitialization($fileInfo->constInfos, $allConstInfos, $fileInfo->getMinimumPhpVersionIdCompatibility(), null);
5183+
if ($attributeInitializationCode) {
51815184
if (!$php80MinimumCompatibility) {
51825185
$attributeInitializationCode = "\n#if (PHP_VERSION_ID >= " . PHP_80_VERSION_ID . ")" . $attributeInitializationCode . "#endif\n";
51835186
}
@@ -5289,6 +5292,51 @@ static function (FuncInfo $funcInfo) use ($allConstInfos, $phpVersionIdMinimumCo
52895292
);
52905293
}
52915294

5295+
/**
5296+
* @param iterable<ConstInfo> $constInfos
5297+
* @param array<string, ConstInfo> $allConstInfos
5298+
*/
5299+
function generateGlobalConstantAttributeInitialization(
5300+
iterable $constInfos,
5301+
array $allConstInfos,
5302+
?int $phpVersionIdMinimumCompatibility,
5303+
?string $parentCond = null
5304+
): string {
5305+
$isConditional = false;
5306+
if ($phpVersionIdMinimumCompatibility !== null && $phpVersionIdMinimumCompatibility < PHP_85_VERSION_ID) {
5307+
$isConditional = true;
5308+
$phpVersionIdMinimumCompatibility = PHP_85_VERSION_ID;
5309+
}
5310+
$code = generateCodeWithConditions(
5311+
$constInfos,
5312+
"",
5313+
static function (ConstInfo $constInfo) use ($allConstInfos, $phpVersionIdMinimumCompatibility) {
5314+
if ($constInfo->attributes === []) {
5315+
return null;
5316+
}
5317+
$constName = str_replace('\\', '\\\\', $constInfo->name->__toString());
5318+
$constVarName = 'const_' . $constName;
5319+
5320+
$code .= "\tzend_constant *$constVarName = zend_hash_str_find_ptr(EG(zend_constants), \"" . $constName . "\", sizeof(\"" . $constName . "\") - 1);\n";
5321+
foreach ($constInfo->attributes as $key => $attribute) {
5322+
$code .= $attribute->generateCode(
5323+
"zend_add_global_constant_attribute($constVarName",
5324+
$constVarName . "_$key",
5325+
$allConstInfos,
5326+
$phpVersionIdMinimumCompatibility
5327+
);
5328+
}
5329+
5330+
return $code;
5331+
},
5332+
$parentCond
5333+
);
5334+
if ($code && $isConditional) {
5335+
return "\n#if (PHP_VERSION_ID >= " . PHP_85_VERSION_ID . ")\n" . $code . "#endif\n";
5336+
}
5337+
return $code;
5338+
}
5339+
52925340
/**
52935341
* @param iterable<ConstInfo> $constInfos
52945342
* @param array<string, ConstInfo> $allConstInfos
@@ -6030,7 +6078,7 @@ function initPhpParser() {
60306078
}
60316079

60326080
$isInitialized = true;
6033-
$version = "5.3.1";
6081+
$version = "5.5.0";
60346082
$phpParserDir = __DIR__ . "/PHP-Parser-$version";
60356083
if (!is_dir($phpParserDir)) {
60366084
installPhpParser($version, $phpParserDir);
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
ReflectionConstant::getAttributes() with attribute (internal constant)
3+
--FILE--
4+
<?php
5+
6+
$reflectionConstant = new ReflectionConstant('E_STRICT');
7+
var_dump($reflectionConstant->getAttributes());
8+
9+
?>
10+
--EXPECTF--
11+
array(1) {
12+
[0]=>
13+
object(ReflectionAttribute)#%d (1) {
14+
["name"]=>
15+
string(10) "Deprecated"
16+
}
17+
}

ext/zend_test/test.stub.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
/** @var string */
1818
const ZEND_CONSTANT_A = "global";
1919

20+
/**
21+
* @var int
22+
*/
23+
#[\Deprecated(message: "use something else", since: "version 1.5")]
24+
const ZEND_TEST_ATTRIBUTED_CONSTANT = 42;
25+
2026
interface _ZendTestInterface
2127
{
2228
/** @var int */

ext/zend_test/test_arginfo.h

Lines changed: 18 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/zend_test/tests/attribute-deprecated.phpt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ $reflection = new ReflectionClassConstant('_ZendTestClass', 'ZEND_TEST_DEPRECATE
1717
var_dump($reflection->getAttributes()[0]->newInstance());
1818
var_dump($reflection->isDeprecated());
1919

20+
ZEND_TEST_ATTRIBUTED_CONSTANT;
21+
22+
$reflection = new ReflectionConstant('ZEND_TEST_ATTRIBUTED_CONSTANT');
23+
var_dump($reflection->getAttributes()[0]->newInstance());
24+
var_dump($reflection->isDeprecated());
25+
2026
?>
2127
--EXPECTF--
2228
Deprecated: Function zend_test_deprecated() is deprecated in %s on line %d
@@ -38,3 +44,12 @@ object(Deprecated)#%d (2) {
3844
NULL
3945
}
4046
bool(true)
47+
48+
Deprecated: Constant ZEND_TEST_ATTRIBUTED_CONSTANT is deprecated since version 1.5, use something else in %s on line %d
49+
object(Deprecated)#%d (2) {
50+
["message"]=>
51+
string(18) "use something else"
52+
["since"]=>
53+
string(11) "version 1.5"
54+
}
55+
bool(true)

ext/zend_test/tests/gh11423.phpt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@ var_dump(get_defined_constants(true)["user"]);
1313

1414
?>
1515
--EXPECT--
16-
array(4) {
16+
array(5) {
1717
["ZEND_TEST_DEPRECATED"]=>
1818
int(42)
1919
["ZEND_CONSTANT_A"]=>
2020
string(6) "global"
21+
["ZEND_TEST_ATTRIBUTED_CONSTANT"]=>
22+
int(42)
2123
["ZendTestNS2\ZEND_CONSTANT_A"]=>
2224
string(10) "namespaced"
2325
["ZendTestNS2\ZendSubNS\ZEND_CONSTANT_A"]=>

0 commit comments

Comments
 (0)