summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTheodore Brown <theodorejb@outlook.com>2020-07-02 09:56:21 -0600
committerNikita Popov <nikita.ppv@gmail.com>2020-07-28 15:28:57 +0200
commit470d1696d9a4057ab72b90941556f78990f61692 (patch)
tree30dc7f275ad3749fe3aff5bc862bc3b67f016e8f
parent44ad2db15fe786b07ce793624885e0c43e0af897 (diff)
downloadphp-git-470d1696d9a4057ab72b90941556f78990f61692.tar.gz
Implement Shorter Attribute Syntax
RFC: https://wiki.php.net/rfc/shorter_attribute_syntax Closes GH-5796. Co-authored-by: Martin Schröder <m.schroeder2007@gmail.com>
-rw-r--r--UPGRADING5
-rw-r--r--Zend/tests/attributes/001_placement.phpt18
-rw-r--r--Zend/tests/attributes/002_rfcexample.phpt4
-rw-r--r--Zend/tests/attributes/003_ast_nodes.phpt12
-rw-r--r--Zend/tests/attributes/004_name_resolution.phpt10
-rw-r--r--Zend/tests/attributes/005_objects.phpt20
-rw-r--r--Zend/tests/attributes/006_filter.phpt16
-rw-r--r--Zend/tests/attributes/008_wrong_attribution.phpt2
-rw-r--r--Zend/tests/attributes/009_doctrine_annotations_example.phpt22
-rw-r--r--Zend/tests/attributes/010_unsupported_const_expression.phpt2
-rw-r--r--Zend/tests/attributes/011_inheritance.phpt8
-rw-r--r--Zend/tests/attributes/012_ast_export.phpt38
-rw-r--r--Zend/tests/attributes/013_class_scope.phpt16
-rw-r--r--Zend/tests/attributes/014_class_const_group.phpt2
-rw-r--r--Zend/tests/attributes/015_property_group.phpt2
-rw-r--r--Zend/tests/attributes/016_custom_attribute_validation.phpt (renamed from Zend/tests/attributes/016_target_resolution_compiler_attributes.phpt)6
-rw-r--r--Zend/tests/attributes/017_closure_scope.phpt2
-rw-r--r--Zend/tests/attributes/018_fatal_error_in_argument.phpt2
-rw-r--r--Zend/tests/attributes/019_variable_attribute_name.phpt2
-rw-r--r--Zend/tests/attributes/020_userland_attribute_validation.phpt12
-rw-r--r--Zend/tests/attributes/021_attribute_flags_type_is_validated.phpt2
-rw-r--r--Zend/tests/attributes/022_attribute_flags_value_is_validated.phpt2
-rw-r--r--Zend/tests/attributes/023_ast_node_in_validation.phpt2
-rw-r--r--Zend/tests/attributes/024_internal_target_validation.phpt2
-rw-r--r--Zend/tests/attributes/025_internal_repeatable_validation.phpt4
-rw-r--r--Zend/tests/attributes/026_unpack_in_args.phpt2
-rw-r--r--Zend/tests/attributes/027_trailing_comma_args.phpt4
-rw-r--r--Zend/tests/ctor_promotion_attributes.phpt2
-rw-r--r--Zend/zend_ast.c4
-rw-r--r--Zend/zend_language_parser.y3
-rw-r--r--Zend/zend_language_scanner.l4
-rw-r--r--ext/tokenizer/tests/attributes.phpt20
-rw-r--r--ext/tokenizer/tokenizer_data.c2
-rw-r--r--ext/zend_test/test.c2
34 files changed, 144 insertions, 112 deletions
diff --git a/UPGRADING b/UPGRADING
index 7a9c92ee65..6eca850664 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -78,6 +78,10 @@ PHP 8.0 UPGRADE NOTES
Additionally, care should be taken that error messages are not displayed in
production environments, which can result in information leaks. Please
ensure that display_errors=Off is used in conjunction with error logging.
+ . Adding more than one @ operator to an expression is no longer supported,
+ since this syntax is now used for attributes (previously extra @ operators
+ had no effect).
+ RFC: https://wiki.php.net/rfc/shorter_attribute_syntax
. Inheritance errors due to incompatible method signatures (LSP violations)
will now always generate a fatal error. Previously a warning was generated
in some cases.
@@ -605,6 +609,7 @@ PHP 8.0 UPGRADE NOTES
. Added support for Attributes
RFC: https://wiki.php.net/rfc/attributes_v2
RFC: https://wiki.php.net/rfc/attribute_amendments
+ RFC: https://wiki.php.net/rfc/shorter_attribute_syntax
. Added support for constructor property promotion (declaring properties in
the constructor signature).
RFC: https://wiki.php.net/rfc/constructor_promotion
diff --git a/Zend/tests/attributes/001_placement.phpt b/Zend/tests/attributes/001_placement.phpt
index cf7bcd4504..9bf9c4d2f7 100644
--- a/Zend/tests/attributes/001_placement.phpt
+++ b/Zend/tests/attributes/001_placement.phpt
@@ -3,27 +3,27 @@ Attributes can be placed on all supported elements.
--FILE--
<?php
-<<A1(1)>>
+@@A1(1)
class Foo
{
- <<A1(2)>>
+ @@A1(2)
public const FOO = 'foo';
- <<A1(3)>>
+ @@A1(3)
public $x;
- <<A1(4)>>
- public function foo(<<A1(5)>> $a, <<A1(6)>> $b) { }
+ @@A1(4)
+ public function foo(@@A1(5) $a, @@A1(6) $b) { }
}
-$object = new <<A1(7)>> class () { };
+$object = new @@A1(7) class () { };
-<<A1(8)>>
+@@A1(8)
function f1() { }
-$f2 = <<A1(9)>> function () { };
+$f2 = @@A1(9) function () { };
-$f3 = <<A1(10)>> fn () => 1;
+$f3 = @@A1(10) fn () => 1;
$ref = new \ReflectionClass(Foo::class);
diff --git a/Zend/tests/attributes/002_rfcexample.phpt b/Zend/tests/attributes/002_rfcexample.phpt
index 6d1028436d..bc608c3eed 100644
--- a/Zend/tests/attributes/002_rfcexample.phpt
+++ b/Zend/tests/attributes/002_rfcexample.phpt
@@ -6,7 +6,7 @@ Attributes: Example from Attributes RFC
namespace My\Attributes {
use Attribute;
- <<Attribute>>
+ @@Attribute
class SingleArgument {
public $argumentValue;
@@ -19,7 +19,7 @@ namespace My\Attributes {
namespace {
use My\Attributes\SingleArgument;
- <<SingleArgument("Hello World")>>
+ @@SingleArgument("Hello World")
class Foo {
}
diff --git a/Zend/tests/attributes/003_ast_nodes.phpt b/Zend/tests/attributes/003_ast_nodes.phpt
index afdab838f8..21125bde13 100644
--- a/Zend/tests/attributes/003_ast_nodes.phpt
+++ b/Zend/tests/attributes/003_ast_nodes.phpt
@@ -5,7 +5,7 @@ Attributes can deal with AST nodes.
define('V1', strtoupper(php_sapi_name()));
-<<A1([V1 => V1])>>
+@@A1([V1 => V1])
class C1
{
public const BAR = 'bar';
@@ -20,7 +20,7 @@ var_dump(count($args), $args[0][V1] === V1);
echo "\n";
-<<A1(V1, 1 + 2, C1::class)>>
+@@A1(V1, 1 + 2, C1::class)
class C2 { }
$ref = new \ReflectionClass(C2::class);
@@ -35,7 +35,7 @@ var_dump($args[2] === C1::class);
echo "\n";
-<<A1(self::FOO, C1::BAR)>>
+@@A1(self::FOO, C1::BAR)
class C3
{
private const FOO = 'foo';
@@ -52,20 +52,20 @@ var_dump($args[1] === C1::BAR);
echo "\n";
-<<ExampleWithShift(4 >> 1)>>
+@@ExampleWithShift(4 >> 1)
class C4 {}
$ref = new \ReflectionClass(C4::class);
var_dump($ref->getAttributes()[0]->getArguments());
echo "\n";
-<<Attribute>>
+@@Attribute
class C5
{
public function __construct() { }
}
-$ref = new \ReflectionFunction(<<C5(MissingClass::SOME_CONST)>> function () { });
+$ref = new \ReflectionFunction(@@C5(MissingClass::SOME_CONST) function () { });
$attr = $ref->getAttributes();
var_dump(count($attr));
diff --git a/Zend/tests/attributes/004_name_resolution.phpt b/Zend/tests/attributes/004_name_resolution.phpt
index 30537e2c12..3c0e1190ff 100644
--- a/Zend/tests/attributes/004_name_resolution.phpt
+++ b/Zend/tests/attributes/004_name_resolution.phpt
@@ -25,11 +25,11 @@ namespace Foo {
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Attributes;
- <<Entity("imported class")>>
- <<ORM\Entity("imported namespace")>>
- <<\Doctrine\ORM\Mapping\Entity("absolute from namespace")>>
- <<\Entity("import absolute from global")>>
- <<Attributes\Table()>>
+ @@Entity("imported class")
+ @@ORM\Entity("imported namespace")
+ @@\Doctrine\ORM\Mapping\Entity("absolute from namespace")
+ @@\Entity("import absolute from global")
+ @@Attributes\Table()
function foo() {
}
}
diff --git a/Zend/tests/attributes/005_objects.phpt b/Zend/tests/attributes/005_objects.phpt
index 712ec93bea..83f523182b 100644
--- a/Zend/tests/attributes/005_objects.phpt
+++ b/Zend/tests/attributes/005_objects.phpt
@@ -3,7 +3,7 @@ Attributes can be converted into objects.
--FILE--
<?php
-<<Attribute(Attribute::TARGET_FUNCTION)>>
+@@Attribute(Attribute::TARGET_FUNCTION)
class A1
{
public string $name;
@@ -16,7 +16,7 @@ class A1
}
}
-$ref = new \ReflectionFunction(<<A1('test')>> function () { });
+$ref = new \ReflectionFunction(@@A1('test') function () { });
foreach ($ref->getAttributes() as $attr) {
$obj = $attr->newInstance();
@@ -26,7 +26,7 @@ foreach ($ref->getAttributes() as $attr) {
echo "\n";
-$ref = new \ReflectionFunction(<<A1>> function () { });
+$ref = new \ReflectionFunction(@@A1 function () { });
try {
$ref->getAttributes()[0]->newInstance();
@@ -36,7 +36,7 @@ try {
echo "\n";
-$ref = new \ReflectionFunction(<<A1([])>> function () { });
+$ref = new \ReflectionFunction(@@A1([]) function () { });
try {
$ref->getAttributes()[0]->newInstance();
@@ -46,7 +46,7 @@ try {
echo "\n";
-$ref = new \ReflectionFunction(<<A2>> function () { });
+$ref = new \ReflectionFunction(@@A2 function () { });
try {
$ref->getAttributes()[0]->newInstance();
@@ -56,13 +56,13 @@ try {
echo "\n";
-<<Attribute>>
+@@Attribute
class A3
{
private function __construct() { }
}
-$ref = new \ReflectionFunction(<<A3>> function () { });
+$ref = new \ReflectionFunction(@@A3 function () { });
try {
$ref->getAttributes()[0]->newInstance();
@@ -72,10 +72,10 @@ try {
echo "\n";
-<<Attribute>>
+@@Attribute
class A4 { }
-$ref = new \ReflectionFunction(<<A4(1)>> function () { });
+$ref = new \ReflectionFunction(@@A4(1) function () { });
try {
$ref->getAttributes()[0]->newInstance();
@@ -87,7 +87,7 @@ echo "\n";
class A5 { }
-$ref = new \ReflectionFunction(<<A5>> function () { });
+$ref = new \ReflectionFunction(@@A5 function () { });
try {
$ref->getAttributes()[0]->newInstance();
diff --git a/Zend/tests/attributes/006_filter.phpt b/Zend/tests/attributes/006_filter.phpt
index 49c02d6e35..69ee146b49 100644
--- a/Zend/tests/attributes/006_filter.phpt
+++ b/Zend/tests/attributes/006_filter.phpt
@@ -3,17 +3,17 @@ Attributes can be filtered by name and base type.
--FILE--
<?php
-$ref = new \ReflectionFunction(<<A1>> <<A2>> function () { });
+$ref = new \ReflectionFunction(@@A1 @@A2 function () { });
$attr = $ref->getAttributes(A3::class);
var_dump(count($attr));
-$ref = new \ReflectionFunction(<<A1>> <<A2>> function () { });
+$ref = new \ReflectionFunction(@@A1 @@A2 function () { });
$attr = $ref->getAttributes(A2::class);
var_dump(count($attr), $attr[0]->getName());
-$ref = new \ReflectionFunction(<<A1>> <<A2>> <<A2>> function () { });
+$ref = new \ReflectionFunction(@@A1 @@A2 @@A2 function () { });
$attr = $ref->getAttributes(A2::class);
var_dump(count($attr), $attr[0]->getName(), $attr[1]->getName());
@@ -25,27 +25,27 @@ class A1 implements Base { }
class A2 implements Base { }
class A3 extends A2 { }
-$ref = new \ReflectionFunction(<<A1>> <<A2>> <<A5>> function () { });
+$ref = new \ReflectionFunction(@@A1 @@A2 @@A5 function () { });
$attr = $ref->getAttributes(\stdClass::class, \ReflectionAttribute::IS_INSTANCEOF);
var_dump(count($attr));
print_r(array_map(fn ($a) => $a->getName(), $attr));
-$ref = new \ReflectionFunction(<<A1>> <<A2>> function () { });
+$ref = new \ReflectionFunction(@@A1 @@A2 function () { });
$attr = $ref->getAttributes(A1::class, \ReflectionAttribute::IS_INSTANCEOF);
var_dump(count($attr));
print_r(array_map(fn ($a) => $a->getName(), $attr));
-$ref = new \ReflectionFunction(<<A1>> <<A2>> function () { });
+$ref = new \ReflectionFunction(@@A1 @@A2 function () { });
$attr = $ref->getAttributes(Base::class, \ReflectionAttribute::IS_INSTANCEOF);
var_dump(count($attr));
print_r(array_map(fn ($a) => $a->getName(), $attr));
-$ref = new \ReflectionFunction(<<A1>> <<A2>> <<A3>> function () { });
+$ref = new \ReflectionFunction(@@A1 @@A2 @@A3 function () { });
$attr = $ref->getAttributes(A2::class, \ReflectionAttribute::IS_INSTANCEOF);
var_dump(count($attr));
print_r(array_map(fn ($a) => $a->getName(), $attr));
-$ref = new \ReflectionFunction(<<A1>> <<A2>> <<A3>> function () { });
+$ref = new \ReflectionFunction(@@A1 @@A2 @@A3 function () { });
$attr = $ref->getAttributes(Base::class, \ReflectionAttribute::IS_INSTANCEOF);
var_dump(count($attr));
print_r(array_map(fn ($a) => $a->getName(), $attr));
diff --git a/Zend/tests/attributes/008_wrong_attribution.phpt b/Zend/tests/attributes/008_wrong_attribution.phpt
index 20a800b9a7..21ea9783d1 100644
--- a/Zend/tests/attributes/008_wrong_attribution.phpt
+++ b/Zend/tests/attributes/008_wrong_attribution.phpt
@@ -3,7 +3,7 @@ Attributes: Prevent Attribute on non classes
--FILE--
<?php
-<<Attribute>>
+@@Attribute
function foo() {}
--EXPECTF--
Fatal error: Attribute "Attribute" cannot target function (allowed targets: class) in %s
diff --git a/Zend/tests/attributes/009_doctrine_annotations_example.phpt b/Zend/tests/attributes/009_doctrine_annotations_example.phpt
index 38fc6389e0..51cb315d48 100644
--- a/Zend/tests/attributes/009_doctrine_annotations_example.phpt
+++ b/Zend/tests/attributes/009_doctrine_annotations_example.phpt
@@ -25,20 +25,22 @@ namespace {
use Doctrine\ORM\Attributes as ORM;
use Symfony\Component\Validator\Constraints as Assert;
-<<ORM\Entity>>
+@@ORM\Entity
/** @ORM\Entity */
class User
{
/** @ORM\Id @ORM\Column(type="integer"*) @ORM\GeneratedValue */
- <<ORM\Id>><<ORM\Column("integer")>><<ORM\GeneratedValue>>
+ @@ORM\Id
+ @@ORM\Column("integer")
+ @@ORM\GeneratedValue
private $id;
/**
* @ORM\Column(type="string", unique=true)
* @Assert\Email(message="The email '{{ value }}' is not a valid email.")
*/
- <<ORM\Column("string", ORM\Column::UNIQUE)>>
- <<Assert\Email(array("message" => "The email '{{ value }}' is not a valid email."))>>
+ @@ORM\Column("string", ORM\Column::UNIQUE)
+ @@Assert\Email(array("message" => "The email '{{ value }}' is not a valid email."))
private $email;
/**
@@ -50,8 +52,8 @@ class User
* maxMessage = "You cannot be taller than {{ limit }}cm to enter"
* )
*/
- <<Assert\Range(["min" => 120, "max" => 180, "minMessage" => "You must be at least {{ limit }}cm tall to enter"])>>
- <<ORM\Column(ORM\Column::T_INTEGER)>>
+ @@Assert\Range(["min" => 120, "max" => 180, "minMessage" => "You must be at least {{ limit }}cm tall to enter"])
+ @@ORM\Column(ORM\Column::T_INTEGER)
protected $height;
/**
@@ -61,10 +63,10 @@ class User
* inverseJoinColumns={@ORM\JoinColumn(name="phonenumber_id", referencedColumnName="id", unique=true)}
* )
*/
- <<ORM\ManyToMany(Phonenumber::class)>>
- <<ORM\JoinTable("users_phonenumbers")>>
- <<ORM\JoinColumn("user_id", "id")>>
- <<ORM\InverseJoinColumn("phonenumber_id", "id", ORM\JoinColumn::UNIQUE)>>
+ @@ORM\ManyToMany(Phonenumber::class)
+ @@ORM\JoinTable("users_phonenumbers")
+ @@ORM\JoinColumn("user_id", "id")
+ @@ORM\InverseJoinColumn("phonenumber_id", "id", ORM\JoinColumn::UNIQUE)
private $phonenumbers;
}
diff --git a/Zend/tests/attributes/010_unsupported_const_expression.phpt b/Zend/tests/attributes/010_unsupported_const_expression.phpt
index 50afa6c4f6..fb30e2c486 100644
--- a/Zend/tests/attributes/010_unsupported_const_expression.phpt
+++ b/Zend/tests/attributes/010_unsupported_const_expression.phpt
@@ -3,7 +3,7 @@ Attribute arguments support only const expressions.
--FILE--
<?php
-<<A1(foo())>>
+@@A1(foo())
class C1 { }
?>
diff --git a/Zend/tests/attributes/011_inheritance.phpt b/Zend/tests/attributes/011_inheritance.phpt
index 007cd5991e..25c943dea3 100644
--- a/Zend/tests/attributes/011_inheritance.phpt
+++ b/Zend/tests/attributes/011_inheritance.phpt
@@ -3,10 +3,10 @@ Attributes comply with inheritance rules.
--FILE--
<?php
-<<A2>>
+@@A2
class C1
{
- <<A1>>
+ @@A1
public function foo() { }
}
@@ -17,7 +17,7 @@ class C2 extends C1
class C3 extends C1
{
- <<A1>>
+ @@A1
public function bar() { }
}
@@ -37,7 +37,7 @@ echo "\n";
trait T1
{
- <<A2>>
+ @@A2
public $a;
}
diff --git a/Zend/tests/attributes/012_ast_export.phpt b/Zend/tests/attributes/012_ast_export.phpt
index fe3539c8a9..c811327d0c 100644
--- a/Zend/tests/attributes/012_ast_export.phpt
+++ b/Zend/tests/attributes/012_ast_export.phpt
@@ -3,51 +3,51 @@ Attributes AST can be exported.
--FILE--
<?php
-assert(0 && ($a = <<A1>><<A2>> function ($a, <<A3(1)>> $b) { }));
+assert(0 && ($a = @@A1 @@A2 function ($a, @@A3(1) $b) { }));
-assert(0 && ($a = <<A1(1, 2, 1 + 2)>> fn () => 1));
+assert(0 && ($a = @@A1(1, 2, 1 + 2) fn () => 1));
-assert(0 && ($a = new <<A1>> class() {
- <<A1>><<A2>> const FOO = 'foo';
- <<A2>> public $x;
- <<A3>> function a() { }
+assert(0 && ($a = new @@A1 class() {
+ @@A1@@A2 const FOO = 'foo';
+ @@A2 public $x;
+ @@A3 function a() { }
}));
assert(0 && ($a = function () {
- <<A1>> class Test1 { }
- <<A2>> interface Test2 { }
- <<A3>> trait Test3 { }
+ @@A1 class Test1 { }
+ @@A2 interface Test2 { }
+ @@A3 trait Test3 { }
}));
?>
--EXPECTF--
-Warning: assert(): assert(0 && ($a = <<A1>> <<A2>> function ($a, <<A3(1)>> $b) {
+Warning: assert(): assert(0 && ($a = @@A1 @@A2 function ($a, @@A3(1) $b) {
})) failed in %s on line %d
-Warning: assert(): assert(0 && ($a = <<A1(1, 2, 1 + 2)>> fn() => 1)) failed in %s on line %d
+Warning: assert(): assert(0 && ($a = @@A1(1, 2, 1 + 2) fn() => 1)) failed in %s on line %d
-Warning: assert(): assert(0 && ($a = new <<A1>> class {
- <<A1>>
- <<A2>>
+Warning: assert(): assert(0 && ($a = new @@A1 class {
+ @@A1
+ @@A2
public const FOO = 'foo';
- <<A2>>
+ @@A2
public $x;
- <<A3>>
+ @@A3
public function a() {
}
})) failed in %s on line %d
Warning: assert(): assert(0 && ($a = function () {
- <<A1>>
+ @@A1
class Test1 {
}
- <<A2>>
+ @@A2
interface Test2 {
}
- <<A3>>
+ @@A3
trait Test3 {
}
diff --git a/Zend/tests/attributes/013_class_scope.phpt b/Zend/tests/attributes/013_class_scope.phpt
index 94764c2d69..be6c7a60da 100644
--- a/Zend/tests/attributes/013_class_scope.phpt
+++ b/Zend/tests/attributes/013_class_scope.phpt
@@ -3,17 +3,17 @@ Attributes make use of class scope.
--FILE--
<?php
-<<A1(self::class, self::FOO)>>
+@@A1(self::class, self::FOO)
class C1
{
- <<A1(self::class, self::FOO)>>
+ @@A1(self::class, self::FOO)
private const FOO = 'foo';
- <<A1(self::class, self::FOO)>>
+ @@A1(self::class, self::FOO)
public $a;
- <<A1(self::class, self::FOO)>>
- public function bar(<<A1(self::class, self::FOO)>> $p) { }
+ @@A1(self::class, self::FOO)
+ public function bar(@@A1(self::class, self::FOO) $p) { }
}
$ref = new \ReflectionClass(C1::class);
@@ -27,7 +27,7 @@ echo "\n";
trait T1
{
- <<A1(self::class, self::FOO)>>
+ @@A1(self::class, self::FOO)
public function foo() { }
}
@@ -58,10 +58,10 @@ class C3
public static function foo()
{
- return new <<A1(self::class, self::FOO)>> class() {
+ return new @@A1(self::class, self::FOO) class() {
private const FOO = 'bar';
- <<A1(self::class, self::FOO)>>
+ @@A1(self::class, self::FOO)
public function bar() { }
};
}
diff --git a/Zend/tests/attributes/014_class_const_group.phpt b/Zend/tests/attributes/014_class_const_group.phpt
index 18ff93683a..5da5c52a95 100644
--- a/Zend/tests/attributes/014_class_const_group.phpt
+++ b/Zend/tests/attributes/014_class_const_group.phpt
@@ -5,7 +5,7 @@ Attributes cannot be applied to groups of class constants.
class C1
{
- <<A1>>
+ @@A1
public const A = 1, B = 2;
}
diff --git a/Zend/tests/attributes/015_property_group.phpt b/Zend/tests/attributes/015_property_group.phpt
index 493e4ae260..52e3b0a92a 100644
--- a/Zend/tests/attributes/015_property_group.phpt
+++ b/Zend/tests/attributes/015_property_group.phpt
@@ -5,7 +5,7 @@ Attributes cannot be applied to groups of properties.
class C1
{
- <<A1>>
+ @@A1
public $x, $y;
}
diff --git a/Zend/tests/attributes/016_target_resolution_compiler_attributes.phpt b/Zend/tests/attributes/016_custom_attribute_validation.phpt
index c96b49cbd7..6095d390d9 100644
--- a/Zend/tests/attributes/016_target_resolution_compiler_attributes.phpt
+++ b/Zend/tests/attributes/016_custom_attribute_validation.phpt
@@ -1,5 +1,5 @@
--TEST--
-Attributes: Compiler Attributes can check for target declarations
+Attribute validation callback of internal attributes.
--SKIPIF--
<?php
if (!extension_loaded('zend-test')) {
@@ -8,8 +8,8 @@ if (!extension_loaded('zend-test')) {
--FILE--
<?php
-<<ZendTestAttribute>>
+@@ZendTestAttribute
function foo() {
}
--EXPECTF--
-Fatal error: Only classes can be marked with <<ZendTestAttribute>> in %s
+Fatal error: Only classes can be marked with @@ZendTestAttribute in %s
diff --git a/Zend/tests/attributes/017_closure_scope.phpt b/Zend/tests/attributes/017_closure_scope.phpt
index f57b4b73b3..936a8ec338 100644
--- a/Zend/tests/attributes/017_closure_scope.phpt
+++ b/Zend/tests/attributes/017_closure_scope.phpt
@@ -14,7 +14,7 @@ class C1
public static function foo()
{
- return <<A1(self::class, self::FOO)>> function (<<A1(self::class, self::FOO)>> $p) { };
+ return @@A1(self::class, self::FOO) function (@@A1(self::class, self::FOO) $p) { };
}
}
diff --git a/Zend/tests/attributes/018_fatal_error_in_argument.phpt b/Zend/tests/attributes/018_fatal_error_in_argument.phpt
index a950c8658a..db2719d85a 100644
--- a/Zend/tests/attributes/018_fatal_error_in_argument.phpt
+++ b/Zend/tests/attributes/018_fatal_error_in_argument.phpt
@@ -3,7 +3,7 @@ Don't free uninitialized memory if a fatal error occurs in an attribute argument
--FILE--
<?php
-<<Attr(a->b::c)>>
+@@Attr(a->b::c)
function test() {}
?>
diff --git a/Zend/tests/attributes/019_variable_attribute_name.phpt b/Zend/tests/attributes/019_variable_attribute_name.phpt
index a259c06f3f..9b8a3c2211 100644
--- a/Zend/tests/attributes/019_variable_attribute_name.phpt
+++ b/Zend/tests/attributes/019_variable_attribute_name.phpt
@@ -3,7 +3,7 @@ Attribute name cannot be a variable
--FILE--
<?php
-<<$x>>
+@@$x
class A {}
?>
diff --git a/Zend/tests/attributes/020_userland_attribute_validation.phpt b/Zend/tests/attributes/020_userland_attribute_validation.phpt
index 48c5e2651b..1025b5008e 100644
--- a/Zend/tests/attributes/020_userland_attribute_validation.phpt
+++ b/Zend/tests/attributes/020_userland_attribute_validation.phpt
@@ -3,17 +3,17 @@ Attributes expose and verify target and repeatable data.
--FILE--
<?php
-<<Attribute(Attribute::TARGET_FUNCTION | Attribute::TARGET_METHOD)>>
+@@Attribute(Attribute::TARGET_FUNCTION | Attribute::TARGET_METHOD)
class A1 { }
-$ref = new \ReflectionFunction(<<A1>> function () { });
+$ref = new \ReflectionFunction(@@A1 function () { });
$attr = $ref->getAttributes()[0];
var_dump($attr->getName(), $attr->getTarget() == Attribute::TARGET_FUNCTION, $attr->isRepeated());
var_dump(get_class($attr->newInstance()));
echo "\n";
-$ref = new \ReflectionObject(new <<A1>> class() { });
+$ref = new \ReflectionObject(new @@A1 class() { });
$attr = $ref->getAttributes()[0];
var_dump($attr->getName(), $attr->getTarget() == Attribute::TARGET_CLASS, $attr->isRepeated());
@@ -25,7 +25,7 @@ try {
echo "\n";
-$ref = new \ReflectionFunction(<<A1>> <<A1>> function () { });
+$ref = new \ReflectionFunction(@@A1 @@A1 function () { });
$attr = $ref->getAttributes()[0];
var_dump($attr->getName(), $attr->getTarget() == Attribute::TARGET_FUNCTION, $attr->isRepeated());
@@ -37,10 +37,10 @@ try {
echo "\n";
-<<Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)>>
+@@Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)
class A2 { }
-$ref = new \ReflectionObject(new <<A2>> <<A2>> class() { });
+$ref = new \ReflectionObject(new @@A2 @@A2 class() { });
$attr = $ref->getAttributes()[0];
var_dump($attr->getName(), $attr->getTarget() == Attribute::TARGET_CLASS, $attr->isRepeated());
var_dump(get_class($attr->newInstance()));
diff --git a/Zend/tests/attributes/021_attribute_flags_type_is_validated.phpt b/Zend/tests/attributes/021_attribute_flags_type_is_validated.phpt
index 06ed4d08fd..0f0b915ebe 100644
--- a/Zend/tests/attributes/021_attribute_flags_type_is_validated.phpt
+++ b/Zend/tests/attributes/021_attribute_flags_type_is_validated.phpt
@@ -3,7 +3,7 @@ Attribute flags type is validated.
--FILE--
<?php
-<<Attribute("foo")>>
+@@Attribute("foo")
class A1 { }
?>
diff --git a/Zend/tests/attributes/022_attribute_flags_value_is_validated.phpt b/Zend/tests/attributes/022_attribute_flags_value_is_validated.phpt
index 1deb81e4d5..28ad550385 100644
--- a/Zend/tests/attributes/022_attribute_flags_value_is_validated.phpt
+++ b/Zend/tests/attributes/022_attribute_flags_value_is_validated.phpt
@@ -3,7 +3,7 @@ Attribute flags value is validated.
--FILE--
<?php
-<<Attribute(-1)>>
+@@Attribute(-1)
class A1 { }
?>
diff --git a/Zend/tests/attributes/023_ast_node_in_validation.phpt b/Zend/tests/attributes/023_ast_node_in_validation.phpt
index 57a7287cae..ce44527f62 100644
--- a/Zend/tests/attributes/023_ast_node_in_validation.phpt
+++ b/Zend/tests/attributes/023_ast_node_in_validation.phpt
@@ -3,7 +3,7 @@ Attribute flags value is validated.
--FILE--
<?php
-<<Attribute(Foo::BAR)>>
+@@Attribute(Foo::BAR)
class A1 { }
?>
diff --git a/Zend/tests/attributes/024_internal_target_validation.phpt b/Zend/tests/attributes/024_internal_target_validation.phpt
index 746ceb3c69..49a5ae68c8 100644
--- a/Zend/tests/attributes/024_internal_target_validation.phpt
+++ b/Zend/tests/attributes/024_internal_target_validation.phpt
@@ -3,7 +3,7 @@ Internal attribute targets are validated.
--FILE--
<?php
-<<Attribute>>
+@@Attribute
function a1() { }
?>
diff --git a/Zend/tests/attributes/025_internal_repeatable_validation.phpt b/Zend/tests/attributes/025_internal_repeatable_validation.phpt
index 631f0b5054..b3c83e810f 100644
--- a/Zend/tests/attributes/025_internal_repeatable_validation.phpt
+++ b/Zend/tests/attributes/025_internal_repeatable_validation.phpt
@@ -3,8 +3,8 @@ Internal attribute targets are validated.
--FILE--
<?php
-<<Attribute>>
-<<Attribute>>
+@@Attribute
+@@Attribute
class A1 { }
?>
diff --git a/Zend/tests/attributes/026_unpack_in_args.phpt b/Zend/tests/attributes/026_unpack_in_args.phpt
index 04a038d3e5..37f8fb5679 100644
--- a/Zend/tests/attributes/026_unpack_in_args.phpt
+++ b/Zend/tests/attributes/026_unpack_in_args.phpt
@@ -3,7 +3,7 @@ Cannot use unpacking in attribute argument list
--FILE--
<?php
-<<MyAttribute(...[1, 2, 3])>>
+@@MyAttribute(...[1, 2, 3])
class Foo { }
?>
diff --git a/Zend/tests/attributes/027_trailing_comma_args.phpt b/Zend/tests/attributes/027_trailing_comma_args.phpt
index c8f6adf0e8..5ac47e08a8 100644
--- a/Zend/tests/attributes/027_trailing_comma_args.phpt
+++ b/Zend/tests/attributes/027_trailing_comma_args.phpt
@@ -3,12 +3,12 @@ Trailing comma in attribute argument list
--FILE--
<?php
-<<MyAttribute(
+@@MyAttribute(
"there",
"are",
"many",
"arguments",
-)>>
+)
class Foo { }
$ref = new \ReflectionClass(Foo::class);
diff --git a/Zend/tests/ctor_promotion_attributes.phpt b/Zend/tests/ctor_promotion_attributes.phpt
index d85d8d3181..08c7117daf 100644
--- a/Zend/tests/ctor_promotion_attributes.phpt
+++ b/Zend/tests/ctor_promotion_attributes.phpt
@@ -5,7 +5,7 @@ Attributes on promoted properties are assigned to both the property and paramete
class Test {
public function __construct(
- <<NonNegative>>
+ @@NonNegative
public int $num,
) {}
}
diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c
index f6e0b476e9..1affe08c37 100644
--- a/Zend/zend_ast.c
+++ b/Zend/zend_ast.c
@@ -1356,7 +1356,7 @@ static ZEND_COLD void zend_ast_export_attributes(smart_str *str, zend_ast *ast,
for (i = 0; i < list->children; i++) {
zend_ast *attr = list->child[i];
- smart_str_appends(str, "<<");
+ smart_str_appends(str, "@@");
zend_ast_export_ns_name(str, attr->child[0], 0, indent);
if (attr->child[1]) {
@@ -1373,8 +1373,6 @@ static ZEND_COLD void zend_ast_export_attributes(smart_str *str, zend_ast *ast,
smart_str_appendc(str, ')');
}
- smart_str_appends(str, ">>");
-
if (newlines) {
smart_str_appendc(str, '\n');
zend_ast_export_indent(str, indent);
diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y
index f8b4b6d862..55f2c0f49c 100644
--- a/Zend/zend_language_parser.y
+++ b/Zend/zend_language_parser.y
@@ -178,6 +178,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%token <ident> T_NS_C "'__NAMESPACE__'"
%token END 0 "end of file"
+%token T_ATTRIBUTE "'@@'"
%token T_PLUS_EQUAL "'+='"
%token T_MINUS_EQUAL "'-='"
%token T_MUL_EQUAL "'*='"
@@ -345,7 +346,7 @@ attribute_decl:
;
attribute:
- T_SL attribute_decl T_SR { $$ = $2; }
+ T_ATTRIBUTE attribute_decl { $$ = $2; }
;
attributes:
diff --git a/Zend/zend_language_scanner.l b/Zend/zend_language_scanner.l
index 3b6cc1dd67..7f42159b46 100644
--- a/Zend/zend_language_scanner.l
+++ b/Zend/zend_language_scanner.l
@@ -1422,6 +1422,10 @@ NEWLINE ("\r"|"\n"|"\r\n")
RETURN_TOKEN_WITH_IDENT(T_RETURN);
}
+<ST_IN_SCRIPTING>"@@" {
+ RETURN_TOKEN(T_ATTRIBUTE);
+}
+
<ST_IN_SCRIPTING>"yield"{WHITESPACE}"from"[^a-zA-Z0-9_\x80-\xff] {
yyless(yyleng - 1);
HANDLE_NEWLINES(yytext, yyleng);
diff --git a/ext/tokenizer/tests/attributes.phpt b/ext/tokenizer/tests/attributes.phpt
new file mode 100644
index 0000000000..8795a4c5dc
--- /dev/null
+++ b/ext/tokenizer/tests/attributes.phpt
@@ -0,0 +1,20 @@
+--TEST--
+Attributes are exposed as tokens.
+--FILE--
+<?php
+
+$tokens = token_get_all('<?php @@A1(1, 2) class C1 { }');
+
+$attr = $tokens[1];
+var_dump(token_name(T_ATTRIBUTE));
+var_dump($attr[0] === T_ATTRIBUTE);
+var_dump($attr[1]);
+
+$class = $tokens[2];
+var_dump($class[1]);
+?>
+--EXPECT--
+string(11) "T_ATTRIBUTE"
+bool(true)
+string(2) "@@"
+string(2) "A1"
diff --git a/ext/tokenizer/tokenizer_data.c b/ext/tokenizer/tokenizer_data.c
index 049433f4b7..5699c57566 100644
--- a/ext/tokenizer/tokenizer_data.c
+++ b/ext/tokenizer/tokenizer_data.c
@@ -148,6 +148,7 @@ void tokenizer_register_constants(INIT_FUNC_ARGS) {
REGISTER_LONG_CONSTANT("T_METHOD_C", T_METHOD_C, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_FUNC_C", T_FUNC_C, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_NS_C", T_NS_C, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("T_ATTRIBUTE", T_ATTRIBUTE, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_INC", T_INC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_DEC", T_DEC, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("T_OBJECT_OPERATOR", T_OBJECT_OPERATOR, CONST_CS | CONST_PERSISTENT);
@@ -296,6 +297,7 @@ char *get_token_type_name(int token_type)
case T_METHOD_C: return "T_METHOD_C";
case T_FUNC_C: return "T_FUNC_C";
case T_NS_C: return "T_NS_C";
+ case T_ATTRIBUTE: return "T_ATTRIBUTE";
case T_INC: return "T_INC";
case T_DEC: return "T_DEC";
case T_OBJECT_OPERATOR: return "T_OBJECT_OPERATOR";
diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c
index 413a6a302f..900c70f59d 100644
--- a/ext/zend_test/test.c
+++ b/ext/zend_test/test.c
@@ -257,7 +257,7 @@ static zend_function *zend_test_class_static_method_get(zend_class_entry *ce, ze
void zend_attribute_validate_zendtestattribute(zend_attribute *attr, uint32_t target, zend_class_entry *scope)
{
if (target != ZEND_ATTRIBUTE_TARGET_CLASS) {
- zend_error(E_COMPILE_ERROR, "Only classes can be marked with <<ZendTestAttribute>>");
+ zend_error(E_COMPILE_ERROR, "Only classes can be marked with @@ZendTestAttribute");
}
}