]> granicus.if.org Git - php/commitdiff
Change Attribute Syntax from @@ to #[]
authorBenjamin Eberlei <kontakt@beberlei.de>
Sat, 15 Aug 2020 08:39:00 +0000 (10:39 +0200)
committerBenjamin Eberlei <kontakt@beberlei.de>
Wed, 2 Sep 2020 18:26:50 +0000 (20:26 +0200)
42 files changed:
Zend/tests/attributes/001_placement.phpt
Zend/tests/attributes/002_rfcexample.phpt
Zend/tests/attributes/003_ast_nodes.phpt
Zend/tests/attributes/004_name_resolution.phpt
Zend/tests/attributes/005_objects.phpt
Zend/tests/attributes/006_filter.phpt
Zend/tests/attributes/008_wrong_attribution.phpt
Zend/tests/attributes/009_doctrine_annotations_example.phpt
Zend/tests/attributes/010_unsupported_const_expression.phpt
Zend/tests/attributes/011_inheritance.phpt
Zend/tests/attributes/012_ast_export.phpt
Zend/tests/attributes/013_class_scope.phpt
Zend/tests/attributes/014_class_const_group.phpt
Zend/tests/attributes/015_property_group.phpt
Zend/tests/attributes/016_custom_attribute_validation.phpt
Zend/tests/attributes/017_closure_scope.phpt
Zend/tests/attributes/018_fatal_error_in_argument.phpt
Zend/tests/attributes/019_variable_attribute_name.phpt
Zend/tests/attributes/020_userland_attribute_validation.phpt
Zend/tests/attributes/021_attribute_flags_type_is_validated.phpt
Zend/tests/attributes/022_attribute_flags_value_is_validated.phpt
Zend/tests/attributes/023_ast_node_in_validation.phpt
Zend/tests/attributes/024_internal_target_validation.phpt
Zend/tests/attributes/025_internal_repeatable_validation.phpt
Zend/tests/attributes/026_unpack_in_args.phpt
Zend/tests/attributes/027_trailing_comma_args.phpt
Zend/tests/attributes/028_grouped.phpt [new file with mode: 0644]
Zend/tests/bug79897.phpt
Zend/tests/ctor_promotion_attributes.phpt
Zend/tests/named_params/attributes.phpt
Zend/tests/named_params/attributes_duplicate_named_param.phpt
Zend/tests/named_params/attributes_named_flags.phpt
Zend/tests/named_params/attributes_named_flags_incorrect.phpt
Zend/tests/named_params/attributes_positional_after_named.phpt
Zend/tests/varSyntax/globalNonSimpleVariableError.phpt
Zend/zend_ast.c
Zend/zend_ast.h
Zend/zend_compile.c
Zend/zend_language_parser.y
Zend/zend_language_scanner.l
ext/tokenizer/tests/attributes.phpt
ext/zend_test/test.c

index 9bf9c4d2f761f0d450d77a7b0502504032b8eed9..7de2210460e52b1d46c63cdcfd281e4d48f78cec 100644 (file)
@@ -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);
 
@@ -43,11 +43,11 @@ $sources = [
 foreach ($sources as $r) {
        $attr = $r->getAttributes();
        var_dump(get_class($r), count($attr));
-       
+
     foreach ($attr as $a) {
         var_dump($a->getName(), $a->getArguments());
     }
-    
+
     echo "\n";
 }
 
index bc608c3eed0d98f6515fe6be4e839526a4485bdf..8a0abf7878953d07bf33be1016e32b4efae738f8 100644 (file)
@@ -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 {
     }
 
index 21125bde13a04f8fe488493ff56d8612b0e8984c..0177804dcc85a7b68b01cb38f65fe17435169bb8 100644 (file)
@@ -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));
 
index 3c0e1190ffec962be0217e54c6f0c55f2070b90f..6f266908b5c08cd4312dd5a58c492273445d25db 100644 (file)
@@ -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() {
     }
 }
index 83f523182b1b5e802b50a5d8152e35fa50bd55b1..206d89fa10ab77c015a9856f9fbe713d9c56bafe 100644 (file)
@@ -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();
index 69ee146b49f0340c7005f95c64f230346569cf86..8da1ec9bde1e158a645d9d681e2cd99e0df1be46 100644 (file)
@@ -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));
index 5a3ec54a6fd1cb7ae462d9afdd1041235b111416..af61bcf10503737f3b637873fd48c9075e3fb46a 100644 (file)
@@ -3,7 +3,7 @@ Attributes: Prevent Attribute on non classes
 --FILE--
 <?php
 
-@@Attribute
+#[Attribute]
 function foo() {}
 ?>
 --EXPECTF--
index 51cb315d487c91bde695282317cd664e78ef23a2..1f2aa647f0a0649590d18a4c94059410cafee399 100644 (file)
@@ -25,22 +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;
 
     /**
@@ -52,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;
 
     /**
@@ -63,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;
 }
 
index fb30e2c486bd5c5b425de6d69409bb69406b73b6..498898379da837ada2844fab593b62b6b30f6233 100644 (file)
@@ -3,7 +3,7 @@ Attribute arguments support only const expressions.
 --FILE--
 <?php
 
-@@A1(foo())
+#[A1(foo())]
 class C1 { }
 
 ?>
index 25c943dea30e93f6f932e9b648d4468f5557a5d3..36ee3fa47addff70f7157dc567ded91251a80bca 100644 (file)
@@ -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;
 }
 
@@ -49,7 +49,7 @@ class C4
 class C5
 {
        use T1;
-       
+
        public $a;
 }
 
index 9412acc6059e72fb91ef3330ad256052bf20f97b..a9131f92d0c0b30ad138f6c451acdb11461210f0 100644 (file)
@@ -7,51 +7,51 @@ assert.warning=1
 --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 {
     }
 
index be6c7a60da4f489c01f80d44ba2dea158a1b6955..61dd9f594c7088a061f06dd8747eaf4b227ad153 100644 (file)
@@ -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() { }
                };
        }
index 5da5c52a957794c070210a3ccd0dcfb054afe3a2..a53ed09c0abd2a289af9e6f5330403dd74dc7d03 100644 (file)
@@ -5,7 +5,7 @@ Attributes cannot be applied to groups of class constants.
 
 class C1
 {
-       @@A1
+       #[A1]
        public const A = 1, B = 2;
 }
 
index 52e3b0a92a8d7e682fd061640dd4cd0292299e92..484d4154cf3a6c236e76c8a7c1e7f102f287a54d 100644 (file)
@@ -5,7 +5,7 @@ Attributes cannot be applied to groups of properties.
 
 class C1
 {
-       @@A1
+       #[A1]
        public $x, $y;
 }
 
index 9971085274d6dce3e6338401decb84964a166105..0f3167f98695cfd93d216c7b870945eff47dfbd3 100644 (file)
@@ -8,9 +8,9 @@ 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
index 936a8ec338a74b298c77319d109db463118dec67..af7de8e2e828bc338087f71f001d706029297dc0 100644 (file)
@@ -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) { };
        }
 }
 
index db2719d85a92cf6a4ba21a1859e62a9159ff6060..fd031f5c2c2eeb79de9d19faa6aec5d144872b91 100644 (file)
@@ -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() {}
 
 ?>
index 9b8a3c22116dfd507a96cf02d1ccc785b5d8a31b..fe96459bb30f470d5be98575ca2628465bd62794 100644 (file)
@@ -3,7 +3,7 @@ Attribute name cannot be a variable
 --FILE--
 <?php
 
-@@$x
+#[$x]
 class A {}
 
 ?>
index 1025b5008e20215c6300c39f325d9434eb682fbe..14a10c39b288b78b24a191eca79683732263fb06 100644 (file)
@@ -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()));
index 0f0b915ebe762a934b3867105a6d36ad12e5e9bb..7c1bfd2da72bf9fa0285ff35d6350dcd2f703d19 100644 (file)
@@ -3,7 +3,7 @@ Attribute flags type is validated.
 --FILE--
 <?php
 
-@@Attribute("foo")
+#[Attribute("foo")]
 class A1 { }
 
 ?>
index 28ad550385d4fd2afeb2a151ccf6341a63041520..72433a9f13930fb22c24df72cd2369740e31eccd 100644 (file)
@@ -3,7 +3,7 @@ Attribute flags value is validated.
 --FILE--
 <?php
 
-@@Attribute(-1)
+#[Attribute(-1)]
 class A1 { }
 
 ?>
index ce44527f62fd54060e9f5b14d1a5b3ee3ca0c2e5..332d83fe86f61cf8717384a8f58e3c4d17fe40e9 100644 (file)
@@ -3,7 +3,7 @@ Attribute flags value is validated.
 --FILE--
 <?php
 
-@@Attribute(Foo::BAR)
+#[Attribute(Foo::BAR)]
 class A1 { }
 
 ?>
index 49a5ae68c8b14ef9b322ba2f55a22eecadb59697..e941cf6132ae5590a2cf8ecff6f1f61b46479dad 100644 (file)
@@ -3,7 +3,7 @@ Internal attribute targets are validated.
 --FILE--
 <?php
 
-@@Attribute
+#[Attribute]
 function a1() { }
 
 ?>
index b3c83e810f2af42aff4d6d4204e7b2762a3babcf..671a15fba31f0a6bdc3e8c58f06fe2d74797c48a 100644 (file)
@@ -3,8 +3,8 @@ Internal attribute targets are validated.
 --FILE--
 <?php
 
-@@Attribute
-@@Attribute
+#[Attribute]
+#[Attribute]
 class A1 { }
 
 ?>
index 37f8fb5679aba32958e2dea1f10d9f0ce9bd25cd..d7528b5114cb1015b81e85c1f3729f53859ee43e 100644 (file)
@@ -3,7 +3,7 @@ Cannot use unpacking in attribute argument list
 --FILE--
 <?php
 
-@@MyAttribute(...[1, 2, 3])
+#[MyAttribute(...[1, 2, 3])]
 class Foo { }
 
 ?>
index 5ac47e08a8586ff313d3d1933f3d413e6612bedc..226025f359c2cd0063ccac4457096f8af99af3e6 100644 (file)
@@ -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/attributes/028_grouped.phpt b/Zend/tests/attributes/028_grouped.phpt
new file mode 100644 (file)
index 0000000..9796531
--- /dev/null
@@ -0,0 +1,55 @@
+--TEST--
+Attributes can be grouped
+--FILE--
+<?php
+
+#[A1(1), A1(2), A2(3)]
+class Foo
+{
+}
+
+#[
+    A1(1),
+    A1(2),
+    A2(3)
+]
+function foo() {}
+
+#[A1, A1, A2]
+function bar() {}
+
+$sources = [
+    new \ReflectionClass(Foo::class),
+    new \ReflectionFunction('foo'),
+    new \ReflectionFunction('bar'),
+];
+
+foreach ($sources as $ref) {
+    $attr = $ref->getAttributes();
+    var_dump(get_class($ref), count($attr));
+
+    foreach ($attr as $a) {
+        printf("%s(%s)\n", $a->getName(), implode(", ", $a->getArguments()));
+    }
+
+    echo "\n";
+}
+?>
+--EXPECT--
+string(15) "ReflectionClass"
+int(3)
+A1(1)
+A1(2)
+A2(3)
+
+string(18) "ReflectionFunction"
+int(3)
+A1(1)
+A1(2)
+A2(3)
+
+string(18) "ReflectionFunction"
+int(3)
+A1()
+A1()
+A2()
index ed79318c766e4007939d584149dff322ef0975cf..19869538a465222132e761cbbe707d438f7fdcdf 100644 (file)
@@ -3,7 +3,7 @@ bug79897: Promoted constructor params with attribs cause crash
 --FILE--
 <?php
 
-@@Attribute
+#[Attribute]
 class B {
     public function __construct($value)
     {
@@ -12,7 +12,7 @@ class B {
 
 class A {
     public function __construct(
-        @@B(12) public $b
+        #[B(12)] public $b
     )
     {
     }
index 08c7117dafb4d0cbf00451f54eb198931fe7f686..1b02eef9df6edc763e69d363faf736e6968474af 100644 (file)
@@ -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,
     ) {}
 }
index 931dbfbe9fae3c4fe7f3d86bc270f3bf03d0c309..94d2fad19f4d9365b75bf6532379784a2c8085e8 100644 (file)
@@ -3,7 +3,7 @@ Named params in attributes
 --FILE--
 <?php
 
-@@Attribute
+#[Attribute]
 class MyAttribute {
     public function __construct(
         public $a = 'a',
@@ -12,10 +12,10 @@ class MyAttribute {
     ) {}
 }
 
-@@MyAttribute('A', c: 'C')
+#[MyAttribute('A', c: 'C')]
 class Test1 {}
 
-@@MyAttribute('A', a: 'C')
+#[MyAttribute('A', a: 'C')]
 class Test2 {}
 
 $attr = (new ReflectionClass(Test1::class))->getAttributes()[0];
index bcd9f9b71792b33a9e8a8a9b624588d2bf82d5c1..3c4203a6c7fd1f8d9df625eca5ddb02511c1b2be 100644 (file)
@@ -3,7 +3,7 @@ Named params in attributes: Duplicate named parameter error
 --FILE--
 <?php
 
-@@MyAttribute(a: 'A', a: 'A')
+#[MyAttribute(a: 'A', a: 'A')]
 class Test {}
 
 ?>
index f553aa5308900cea18f101b6bee26c426761bd47..fcb08755e3767e98b537f75fd4aa18945df9da3d 100644 (file)
@@ -3,11 +3,11 @@ Named flags parameter for Attribute attribute
 --FILE--
 <?php
 
-@@Attribute(flags: Attribute::TARGET_CLASS)
+#[Attribute(flags: Attribute::TARGET_CLASS)]
 class MyAttribute {
 }
 
-@@MyAttribute
+#[MyAttribute]
 function test() {}
 
 (new ReflectionFunction('test'))->getAttributes()[0]->newInstance();
index 6f5b226157b1b5406cb16a2a737be335422bf781..2ad231602f5f14f557145dda6bc3ce67ad1316cb 100644 (file)
@@ -4,11 +4,11 @@ Named flags parameter for Attribute attribute (incorrect parameter name)
 <?php
 
 // TODO: This should error at compile-time.
-@@Attribute(not_flags: Attribute::TARGET_CLASS)
+#[Attribute(not_flags: Attribute::TARGET_CLASS)]
 class MyAttribute {
 }
 
-@@MyAttribute
+#[MyAttribute]
 function test() {}
 
 (new ReflectionFunction('test'))->getAttributes()[0]->newInstance();
index 61cee4dc1f2c5f7b229bd6074fece891f6e3e8da..2301635b689647403030a4145f300c11052bcd77 100644 (file)
@@ -3,10 +3,10 @@ Named params in attributes: Positional after named error
 --FILE--
 <?php
 
-@@Attribute
+#[Attribute]
 class MyAttribute { }
 
-@@MyAttribute(a: 'A', 'B')
+#[MyAttribute(a: 'A', 'B')]
 class Test {}
 
 ?>
index 6162cbc993ff8d98c86ff9563ef43ecfd4f9cee3..f41840cbb364950a45eed9cf02361e6c016c58a8 100644 (file)
@@ -7,4 +7,4 @@ global $$foo->bar;
 
 ?>
 --EXPECTF--
-Parse error: syntax error, unexpected token "->", expecting ";" or "," in %s on line %d
+Parse error: syntax error, unexpected token "->", expecting "," or ";" in %s on line %d
index f746b408a1f84585e44f8822caf468a9e13504fe..7932bf597471f16882b27f5dc11de64aeee98f5d 100644 (file)
@@ -1349,19 +1349,20 @@ static ZEND_COLD void zend_ast_export_class_no_header(smart_str *str, zend_ast_d
        smart_str_appends(str, "}");
 }
 
-static ZEND_COLD void zend_ast_export_attributes(smart_str *str, zend_ast *ast, int indent, zend_bool newlines) {
+static ZEND_COLD void zend_ast_export_attribute_group(smart_str *str, zend_ast *ast, int indent) {
        zend_ast_list *list = zend_ast_get_list(ast);
-       uint32_t i;
+       uint32_t i, j;
 
        for (i = 0; i < list->children; i++) {
                zend_ast *attr = list->child[i];
 
-               smart_str_appends(str, "@@");
+               if (i) {
+                       smart_str_appends(str, ", ");
+               }
                zend_ast_export_ns_name(str, attr->child[0], 0, indent);
 
                if (attr->child[1]) {
                        zend_ast_list *args = zend_ast_get_list(attr->child[1]);
-                       uint32_t j;
 
                        smart_str_appendc(str, '(');
                        for (j = 0; j < args->children; j++) {
@@ -1372,6 +1373,17 @@ static ZEND_COLD void zend_ast_export_attributes(smart_str *str, zend_ast *ast,
                        }
                        smart_str_appendc(str, ')');
                }
+       }
+}
+
+static ZEND_COLD void zend_ast_export_attributes(smart_str *str, zend_ast *ast, int indent, zend_bool newlines) {
+       zend_ast_list *list = zend_ast_get_list(ast);
+       uint32_t i;
+
+       for (i = 0; i < list->children; i++) {
+               smart_str_appends(str, "#[");
+               zend_ast_export_attribute_group(str, list->child[i], indent);
+               smart_str_appends(str, "]");
 
                if (newlines) {
                        smart_str_appendc(str, '\n');
index 2d72759c392a42f9e1acfbed92a88d39e0cf38f8..eb02e9bea0c38362a0b32d2d258b0a33f8f20e7d 100644 (file)
@@ -63,6 +63,7 @@ enum _zend_ast_kind {
        ZEND_AST_USE,
        ZEND_AST_TYPE_UNION,
        ZEND_AST_ATTRIBUTE_LIST,
+       ZEND_AST_ATTRIBUTE_GROUP,
        ZEND_AST_MATCH_ARM_LIST,
 
        /* 0 child nodes */
index 8d76148fc2a339d37f239da746c993ff62b48f95..c403a1f54e3a14c9a46bb91f901fea0087f52cd4 100644 (file)
@@ -6214,51 +6214,57 @@ static void zend_compile_attributes(HashTable **attributes, zend_ast *ast, uint3
        zend_internal_attribute *config;
 
        zend_ast_list *list = zend_ast_get_list(ast);
-       uint32_t i, j;
+       uint32_t g, i, j;
 
        ZEND_ASSERT(ast->kind == ZEND_AST_ATTRIBUTE_LIST);
 
-       for (i = 0; i < list->children; i++) {
-               ZEND_ASSERT(list->child[i]->kind == ZEND_AST_ATTRIBUTE);
+       for (g = 0; g < list->children; g++) {
+               zend_ast_list *group = zend_ast_get_list(list->child[g]);
 
-               zend_ast *el = list->child[i];
-               zend_string *name = zend_resolve_class_name_ast(el->child[0]);
-               zend_ast_list *args = el->child[1] ? zend_ast_get_list(el->child[1]) : NULL;
+               ZEND_ASSERT(group->kind == ZEND_AST_ATTRIBUTE_GROUP);
 
-               attr = zend_add_attribute(attributes, 0, offset, name, args ? args->children : 0);
-               zend_string_release(name);
+               for (i = 0; i < group->children; i++) {
+                       ZEND_ASSERT(group->child[i]->kind == ZEND_AST_ATTRIBUTE);
 
-               /* Populate arguments */
-               if (args) {
-                       ZEND_ASSERT(args->kind == ZEND_AST_ARG_LIST);
+                       zend_ast *el = group->child[i];
+                       zend_string *name = zend_resolve_class_name_ast(el->child[0]);
+                       zend_ast_list *args = el->child[1] ? zend_ast_get_list(el->child[1]) : NULL;
 
-                       zend_bool uses_named_args = 0;
-                       for (j = 0; j < args->children; j++) {
-                               zend_ast *arg_ast = args->child[j];
+                       attr = zend_add_attribute(attributes, 0, offset, name, args ? args->children : 0);
+                       zend_string_release(name);
 
-                               if (arg_ast->kind == ZEND_AST_UNPACK) {
-                                       zend_error_noreturn(E_COMPILE_ERROR,
-                                               "Cannot use unpacking in attribute argument list");
-                               }
+                       /* Populate arguments */
+                       if (args) {
+                               ZEND_ASSERT(args->kind == ZEND_AST_ARG_LIST);
 
-                               if (arg_ast->kind == ZEND_AST_NAMED_ARG) {
-                                       attr->args[j].name = zend_string_copy(zend_ast_get_str(arg_ast->child[0]));
-                                       arg_ast = arg_ast->child[1];
-                                       uses_named_args = 1;
+                               zend_bool uses_named_args = 0;
+                               for (j = 0; j < args->children; j++) {
+                                       zend_ast *arg_ast = args->child[j];
 
-                                       for (uint32_t k = 0; k < j; k++) {
-                                               if (attr->args[k].name &&
-                                                               zend_string_equals(attr->args[k].name, attr->args[j].name)) {
-                                                       zend_error_noreturn(E_COMPILE_ERROR, "Duplicate named parameter $%s",
-                                                               ZSTR_VAL(attr->args[j].name));
+                                       if (arg_ast->kind == ZEND_AST_UNPACK) {
+                                               zend_error_noreturn(E_COMPILE_ERROR,
+                                                       "Cannot use unpacking in attribute argument list");
+                                       }
+
+                                       if (arg_ast->kind == ZEND_AST_NAMED_ARG) {
+                                               attr->args[j].name = zend_string_copy(zend_ast_get_str(arg_ast->child[0]));
+                                               arg_ast = arg_ast->child[1];
+                                               uses_named_args = 1;
+
+                                               for (uint32_t k = 0; k < j; k++) {
+                                                       if (attr->args[k].name &&
+                                                                       zend_string_equals(attr->args[k].name, attr->args[j].name)) {
+                                                               zend_error_noreturn(E_COMPILE_ERROR, "Duplicate named parameter $%s",
+                                                                       ZSTR_VAL(attr->args[j].name));
+                                                       }
                                                }
+                                       } else if (uses_named_args) {
+                                               zend_error_noreturn(E_COMPILE_ERROR,
+                                                       "Cannot use positional argument after named argument");
                                        }
-                               } else if (uses_named_args) {
-                                       zend_error_noreturn(E_COMPILE_ERROR,
-                                               "Cannot use positional argument after named argument");
-                               }
 
-                               zend_const_expr_to_zval(&attr->args[j].value, arg_ast);
+                                       zend_const_expr_to_zval(&attr->args[j].value, arg_ast);
+                               }
                        }
                }
        }
index 1a566e352d2d9f47a0e24e9a486c25fcd2c325a1..ed800c46f867e5caac2b17103c836003b333ded6 100644 (file)
@@ -178,7 +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_ATTRIBUTE    "'#['"
 %token T_PLUS_EQUAL   "'+='"
 %token T_MINUS_EQUAL  "'-='"
 %token T_MUL_EQUAL    "'*='"
@@ -266,7 +266,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
 %type <ast> identifier type_expr_without_static union_type_without_static
 %type <ast> inline_function union_type
 %type <ast> attributed_statement attributed_class_statement attributed_parameter
-%type <ast> attribute_decl attribute attributes namespace_declaration_name
+%type <ast> attribute_decl attribute attributes attribute_group namespace_declaration_name
 %type <ast> match match_arm_list non_empty_match_arm_list match_arm match_arm_cond_list
 
 %type <num> returns_ref function fn is_reference is_variadic variable_modifiers
@@ -345,8 +345,15 @@ attribute_decl:
                        { $$ = zend_ast_create(ZEND_AST_ATTRIBUTE, $1, $2); }
 ;
 
+attribute_group:
+               attribute_decl
+                       { $$ = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_GROUP, $1); }
+       |       attribute_group ',' attribute_decl
+                       { $$ = zend_ast_list_add($1, $3); }
+;
+
 attribute:
-               T_ATTRIBUTE attribute_decl      { $$ = $2; }
+               T_ATTRIBUTE attribute_group possible_comma ']'  { $$ = $2; }
 ;
 
 attributes:
index ee88282fd517626b9e293c9772770b6889f34bc8..b00e4e3edb61a3f244c24446f55710969c418e58 100644 (file)
@@ -1411,7 +1411,8 @@ NEWLINE ("\r"|"\n"|"\r\n")
        RETURN_TOKEN_WITH_IDENT(T_RETURN);
 }
 
-<ST_IN_SCRIPTING>"@@" {
+<ST_IN_SCRIPTING>"#[" {
+       enter_nesting('[');
        RETURN_TOKEN(T_ATTRIBUTE);
 }
 
index 8795a4c5dcddf1ba9ddfac11cf01e26aef03cd54..f4211ca769a1737f81f88b2a8ce31c77895d8f52 100644 (file)
@@ -3,7 +3,7 @@ Attributes are exposed as tokens.
 --FILE--
 <?php
 
-$tokens = token_get_all('<?php @@A1(1, 2) class C1 { }');
+$tokens = token_get_all('<?php #[A1(1, 2)] class C1 { }');
 
 $attr = $tokens[1];
 var_dump(token_name(T_ATTRIBUTE));
@@ -16,5 +16,5 @@ var_dump($class[1]);
 --EXPECT--
 string(11) "T_ATTRIBUTE"
 bool(true)
-string(2) "@@"
+string(2) "#["
 string(2) "A1"
index 64fd421292e76aadcec72db7b4da4434e9f85eb3..2d91543cf1f66fa46a16a548cf19e97dc54d1b48 100644 (file)
@@ -275,7 +275,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]");
        }
 }