Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Add support for attributes on compile-time constants #16952

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ PHP 8.5 UPGRADE NOTES
- Core:
. Added support for Closures in constant expressions.
RFC: https://wiki.php.net/rfc/closures_in_const_expr
. Added support for attributes on compile-time non-class constants.
. Added constant Attribute::TARGET_CONSTANT.
. The #[\Deprecated] attribute can now be used on constants.
RFC: https://wiki.php.net/rfc/attributes-on-constants

- DOM:
. Added Dom\Element::$outerHTML.
Expand Down Expand Up @@ -158,6 +162,8 @@ PHP 8.5 UPGRADE NOTES
. ReflectionConstant::getFileName() was introduced.
. ReflectionConstant::getExtension() and
ReflectionConstant::getExtensionName() were introduced.
. ReflectionConstant::getAttributes() was introduced.
RFC: https://wiki.php.net/rfc/attributes-on-constants

========================================
7. New Classes and Interfaces
Expand Down
14 changes: 13 additions & 1 deletion Zend/tests/attributes/001_placement.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ $f2 = #[A1(9)] function () { };

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

#[A1(11)]
const CT_CONSTANT = 'Demo';

$ref = new \ReflectionClass(Foo::class);

$sources = [
Expand All @@ -37,7 +40,8 @@ $sources = [
new \ReflectionObject($object),
new \ReflectionFunction('f1'),
new \ReflectionFunction($f2),
new \ReflectionFunction($f3)
new \ReflectionFunction($f3),
new \ReflectionConstant('CT_CONSTANT'),
];

foreach ($sources as $r) {
Expand Down Expand Up @@ -132,3 +136,11 @@ array(1) {
[0]=>
int(10)
}

string(18) "ReflectionConstant"
int(1)
string(2) "A1"
array(1) {
[0]=>
int(11)
}
5 changes: 5 additions & 0 deletions Zend/tests/attributes/029_reflect_internal_symbols.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ var_dump($rcc->getAttributes());
$rp = new ReflectionProperty('Exception', 'message');
var_dump($rp->getAttributes());

$rct = new ReflectionConstant('PHP_VERSION');
var_dump($rct->getAttributes());

?>
--EXPECT--
array(0) {
Expand All @@ -30,3 +33,5 @@ array(0) {
}
array(0) {
}
array(0) {
}
39 changes: 39 additions & 0 deletions Zend/tests/attributes/034_target_values.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
--TEST--
Attribute flags are all different, TARGET_ALL includes all targets
--FILE--
<?php

function showFlag( string $name, int $value ) {
$all = Attribute::TARGET_ALL;
$and = $all & $value;
echo "Attribute::$name = $value ($all & $value === $and)\n";
}

showFlag( "TARGET_CLASS", Attribute::TARGET_CLASS );
showFlag( "TARGET_FUNCTION", Attribute::TARGET_FUNCTION );
showFlag( "TARGET_METHOD", Attribute::TARGET_METHOD );
showFlag( "TARGET_PROPERTY", Attribute::TARGET_PROPERTY );
showFlag( "TARGET_CLASS_CONSTANT", Attribute::TARGET_CLASS_CONSTANT );
showFlag( "TARGET_PARAMETER", Attribute::TARGET_PARAMETER );
showFlag( "TARGET_CONSTANT", Attribute::TARGET_CONSTANT );
showFlag( "IS_REPEATABLE", Attribute::IS_REPEATABLE );

$all = Attribute::TARGET_CLASS | Attribute::TARGET_FUNCTION
| Attribute::TARGET_METHOD | Attribute::TARGET_PROPERTY
| Attribute::TARGET_CLASS_CONSTANT | Attribute::TARGET_PARAMETER
| Attribute::TARGET_CONSTANT;
var_dump( $all, Attribute::TARGET_ALL, $all === Attribute::TARGET_ALL );

?>
--EXPECT--
Attribute::TARGET_CLASS = 1 (127 & 1 === 1)
Attribute::TARGET_FUNCTION = 2 (127 & 2 === 2)
Attribute::TARGET_METHOD = 4 (127 & 4 === 4)
Attribute::TARGET_PROPERTY = 8 (127 & 8 === 8)
Attribute::TARGET_CLASS_CONSTANT = 16 (127 & 16 === 16)
Attribute::TARGET_PARAMETER = 32 (127 & 32 === 32)
Attribute::TARGET_CONSTANT = 64 (127 & 64 === 64)
Attribute::IS_REPEATABLE = 128 (127 & 128 === 0)
int(127)
int(127)
bool(true)
26 changes: 26 additions & 0 deletions Zend/tests/attributes/constants/allow_named_parameters.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
Verify that named parameters can be passed to attributes on constants
--FILE--
<?php

#[MyAttribute(foo: "bar")]
const EXAMPLE = 'Foo';

$ref = new ReflectionConstant('EXAMPLE');
$attribs = $ref->getAttributes();
var_dump( $attribs );
var_dump( $attribs[0]->getArguments() );

?>
--EXPECTF--
array(1) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(11) "MyAttribute"
}
}
array(1) {
["foo"]=>
string(3) "bar"
}
34 changes: 34 additions & 0 deletions Zend/tests/attributes/constants/ast_export.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
--TEST--
AST can be recreated when constants have attributes
--EXTENSIONS--
zend_test
--FILE--
<?php

#[MyAttrib]
const WITH_ATTRIBUTE = true;

#[First]
#[Second]
const WITH_UNGROUPED = true;

#[First, Second]
const WITH_GROUPED = true;

#[MyAttrib(5, param: "example")]
const WITH_PARAMETERS = true;

echo zend_test_compile_to_ast( file_get_contents( __FILE__ ) );

?>
--EXPECT--
#[MyAttrib]
const WITH_ATTRIBUTE = true;
#[First]
#[Second]
const WITH_UNGROUPED = true;
#[First, Second]
const WITH_GROUPED = true;
#[MyAttrib(5, param: 'example')]
const WITH_PARAMETERS = true;
echo zend_test_compile_to_ast(file_get_contents(__FILE__));
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Constants listed in valid targets when used wrong (internal attribute)
--FILE--
<?php

#[Deprecated]
class Example {}

?>
--EXPECTF--
Fatal error: Attribute "Deprecated" cannot target class (allowed targets: function, method, class constant, constant) in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
--TEST--
Constants listed in valid targets when used wrong (userland attribute)
--FILE--
<?php

#[Attribute(Attribute::TARGET_CONSTANT)]
class MyConstantAttribute {}

#[MyConstantAttribute]
class Example {}

$ref = new ReflectionClass(Example::class);
$attribs = $ref->getAttributes();
var_dump($attribs);
$attribs[0]->newInstance();

?>
--EXPECTF--
array(1) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(19) "MyConstantAttribute"
}
}

Fatal error: Uncaught Error: Attribute "MyConstantAttribute" cannot target class (allowed targets: constant) in %s:%d
Stack trace:
#0 %s(%d): ReflectionAttribute->newInstance()
#1 {main}
thrown in %s on line %d
21 changes: 21 additions & 0 deletions Zend/tests/attributes/constants/constant_redefined_addition.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
If a constant is redefined, attributes remain unchanged (no attributes)
--FILE--
<?php

const MY_CONST = "No attributes";

#[\MyAttribute]
const MY_CONST = "Has attributes";

echo MY_CONST . "\n";

$reflection = new ReflectionConstant( 'MY_CONST' );
var_dump( $reflection->getAttributes() )

?>
--EXPECTF--
Warning: Constant MY_CONST already defined in %s on line %d
No attributes
array(0) {
}
27 changes: 27 additions & 0 deletions Zend/tests/attributes/constants/constant_redefined_change.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
--TEST--
If a constant is redefined, attributes remain unchanged (different attributes)
--FILE--
<?php

#[\MyAttribute]
const MY_CONST = "Has attributes (1)";

#[\MyOtherAttribute]
const MY_CONST = "Has attributes (2)";

echo MY_CONST . "\n";

$reflection = new ReflectionConstant( 'MY_CONST' );
var_dump( $reflection->getAttributes() )

?>
--EXPECTF--
Warning: Constant MY_CONST already defined in %s on line %d
Has attributes (1)
array(1) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(11) "MyAttribute"
}
}
26 changes: 26 additions & 0 deletions Zend/tests/attributes/constants/constant_redefined_removal.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
If a constant is redefined, attributes remain unchanged (had attributes)
--FILE--
<?php

#[\MyAttribute]
const MY_CONST = "Has attributes";

const MY_CONST = "No attributes";

echo MY_CONST . "\n";

$reflection = new ReflectionConstant( 'MY_CONST' );
var_dump( $reflection->getAttributes() )

?>
--EXPECTF--
Warning: Constant MY_CONST already defined in %s on line %d
Has attributes
array(1) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(11) "MyAttribute"
}
}
25 changes: 25 additions & 0 deletions Zend/tests/attributes/constants/multiple_attributes_grouped.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
Multiple attributes in a group are allowed
--FILE--
<?php

#[\Foo, \Bar]
const CONSTANT = 1;

$ref = new ReflectionConstant('CONSTANT');
var_dump($ref->getAttributes());

?>
--EXPECTF--
array(2) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(3) "Foo"
}
[1]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(3) "Bar"
}
}
26 changes: 26 additions & 0 deletions Zend/tests/attributes/constants/multiple_attributes_ungrouped.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
Multiple attributes in separate groups are allowed
--FILE--
<?php

#[\Foo]
#[\Bar]
const CONSTANT = 1;

$ref = new ReflectionConstant('CONSTANT');
var_dump($ref->getAttributes());

?>
--EXPECTF--
array(2) {
[0]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(3) "Foo"
}
[1]=>
object(ReflectionAttribute)#%d (1) {
["name"]=>
string(3) "Bar"
}
}
12 changes: 12 additions & 0 deletions Zend/tests/attributes/constants/multiple_constants_error.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
Error trying to add attributes to multiple constants at once
--FILE--
<?php

#[\Foo]
const First = 1,
Second = 2;

?>
--EXPECTF--
Fatal error: Cannot apply attributes to multiple constants at once in %s on line %d
11 changes: 11 additions & 0 deletions Zend/tests/attributes/constants/must_target_const-internal.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
--TEST--
Error when attribute does not target constants (internal attribute)
--FILE--
<?php

#[Attribute]
const EXAMPLE = 'Foo';

?>
--EXPECTF--
Fatal error: Attribute "Attribute" cannot target constant (allowed targets: class) in %s on line %d
Loading
Loading