mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Change Attribute Syntax from @@ to #[]
This commit is contained in:
@@ -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";
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -3,7 +3,7 @@ Attributes: Prevent Attribute on non classes
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
@@Attribute
|
||||
#[Attribute]
|
||||
function foo() {}
|
||||
?>
|
||||
--EXPECTF--
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ Attribute arguments support only const expressions.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
@@A1(foo())
|
||||
#[A1(foo())]
|
||||
class C1 { }
|
||||
|
||||
?>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
}
|
||||
|
||||
|
||||
@@ -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() { }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ Attributes cannot be applied to groups of class constants.
|
||||
|
||||
class C1
|
||||
{
|
||||
@@A1
|
||||
#[A1]
|
||||
public const A = 1, B = 2;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ Attributes cannot be applied to groups of properties.
|
||||
|
||||
class C1
|
||||
{
|
||||
@@A1
|
||||
#[A1]
|
||||
public $x, $y;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) { };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
?>
|
||||
|
||||
@@ -3,7 +3,7 @@ Attribute name cannot be a variable
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
@@$x
|
||||
#[$x]
|
||||
class A {}
|
||||
|
||||
?>
|
||||
|
||||
@@ -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()));
|
||||
|
||||
@@ -3,7 +3,7 @@ Attribute flags type is validated.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
@@Attribute("foo")
|
||||
#[Attribute("foo")]
|
||||
class A1 { }
|
||||
|
||||
?>
|
||||
|
||||
@@ -3,7 +3,7 @@ Attribute flags value is validated.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
@@Attribute(-1)
|
||||
#[Attribute(-1)]
|
||||
class A1 { }
|
||||
|
||||
?>
|
||||
|
||||
@@ -3,7 +3,7 @@ Attribute flags value is validated.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
@@Attribute(Foo::BAR)
|
||||
#[Attribute(Foo::BAR)]
|
||||
class A1 { }
|
||||
|
||||
?>
|
||||
|
||||
@@ -3,7 +3,7 @@ Internal attribute targets are validated.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
@@Attribute
|
||||
#[Attribute]
|
||||
function a1() { }
|
||||
|
||||
?>
|
||||
|
||||
@@ -3,8 +3,8 @@ Internal attribute targets are validated.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
@@Attribute
|
||||
@@Attribute
|
||||
#[Attribute]
|
||||
#[Attribute]
|
||||
class A1 { }
|
||||
|
||||
?>
|
||||
|
||||
@@ -3,7 +3,7 @@ Cannot use unpacking in attribute argument list
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
@@MyAttribute(...[1, 2, 3])
|
||||
#[MyAttribute(...[1, 2, 3])]
|
||||
class Foo { }
|
||||
|
||||
?>
|
||||
|
||||
@@ -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);
|
||||
|
||||
55
Zend/tests/attributes/028_grouped.phpt
Normal file
55
Zend/tests/attributes/028_grouped.phpt
Normal file
@@ -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()
|
||||
@@ -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
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
?>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
?>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
} 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);
|
||||
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");
|
||||
}
|
||||
|
||||
zend_const_expr_to_zval(&attr->args[j].value, arg_ast);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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]");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user