Skip to content

Commit e870452

Browse files
authored
Merge pull request #8 from koolkode/FixAttributeValidation
Implement Changes Requested in PR Review
2 parents ea9277b + 141409e commit e870452

17 files changed

+447
-313
lines changed

Zend/tests/attributes/002_rfcexample.phpt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ namespace {
3030
var_dump($attributes[0]->getArguments());
3131
var_dump($attributes[0]->newInstance());
3232
}
33+
?>
3334
--EXPECTF--
3435
string(28) "My\Attributes\SingleArgument"
3536
array(1) {

Zend/tests/attributes/003_ast_nodes.phpt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,37 @@ var_dump(count($args));
5050
var_dump($args[0] === 'foo');
5151
var_dump($args[1] === C1::BAR);
5252

53+
echo "\n";
54+
5355
<<ExampleWithShift(4 >> 1)>>
5456
class C4 {}
5557
$ref = new \ReflectionClass(C4::class);
5658
var_dump($ref->getAttributes()[0]->getArguments());
5759

60+
echo "\n";
61+
62+
<<PhpAttribute>>
63+
class C5
64+
{
65+
public function __construct() { }
66+
}
67+
68+
$ref = new \ReflectionFunction(<<C5(MissingClass::SOME_CONST)>> function () { });
69+
$attr = $ref->getAttributes();
70+
var_dump(count($attr));
71+
72+
try {
73+
$attr[0]->getArguments();
74+
} catch (\Error $e) {
75+
var_dump($e->getMessage());
76+
}
77+
78+
try {
79+
$attr[0]->newInstance();
80+
} catch (\Error $e) {
81+
var_dump($e->getMessage());
82+
}
83+
5884
?>
5985
--EXPECT--
6086
int(1)
@@ -71,7 +97,13 @@ int(1)
7197
int(2)
7298
bool(true)
7399
bool(true)
100+
74101
array(1) {
75102
[0]=>
76103
int(2)
77104
}
105+
106+
int(1)
107+
string(30) "Class 'MissingClass' not found"
108+
string(30) "Class 'MissingClass' not found"
109+

Zend/tests/attributes/004_name_resolution.phpt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ namespace Foo {
2626
namespace {
2727
dump_attributes((new ReflectionFunction('Foo\foo'))->getAttributes());
2828
}
29+
?>
2930
--EXPECTF--
3031
array(1) {
3132
["Doctrine\ORM\Mapping\Entity"]=>

Zend/tests/attributes/005_objects.phpt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Attributes can be converted into objects.
33
--FILE--
44
<?php
55

6+
<<PhpAttribute>>
67
class A1
78
{
89
public string $name;
@@ -55,6 +56,7 @@ try {
5556

5657
echo "\n";
5758

59+
<<PhpAttribute>>
5860
class A3
5961
{
6062
private function __construct() { }
@@ -70,6 +72,7 @@ try {
7072

7173
echo "\n";
7274

75+
<<PhpAttribute>>
7376
class A4 { }
7477

7578
$ref = new \ReflectionFunction(<<A4(1)>> function () { });
@@ -80,6 +83,18 @@ try {
8083
var_dump('ERROR 5', $e->getMessage());
8184
}
8285

86+
echo "\n";
87+
88+
class A5 { }
89+
90+
$ref = new \ReflectionFunction(<<A5>> function () { });
91+
92+
try {
93+
$ref->getAttributes()[0]->newInstance();
94+
} catch (\Error $e) {
95+
var_dump('ERROR 6', $e->getMessage());
96+
}
97+
8398
?>
8499
--EXPECT--
85100
string(2) "A1"
@@ -100,3 +115,6 @@ string(50) "Attribute constructor of class 'A3' must be public"
100115

101116
string(7) "ERROR 5"
102117
string(71) "Attribute class 'A4' does not have a constructor, cannot pass arguments"
118+
119+
string(7) "ERROR 6"
120+
string(78) "Attempting to use class 'A5' as attribute that does not have <<PhpAttribute>>."
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
--TEST--
2+
Attribute arguments support only const expressions.
3+
--FILE--
4+
<?php
5+
6+
<<A1(foo())>>
7+
class C1 { }
8+
9+
?>
10+
--EXPECTF--
11+
Fatal error: Constant expression contains invalid operations in %s
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
--TEST--
2+
Attributes comply with inheritance rules.
3+
--FILE--
4+
<?php
5+
6+
<<A2>>
7+
class C1
8+
{
9+
<<A1>>
10+
public function foo() { }
11+
}
12+
13+
class C2 extends C1
14+
{
15+
public function foo() { }
16+
}
17+
18+
class C3 extends C1
19+
{
20+
<<A1>>
21+
public function bar() { }
22+
}
23+
24+
$ref = new \ReflectionClass(C1::class);
25+
print_r(array_map(fn ($a) => $a->getName(), $ref->getAttributes()));
26+
print_r(array_map(fn ($a) => $a->getName(), $ref->getMethod('foo')->getAttributes()));
27+
28+
$ref = new \ReflectionClass(C2::class);
29+
print_r(array_map(fn ($a) => $a->getName(), $ref->getAttributes()));
30+
print_r(array_map(fn ($a) => $a->getName(), $ref->getMethod('foo')->getAttributes()));
31+
32+
$ref = new \ReflectionClass(C3::class);
33+
print_r(array_map(fn ($a) => $a->getName(), $ref->getAttributes()));
34+
print_r(array_map(fn ($a) => $a->getName(), $ref->getMethod('foo')->getAttributes()));
35+
36+
echo "\n";
37+
38+
trait T1
39+
{
40+
<<A2>>
41+
public $a;
42+
}
43+
44+
class C4
45+
{
46+
use T1;
47+
}
48+
49+
class C5
50+
{
51+
use T1;
52+
53+
public $a;
54+
}
55+
56+
$ref = new \ReflectionClass(T1::class);
57+
print_r(array_map(fn ($a) => $a->getName(), $ref->getProperty('a')->getAttributes()));
58+
59+
$ref = new \ReflectionClass(C4::class);
60+
print_r(array_map(fn ($a) => $a->getName(), $ref->getProperty('a')->getAttributes()));
61+
62+
$ref = new \ReflectionClass(C5::class);
63+
print_r(array_map(fn ($a) => $a->getName(), $ref->getProperty('a')->getAttributes()));
64+
65+
?>
66+
--EXPECT--
67+
Array
68+
(
69+
[0] => A2
70+
)
71+
Array
72+
(
73+
[0] => A1
74+
)
75+
Array
76+
(
77+
)
78+
Array
79+
(
80+
)
81+
Array
82+
(
83+
)
84+
Array
85+
(
86+
[0] => A1
87+
)
88+
89+
Array
90+
(
91+
[0] => A2
92+
)
93+
Array
94+
(
95+
[0] => A2
96+
)
97+
Array
98+
(
99+
)

Zend/zend_ast.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2141,8 +2141,7 @@ zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr)
21412141
ast = zend_ast_create(ZEND_AST_CLASS_CONST_DECL_ATTRIBUTES, ast, attr);
21422142
ast->lineno = ast->child[0]->lineno;
21432143
break;
2144-
default:
2145-
zend_error_noreturn(E_COMPILE_ERROR, "Invalid use of attributes");
2144+
EMPTY_SWITCH_DEFAULT_CASE()
21462145
}
21472146

21482147
return ast;

Zend/zend_attributes.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
#include "zend_API.h"
33
#include "zend_attributes.h"
44

5-
void zend_attribute_validate_phpattribute(zval *attribute, int target)
5+
void zend_attribute_validate_phpattribute(zend_attribute *attr, int target)
66
{
77
if (target != ZEND_ATTRIBUTE_TARGET_CLASS) {
88
zend_error(E_COMPILE_ERROR, "Only classes can be marked with <<PhpAttribute>>");
99
}
1010
}
1111

12-
void zend_attribute_validate_phpcompilerattribute(zval *attribute, int target)
12+
void zend_attribute_validate_phpcompilerattribute(zend_attribute *attr, int target)
1313
{
1414
zend_error(E_COMPILE_ERROR, "The PhpCompilerAttribute can only be used by internal classes, use PhpAttribute instead");
1515
}

Zend/zend_attributes.h

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,59 @@
1212
zend_class_entry *zend_ce_php_attribute;
1313
zend_class_entry *zend_ce_php_compiler_attribute;
1414

15-
typedef void (*zend_attributes_internal_validator)(zval *attribute, int target);
15+
#define ZEND_ATTRIBUTE_SIZE(argc) (sizeof(zend_attribute) + sizeof(zval) * (argc) - sizeof(zval))
16+
17+
typedef struct _zend_attribute {
18+
zend_string *name;
19+
zend_string *lcname;
20+
uint32_t offset;
21+
uint32_t argc;
22+
zval argv[1];
23+
} zend_attribute;
24+
25+
static zend_always_inline void zend_attribute_release(zend_attribute *attr)
26+
{
27+
uint32_t i;
28+
29+
zend_string_release(attr->name);
30+
zend_string_release(attr->lcname);
31+
32+
for (i = 0; i < attr->argc; i++) {
33+
zval_ptr_dtor(&attr->argv[i]);
34+
}
35+
36+
efree(attr);
37+
}
38+
39+
static zend_always_inline zend_bool zend_has_attribute(HashTable *attributes, zend_string *name, uint32_t offset)
40+
{
41+
if (attributes) {
42+
zend_attribute *attr;
43+
44+
ZEND_HASH_FOREACH_PTR(attributes, attr) {
45+
if (attr->offset == offset && zend_string_equals(attr->lcname, name)) {
46+
return 1;
47+
}
48+
} ZEND_HASH_FOREACH_END();
49+
}
50+
51+
return 0;
52+
}
53+
54+
static zend_always_inline zend_bool zend_has_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset)
55+
{
56+
zend_bool result = 0;
57+
58+
if (attributes) {
59+
zend_string *name = zend_string_init(str, len, 0);
60+
result = zend_has_attribute(attributes, name, offset);
61+
zend_string_release(name);
62+
}
63+
64+
return result;
65+
}
66+
67+
typedef void (*zend_attributes_internal_validator)(zend_attribute *attr, int target);
1668
HashTable zend_attributes_internal_validators;
1769

1870
void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator);

0 commit comments

Comments
 (0)