Skip to content

Commit 6bf867a

Browse files
beberleikooldev
andcommitted
Add Attributes based on attributes_v2 RFC.
Co-authored-by: Martin Schröder <m.schroeder2007@gmail.com>
1 parent dd163d0 commit 6bf867a

38 files changed

+1663
-52
lines changed
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
--TEST--
2+
Attributes can be placed on all supported elements.
3+
--FILE--
4+
<?php
5+
6+
<<A1(1)>>
7+
class Foo
8+
{
9+
<<A1(2)>>
10+
public const FOO = 'foo', BAR = 'bar';
11+
12+
<<A1(3)>>
13+
public $x, $y;
14+
15+
<<A1(4)>>
16+
public function foo(<<A1(5)>> $a, <<A1(6)>> $b) { }
17+
}
18+
19+
$object = new <<A1(7)>> class () { };
20+
21+
<<A1(8)>>
22+
function f1() { }
23+
24+
$f2 = <<A1(9)>> function () { };
25+
26+
$f3 = <<A1(10)>> fn () => 1;
27+
28+
$ref = new \ReflectionClass(Foo::class);
29+
30+
$sources = [
31+
$ref,
32+
$ref->getReflectionConstant('FOO'),
33+
$ref->getReflectionConstant('BAR'),
34+
$ref->getProperty('x'),
35+
$ref->getProperty('y'),
36+
$ref->getMethod('foo'),
37+
$ref->getMethod('foo')->getParameters()[0],
38+
$ref->getMethod('foo')->getParameters()[1],
39+
new \ReflectionObject($object),
40+
new \ReflectionFunction('f1'),
41+
new \ReflectionFunction($f2),
42+
new \ReflectionFunction($f3)
43+
];
44+
45+
foreach ($sources as $r) {
46+
foreach ($r->getAttributes() as $attr) {
47+
var_dump($attr->getName(), $attr->getArguments());
48+
}
49+
}
50+
51+
?>
52+
--EXPECT--
53+
string(2) "A1"
54+
array(1) {
55+
[0]=>
56+
int(1)
57+
}
58+
string(2) "A1"
59+
array(1) {
60+
[0]=>
61+
int(2)
62+
}
63+
string(2) "A1"
64+
array(1) {
65+
[0]=>
66+
int(2)
67+
}
68+
string(2) "A1"
69+
array(1) {
70+
[0]=>
71+
int(3)
72+
}
73+
string(2) "A1"
74+
array(1) {
75+
[0]=>
76+
int(3)
77+
}
78+
string(2) "A1"
79+
array(1) {
80+
[0]=>
81+
int(4)
82+
}
83+
string(2) "A1"
84+
array(1) {
85+
[0]=>
86+
int(5)
87+
}
88+
string(2) "A1"
89+
array(1) {
90+
[0]=>
91+
int(6)
92+
}
93+
string(2) "A1"
94+
array(1) {
95+
[0]=>
96+
int(7)
97+
}
98+
string(2) "A1"
99+
array(1) {
100+
[0]=>
101+
int(8)
102+
}
103+
string(2) "A1"
104+
array(1) {
105+
[0]=>
106+
int(9)
107+
}
108+
string(2) "A1"
109+
array(1) {
110+
[0]=>
111+
int(10)
112+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
--TEST--
2+
Attributes: RFC Example
3+
--FILE--
4+
<?php
5+
namespace My\Attributes {
6+
use PhpAttribute;
7+
8+
<<PhpAttribute>>
9+
class SingleArgument {
10+
public $argumentValue;
11+
12+
public function __construct($argumentValue) {
13+
$this->argumentValue = $argumentValue;
14+
}
15+
}
16+
}
17+
18+
namespace {
19+
use My\Attributes\SingleArgument;
20+
21+
<<SingleArgument("Hello World")>>
22+
class Foo {
23+
}
24+
25+
$reflectionClass = new \ReflectionClass(Foo::class);
26+
$attributes = $reflectionClass->getAttributes();
27+
28+
var_dump($attributes[0]->getName());
29+
var_dump($attributes[0]->getArguments());
30+
var_dump($attributes[0]->getAsObject());
31+
}
32+
--EXPECTF--
33+
string(28) "My\Attributes\SingleArgument"
34+
array(1) {
35+
[0]=>
36+
string(11) "Hello World"
37+
}
38+
object(My\Attributes\SingleArgument)#3 (1) {
39+
["argumentValue"]=>
40+
string(11) "Hello World"
41+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
--TEST--
2+
Attributes can deal with AST nodes.
3+
--FILE--
4+
<?php
5+
6+
define('V1', strtoupper(php_sapi_name()));
7+
8+
<<A1([V1 => V1])>>
9+
class C1
10+
{
11+
public const BAR = 'bar';
12+
}
13+
14+
$ref = new \ReflectionClass(C1::class);
15+
$attr = $ref->getAttributes();
16+
var_dump(count($attr));
17+
18+
$args = $attr[0]->getArguments();
19+
var_dump(count($args), $args[0][V1] === V1);
20+
21+
echo "\n";
22+
23+
<<A1(V1, 1 + 2, C1::class)>>
24+
class C2 { }
25+
26+
$ref = new \ReflectionClass(C2::class);
27+
$attr = $ref->getAttributes();
28+
var_dump(count($attr));
29+
30+
$args = $attr[0]->getArguments();
31+
var_dump(count($args));
32+
var_dump($args[0] === V1);
33+
var_dump($args[1] === 3);
34+
var_dump($args[2] === C1::class);
35+
36+
echo "\n";
37+
38+
<<A1(self::FOO, C1::BAR)>>
39+
class C3
40+
{
41+
private const FOO = 'foo';
42+
}
43+
44+
$ref = new \ReflectionClass(C3::class);
45+
$attr = $ref->getAttributes();
46+
var_dump(count($attr));
47+
48+
$args = $attr[0]->getArguments();
49+
var_dump(count($args));
50+
var_dump($args[0] === 'foo');
51+
var_dump($args[1] === C1::BAR);
52+
53+
?>
54+
--EXPECT--
55+
int(1)
56+
int(1)
57+
bool(true)
58+
59+
int(1)
60+
int(3)
61+
bool(true)
62+
bool(true)
63+
bool(true)
64+
65+
int(1)
66+
int(2)
67+
bool(true)
68+
bool(true)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
--TEST--
2+
Resolve attribute names
3+
--FILE--
4+
<?php
5+
function dump_attributes($attributes) {
6+
$arr = [];
7+
foreach ($attributes as $attribute) {
8+
$arr[$attribute->getName()] = $attribute->getArguments();
9+
}
10+
var_dump($arr);
11+
}
12+
13+
namespace Doctrine\ORM\Mapping {
14+
class Entity {
15+
}
16+
}
17+
18+
namespace Foo {
19+
use Doctrine\ORM\Mapping\Entity;
20+
21+
<<Entity(["foo" => "bar"])>>
22+
function foo() {
23+
}
24+
}
25+
26+
namespace {
27+
dump_attributes((new ReflectionFunction('Foo\foo'))->getAttributes());
28+
}
29+
--EXPECTF--
30+
array(1) {
31+
["Doctrine\ORM\Mapping\Entity"]=>
32+
array(1) {
33+
[0]=>
34+
array(1) {
35+
["foo"]=>
36+
string(3) "bar"
37+
}
38+
}
39+
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
--TEST--
2+
Attributes can be converted into objects.
3+
--FILE--
4+
<?php
5+
6+
class A1
7+
{
8+
public string $name;
9+
public int $ttl;
10+
11+
public function __construct(string $name, int $ttl = 50)
12+
{
13+
$this->name = $name;
14+
$this->ttl = $ttl;
15+
}
16+
}
17+
18+
$ref = new \ReflectionFunction(<<A1('test')>> function () { });
19+
20+
foreach ($ref->getAttributes() as $attr) {
21+
$obj = $attr->getAsObject();
22+
23+
var_dump(get_class($obj), $obj->name, $obj->ttl);
24+
}
25+
26+
echo "\n";
27+
28+
$ref = new \ReflectionFunction(<<A1>> function () { });
29+
30+
try {
31+
$ref->getAttributes()[0]->getAsObject();
32+
} catch (\ArgumentCountError $e) {
33+
var_dump('ERROR 1', $e->getMessage());
34+
}
35+
36+
echo "\n";
37+
38+
$ref = new \ReflectionFunction(<<A1([])>> function () { });
39+
40+
try {
41+
$ref->getAttributes()[0]->getAsObject();
42+
} catch (\TypeError $e) {
43+
var_dump('ERROR 2', $e->getMessage());
44+
}
45+
46+
echo "\n";
47+
48+
$ref = new \ReflectionFunction(<<A2>> function () { });
49+
50+
try {
51+
$ref->getAttributes()[0]->getAsObject();
52+
} catch (\Error $e) {
53+
var_dump('ERROR 3', $e->getMessage());
54+
}
55+
56+
echo "\n";
57+
58+
class A3
59+
{
60+
private function __construct() { }
61+
}
62+
63+
$ref = new \ReflectionFunction(<<A3>> function () { });
64+
65+
try {
66+
$ref->getAttributes()[0]->getAsObject();
67+
} catch (\Error $e) {
68+
var_dump('ERROR 4', $e->getMessage());
69+
}
70+
71+
echo "\n";
72+
73+
class A4 { }
74+
75+
$ref = new \ReflectionFunction(<<A4(1)>> function () { });
76+
77+
try {
78+
$ref->getAttributes()[0]->getAsObject();
79+
} catch (\Error $e) {
80+
var_dump('ERROR 5', $e->getMessage());
81+
}
82+
83+
?>
84+
--EXPECT--
85+
string(2) "A1"
86+
string(4) "test"
87+
int(50)
88+
89+
string(7) "ERROR 1"
90+
string(81) "Too few arguments to function A1::__construct(), 0 passed and at least 1 expected"
91+
92+
string(7) "ERROR 2"
93+
string(74) "A1::__construct(): Argument #1 ($name) must be of type string, array given"
94+
95+
string(7) "ERROR 3"
96+
string(30) "Attribute class 'A2' not found"
97+
98+
string(7) "ERROR 4"
99+
string(50) "Attribute constructor of class 'A3' must be public"
100+
101+
string(7) "ERROR 5"
102+
string(71) "Attribute class 'A4' does not have a constructor, cannot pass arguments"

0 commit comments

Comments
 (0)