Skip to content

Commit 59b778a

Browse files
[RFC] Add support for attributes on compile-time constants
https://wiki.php.net/rfc/attributes-on-constants
1 parent 787f26c commit 59b778a

File tree

65 files changed

+1829
-581
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+1829
-581
lines changed

Diff for: UPGRADING

+6
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ PHP 8.5 UPGRADE NOTES
130130
RFC: https://wiki.php.net/rfc/marking_return_value_as_important
131131
. Added asymmetric visibility support for static properties.
132132
RFC: https://wiki.php.net/rfc/static-aviz
133+
. Added support for attributes on compile-time non-class constants.
134+
. Added constant Attribute::TARGET_CONSTANT.
135+
. The #[\Deprecated] attribute can now be used on constants.
136+
RFC: https://wiki.php.net/rfc/attributes-on-constants
133137

134138
- Curl:
135139
. Added support for share handles that are persisted across multiple PHP
@@ -314,6 +318,8 @@ PHP 8.5 UPGRADE NOTES
314318
. ReflectionConstant::getFileName() was introduced.
315319
. ReflectionConstant::getExtension() and
316320
ReflectionConstant::getExtensionName() were introduced.
321+
. ReflectionConstant::getAttributes() was introduced.
322+
RFC: https://wiki.php.net/rfc/attributes-on-constants
317323

318324
========================================
319325
7. New Classes and Interfaces

Diff for: Zend/tests/attributes/001_placement.phpt

+13-1
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+
}

Diff for: Zend/tests/attributes/029_reflect_internal_symbols.phpt

+5
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+
}

Diff for: Zend/tests/attributes/034_target_values.phpt

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
Attribute flags are all different, TARGET_ALL includes all targets
3+
--FILE--
4+
<?php
5+
6+
function showFlag(string $name, int $value) {
7+
$all = Attribute::TARGET_ALL;
8+
$and = $all & $value;
9+
echo "Attribute::$name = $value ($all & $value === $and)\n";
10+
}
11+
12+
showFlag("TARGET_CLASS", Attribute::TARGET_CLASS);
13+
showFlag("TARGET_FUNCTION", Attribute::TARGET_FUNCTION);
14+
showFlag("TARGET_METHOD", Attribute::TARGET_METHOD);
15+
showFlag("TARGET_PROPERTY", Attribute::TARGET_PROPERTY);
16+
showFlag("TARGET_CLASS_CONSTANT", Attribute::TARGET_CLASS_CONSTANT);
17+
showFlag("TARGET_PARAMETER", Attribute::TARGET_PARAMETER);
18+
showFlag("TARGET_CONSTANT", Attribute::TARGET_CONSTANT);
19+
showFlag("IS_REPEATABLE", Attribute::IS_REPEATABLE);
20+
21+
$all = Attribute::TARGET_CLASS | Attribute::TARGET_FUNCTION
22+
| Attribute::TARGET_METHOD | Attribute::TARGET_PROPERTY
23+
| Attribute::TARGET_CLASS_CONSTANT | Attribute::TARGET_PARAMETER
24+
| Attribute::TARGET_CONSTANT;
25+
var_dump($all, Attribute::TARGET_ALL, $all === Attribute::TARGET_ALL);
26+
27+
?>
28+
--EXPECT--
29+
Attribute::TARGET_CLASS = 1 (127 & 1 === 1)
30+
Attribute::TARGET_FUNCTION = 2 (127 & 2 === 2)
31+
Attribute::TARGET_METHOD = 4 (127 & 4 === 4)
32+
Attribute::TARGET_PROPERTY = 8 (127 & 8 === 8)
33+
Attribute::TARGET_CLASS_CONSTANT = 16 (127 & 16 === 16)
34+
Attribute::TARGET_PARAMETER = 32 (127 & 32 === 32)
35+
Attribute::TARGET_CONSTANT = 64 (127 & 64 === 64)
36+
Attribute::IS_REPEATABLE = 128 (127 & 128 === 0)
37+
int(127)
38+
int(127)
39+
bool(true)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
Verify that named parameters can be passed to attributes on constants
3+
--FILE--
4+
<?php
5+
6+
#[Attribute]
7+
class MyAttribute {
8+
public function __construct($first, $second) {
9+
echo "first: $first\n";
10+
echo "second: $second\n";
11+
}
12+
}
13+
14+
#[MyAttribute(second: "bar", first: "foo")]
15+
const EXAMPLE = 'ignored';
16+
17+
$ref = new ReflectionConstant('EXAMPLE');
18+
$attribs = $ref->getAttributes();
19+
var_dump($attribs);
20+
var_dump($attribs[0]->getArguments());
21+
$attribs[0]->newInstance();
22+
23+
?>
24+
--EXPECTF--
25+
array(1) {
26+
[0]=>
27+
object(ReflectionAttribute)#%d (1) {
28+
["name"]=>
29+
string(11) "MyAttribute"
30+
}
31+
}
32+
array(2) {
33+
["second"]=>
34+
string(3) "bar"
35+
["first"]=>
36+
string(3) "foo"
37+
}
38+
first: foo
39+
second: bar

Diff for: Zend/tests/attributes/constants/ast_export.phpt

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
--TEST--
2+
AST can be recreated when constants have attributes
3+
--EXTENSIONS--
4+
zend_test
5+
--FILE--
6+
<?php
7+
8+
#[MyAttrib]
9+
const WITH_ATTRIBUTE = true;
10+
11+
#[First]
12+
#[Second]
13+
const WITH_UNGROUPED = true;
14+
15+
#[First, Second]
16+
const WITH_GROUPED = true;
17+
18+
#[MyAttrib(5, param: "example")]
19+
const WITH_PARAMETERS = true;
20+
21+
echo zend_test_compile_to_ast(file_get_contents(__FILE__));
22+
23+
?>
24+
--EXPECT--
25+
#[MyAttrib]
26+
const WITH_ATTRIBUTE = true;
27+
#[First]
28+
#[Second]
29+
const WITH_UNGROUPED = true;
30+
#[First, Second]
31+
const WITH_GROUPED = true;
32+
#[MyAttrib(5, param: 'example')]
33+
const WITH_PARAMETERS = true;
34+
echo zend_test_compile_to_ast(file_get_contents(__FILE__));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Constants listed in valid targets when used wrong (internal attribute)
3+
--FILE--
4+
<?php
5+
6+
#[Deprecated]
7+
class Example {}
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Attribute "Deprecated" cannot target class (allowed targets: function, method, class constant, constant) in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--TEST--
2+
Constants listed in valid targets when used wrong (userland attribute)
3+
--FILE--
4+
<?php
5+
6+
#[Attribute(Attribute::TARGET_CONSTANT)]
7+
class MyConstantAttribute {}
8+
9+
#[MyConstantAttribute]
10+
class Example {}
11+
12+
$ref = new ReflectionClass(Example::class);
13+
$attribs = $ref->getAttributes();
14+
var_dump($attribs);
15+
$attribs[0]->newInstance();
16+
17+
?>
18+
--EXPECTF--
19+
array(1) {
20+
[0]=>
21+
object(ReflectionAttribute)#%d (1) {
22+
["name"]=>
23+
string(19) "MyConstantAttribute"
24+
}
25+
}
26+
27+
Fatal error: Uncaught Error: Attribute "MyConstantAttribute" cannot target class (allowed targets: constant) in %s:%d
28+
Stack trace:
29+
#0 %s(%d): ReflectionAttribute->newInstance()
30+
#1 {main}
31+
thrown in %s on line %d
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
If a constant is redefined, attributes remain unchanged (no attributes)
3+
--FILE--
4+
<?php
5+
6+
const MY_CONST = "No attributes";
7+
8+
#[\MyAttribute]
9+
const MY_CONST = "Has attributes";
10+
11+
echo MY_CONST . "\n";
12+
13+
$reflection = new ReflectionConstant('MY_CONST');
14+
var_dump($reflection->getAttributes())
15+
16+
?>
17+
--EXPECTF--
18+
Warning: Constant MY_CONST already defined in %s on line %d
19+
No attributes
20+
array(0) {
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
--TEST--
2+
If a constant is redefined, attributes remain unchanged (different attributes)
3+
--FILE--
4+
<?php
5+
6+
#[\MyAttribute]
7+
const MY_CONST = "Has attributes (1)";
8+
9+
#[\MyOtherAttribute]
10+
const MY_CONST = "Has attributes (2)";
11+
12+
echo MY_CONST . "\n";
13+
14+
$reflection = new ReflectionConstant('MY_CONST');
15+
var_dump($reflection->getAttributes())
16+
17+
?>
18+
--EXPECTF--
19+
Warning: Constant MY_CONST already defined in %s on line %d
20+
Has attributes (1)
21+
array(1) {
22+
[0]=>
23+
object(ReflectionAttribute)#%d (1) {
24+
["name"]=>
25+
string(11) "MyAttribute"
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
If a constant is redefined, attributes remain unchanged (had attributes)
3+
--FILE--
4+
<?php
5+
6+
#[\MyAttribute]
7+
const MY_CONST = "Has attributes";
8+
9+
const MY_CONST = "No attributes";
10+
11+
echo MY_CONST . "\n";
12+
13+
$reflection = new ReflectionConstant('MY_CONST');
14+
var_dump($reflection->getAttributes())
15+
16+
?>
17+
--EXPECTF--
18+
Warning: Constant MY_CONST already defined in %s on line %d
19+
Has attributes
20+
array(1) {
21+
[0]=>
22+
object(ReflectionAttribute)#%d (1) {
23+
["name"]=>
24+
string(11) "MyAttribute"
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
Multiple attributes in a group are allowed
3+
--FILE--
4+
<?php
5+
6+
#[\Foo, \Bar]
7+
const CONSTANT = 1;
8+
9+
$ref = new ReflectionConstant('CONSTANT');
10+
var_dump($ref->getAttributes());
11+
12+
?>
13+
--EXPECTF--
14+
array(2) {
15+
[0]=>
16+
object(ReflectionAttribute)#%d (1) {
17+
["name"]=>
18+
string(3) "Foo"
19+
}
20+
[1]=>
21+
object(ReflectionAttribute)#%d (1) {
22+
["name"]=>
23+
string(3) "Bar"
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
Multiple attributes in separate groups are allowed
3+
--FILE--
4+
<?php
5+
6+
#[\Foo]
7+
#[\Bar]
8+
const CONSTANT = 1;
9+
10+
$ref = new ReflectionConstant('CONSTANT');
11+
var_dump($ref->getAttributes());
12+
13+
?>
14+
--EXPECTF--
15+
array(2) {
16+
[0]=>
17+
object(ReflectionAttribute)#%d (1) {
18+
["name"]=>
19+
string(3) "Foo"
20+
}
21+
[1]=>
22+
object(ReflectionAttribute)#%d (1) {
23+
["name"]=>
24+
string(3) "Bar"
25+
}
26+
}
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
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Error when attribute does not target constants (internal attribute)
3+
--FILE--
4+
<?php
5+
6+
#[Attribute]
7+
const EXAMPLE = 'Foo';
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Attribute "Attribute" cannot target constant (allowed targets: class) in %s on line %d

0 commit comments

Comments
 (0)