Skip to content

Commit 29f565e

Browse files
Add support for attributes on compile-time constants
1 parent 4591f04 commit 29f565e

27 files changed

+901
-570
lines changed

Zend/tests/attributes/001_placement.phpt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ $f2 = #[A1(9)] function () { };
2525

2626
$f3 = #[A1(10)] fn () => 1;
2727

28+
#[A1(11)]
29+
const CT_CONSTANT = 'Demo';
30+
2831
$ref = new \ReflectionClass(Foo::class);
2932

3033
$sources = [
@@ -37,7 +40,8 @@ $sources = [
3740
new \ReflectionObject($object),
3841
new \ReflectionFunction('f1'),
3942
new \ReflectionFunction($f2),
40-
new \ReflectionFunction($f3)
43+
new \ReflectionFunction($f3),
44+
new \ReflectionConstant('CT_CONSTANT'),
4145
];
4246

4347
foreach ($sources as $r) {
@@ -132,3 +136,11 @@ array(1) {
132136
[0]=>
133137
int(10)
134138
}
139+
140+
string(18) "ReflectionConstant"
141+
int(1)
142+
string(2) "A1"
143+
array(1) {
144+
[0]=>
145+
int(11)
146+
}

Zend/tests/attributes/029_reflect_internal_symbols.phpt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ var_dump($rcc->getAttributes());
1818
$rp = new ReflectionProperty('Exception', 'message');
1919
var_dump($rp->getAttributes());
2020

21+
$rct = new ReflectionConstant('PHP_VERSION');
22+
var_dump($rct->getAttributes());
23+
2124
?>
2225
--EXPECT--
2326
array(0) {
@@ -30,3 +33,5 @@ array(0) {
3033
}
3134
array(0) {
3235
}
36+
array(0) {
37+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Error trying to add attributes to multiple constants at once
3+
--FILE--
4+
<?php
5+
6+
#[\Foo]
7+
const First = 1,
8+
Second = 2;
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Cannot apply attributes to multiple constants at once in %s on line %d
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
--TEST--
2+
#[\Deprecated]: Messages on compile time constants.
3+
--FILE--
4+
<?php
5+
6+
#[\Deprecated]
7+
const DeprecatedConst1 = 1;
8+
9+
#[\Deprecated("use DEPRECATED_CONST_2")]
10+
const DeprecatedConst2 = 2;
11+
12+
#[\Deprecated(message: "use DEPRECATED_CONST_3")]
13+
const DeprecatedConst3 = 3;
14+
15+
#[\Deprecated(message: "use DEPRECATED_CONST_4", since: "1.0")]
16+
const DeprecatedConst4 = 4;
17+
18+
#[\Deprecated(since: "1.0")]
19+
const DeprecatedConst5 = 5;
20+
21+
echo DeprecatedConst1 . "\n";
22+
echo DeprecatedConst2 . "\n";
23+
echo DeprecatedConst3 . "\n";
24+
echo DeprecatedConst4 . "\n";
25+
echo DeprecatedConst5 . "\n";
26+
?>
27+
--EXPECTF--
28+
Deprecated: Constant DeprecatedConst1 is deprecated in %s on line %d
29+
1
30+
31+
Deprecated: Constant DeprecatedConst2 is deprecated, use DEPRECATED_CONST_2 in %s on line %d
32+
2
33+
34+
Deprecated: Constant DeprecatedConst3 is deprecated, use DEPRECATED_CONST_3 in %s on line %d
35+
3
36+
37+
Deprecated: Constant DeprecatedConst4 is deprecated since 1.0, use DEPRECATED_CONST_4 in %s on line %d
38+
4
39+
40+
Deprecated: Constant DeprecatedConst5 is deprecated since 1.0 in %s on line %d
41+
5
42+

Zend/zend_ast.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2707,6 +2707,9 @@ zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr)
27072707
case ZEND_AST_CLASS_CONST_GROUP:
27082708
ast->child[1] = attr;
27092709
break;
2710+
case ZEND_AST_CONST_DECL:
2711+
zend_ast_list_add(ast, attr);
2712+
break;
27102713
EMPTY_SWITCH_DEFAULT_CASE()
27112714
}
27122715

Zend/zend_attributes.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,8 @@ static const char *target_names[] = {
354354
"method",
355355
"property",
356356
"class constant",
357-
"parameter"
357+
"parameter",
358+
"constant"
358359
};
359360

360361
ZEND_API zend_string *zend_get_attribute_target_names(uint32_t flags)

Zend/zend_attributes.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@
2828
#define ZEND_ATTRIBUTE_TARGET_PROPERTY (1<<3)
2929
#define ZEND_ATTRIBUTE_TARGET_CLASS_CONST (1<<4)
3030
#define ZEND_ATTRIBUTE_TARGET_PARAMETER (1<<5)
31-
#define ZEND_ATTRIBUTE_TARGET_ALL ((1<<6) - 1)
32-
#define ZEND_ATTRIBUTE_IS_REPEATABLE (1<<6)
33-
#define ZEND_ATTRIBUTE_FLAGS ((1<<7) - 1)
31+
#define ZEND_ATTRIBUTE_TARGET_CONST (1<<6)
32+
#define ZEND_ATTRIBUTE_TARGET_ALL ((1<<7) - 1)
33+
#define ZEND_ATTRIBUTE_IS_REPEATABLE (1<<7)
34+
#define ZEND_ATTRIBUTE_FLAGS ((1<<8) - 1)
3435

3536
/* Flags for zend_attribute.flags */
3637
#define ZEND_ATTRIBUTE_PERSISTENT (1<<0)

Zend/zend_attributes.stub.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ final class Attribute
1717
const int TARGET_CLASS_CONSTANT = UNKNOWN;
1818
/** @cvalue ZEND_ATTRIBUTE_TARGET_PARAMETER */
1919
const int TARGET_PARAMETER = UNKNOWN;
20+
/** @cvalue ZEND_ATTRIBUTE_TARGET_CONST */
21+
const int TARGET_CONSTANT = UNKNOWN;
2022
/** @cvalue ZEND_ATTRIBUTE_TARGET_ALL */
2123
const int TARGET_ALL = UNKNOWN;
2224
/** @cvalue ZEND_ATTRIBUTE_IS_REPEATABLE */
@@ -75,7 +77,7 @@ public function __construct() {}
7577
/**
7678
* @strict-properties
7779
*/
78-
#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION|Attribute::TARGET_CLASS_CONSTANT)]
80+
#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION|Attribute::TARGET_CLASS_CONSTANT|Attribute::TARGET_CONSTANT)]
7981
final class Deprecated
8082
{
8183
public readonly ?string $message;

Zend/zend_attributes_arginfo.h

Lines changed: 8 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Zend/zend_compile.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9415,8 +9415,14 @@ static void zend_compile_const_decl(zend_ast *ast) /* {{{ */
94159415
{
94169416
zend_ast_list *list = zend_ast_get_list(ast);
94179417
uint32_t i;
9418+
zend_ast *attributes_ast = NULL;
9419+
zend_op *last_op = NULL;
94189420
for (i = 0; i < list->children; ++i) {
94199421
zend_ast *const_ast = list->child[i];
9422+
if (i == list->children - 1 && const_ast->kind == ZEND_AST_ATTRIBUTE_LIST) {
9423+
attributes_ast = const_ast;
9424+
continue;
9425+
}
94209426
zend_ast *name_ast = const_ast->child[0];
94219427
zend_ast **value_ast_ptr = &const_ast->child[1];
94229428
zend_string *unqualified_name = zend_ast_get_str(name_ast);
@@ -9447,10 +9453,30 @@ static void zend_compile_const_decl(zend_ast *ast) /* {{{ */
94479453
name_node.op_type = IS_CONST;
94489454
ZVAL_STR(&name_node.u.constant, name);
94499455

9450-
zend_emit_op(NULL, ZEND_DECLARE_CONST, &name_node, &value_node);
9456+
last_op = zend_emit_op(NULL, ZEND_DECLARE_CONST, &name_node, &value_node);
94519457

94529458
zend_register_seen_symbol(name, ZEND_SYMBOL_CONST);
94539459
}
9460+
if (attributes_ast == NULL) {
9461+
return;
9462+
}
9463+
// Validate: attributes can only be applied to one constant at a time
9464+
// Since we store the AST for the attributes in the list of children,
9465+
// there should be exactly 2 children
9466+
if (list->children > 2) {
9467+
zend_error_noreturn(
9468+
E_COMPILE_ERROR,
9469+
"Cannot apply attributes to multiple constants at once"
9470+
);
9471+
}
9472+
ZEND_ASSERT(last_op != NULL);
9473+
last_op->opcode = ZEND_DECLARE_ATTRIBUTED_CONST;
9474+
HashTable *attribs_ht = NULL;
9475+
zend_compile_attributes(&attribs_ht, list->child[1], 0, ZEND_ATTRIBUTE_TARGET_CONST, 0);
9476+
znode attribs_node;
9477+
attribs_node.op_type = IS_CONST;
9478+
ZVAL_PTR(&attribs_node.u.constant, attribs_ht);
9479+
zend_emit_op_data(&attribs_node);
94549480
}
94559481
/* }}}*/
94569482

0 commit comments

Comments
 (0)