Skip to content

Commit 26ad0f2

Browse files
Run validators for delayed validators
1 parent 818e647 commit 26ad0f2

File tree

6 files changed

+275
-2
lines changed

6 files changed

+275
-2
lines changed
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
--TEST--
2+
#[\DelayedTargetValidation] affects errors from validators
3+
--FILE--
4+
<?php
5+
6+
#[DelayedTargetValidation]
7+
#[AllowDynamicProperties]
8+
trait DemoTrait {}
9+
10+
#[DelayedTargetValidation]
11+
#[AllowDynamicProperties]
12+
interface DemoInterface {}
13+
14+
#[DelayedTargetValidation]
15+
#[AllowDynamicProperties]
16+
readonly class DemoReadonly {}
17+
18+
#[DelayedTargetValidation]
19+
#[AllowDynamicProperties]
20+
enum DemoEnum {}
21+
22+
$cases = [
23+
new ReflectionClass('DemoTrait'),
24+
new ReflectionClass('DemoInterface'),
25+
new ReflectionClass('DemoReadonly'),
26+
new ReflectionClass('DemoEnum'),
27+
];
28+
foreach ($cases as $r) {
29+
echo str_repeat("*", 20) . "\n";
30+
echo $r . "\n";
31+
$attributes = $r->getAttributes();
32+
var_dump($attributes);
33+
try {
34+
$attributes[1]->newInstance();
35+
} catch (Error $e) {
36+
echo get_class($e) . ": " . $e->getMessage() . "\n";
37+
}
38+
}
39+
40+
?>
41+
--EXPECTF--
42+
********************
43+
Trait [ <user> trait DemoTrait ] {
44+
@@ %s %d-%d
45+
46+
- Constants [0] {
47+
}
48+
49+
- Static properties [0] {
50+
}
51+
52+
- Static methods [0] {
53+
}
54+
55+
- Properties [0] {
56+
}
57+
58+
- Methods [0] {
59+
}
60+
}
61+
62+
array(2) {
63+
[0]=>
64+
object(ReflectionAttribute)#%d (1) {
65+
["name"]=>
66+
string(23) "DelayedTargetValidation"
67+
}
68+
[1]=>
69+
object(ReflectionAttribute)#%d (1) {
70+
["name"]=>
71+
string(22) "AllowDynamicProperties"
72+
}
73+
}
74+
Error: Cannot apply #[AllowDynamicProperties] to trait DemoTrait
75+
********************
76+
Interface [ <user> interface DemoInterface ] {
77+
@@ %s %d-%d
78+
79+
- Constants [0] {
80+
}
81+
82+
- Static properties [0] {
83+
}
84+
85+
- Static methods [0] {
86+
}
87+
88+
- Properties [0] {
89+
}
90+
91+
- Methods [0] {
92+
}
93+
}
94+
95+
array(2) {
96+
[0]=>
97+
object(ReflectionAttribute)#%d (1) {
98+
["name"]=>
99+
string(23) "DelayedTargetValidation"
100+
}
101+
[1]=>
102+
object(ReflectionAttribute)#%d (1) {
103+
["name"]=>
104+
string(22) "AllowDynamicProperties"
105+
}
106+
}
107+
Error: Cannot apply #[AllowDynamicProperties] to interface DemoInterface
108+
********************
109+
Class [ <user> readonly class DemoReadonly ] {
110+
@@ %s %d-%d
111+
112+
- Constants [0] {
113+
}
114+
115+
- Static properties [0] {
116+
}
117+
118+
- Static methods [0] {
119+
}
120+
121+
- Properties [0] {
122+
}
123+
124+
- Methods [0] {
125+
}
126+
}
127+
128+
array(2) {
129+
[0]=>
130+
object(ReflectionAttribute)#%d (1) {
131+
["name"]=>
132+
string(23) "DelayedTargetValidation"
133+
}
134+
[1]=>
135+
object(ReflectionAttribute)#%d (1) {
136+
["name"]=>
137+
string(22) "AllowDynamicProperties"
138+
}
139+
}
140+
Error: Cannot apply #[AllowDynamicProperties] to readonly class DemoReadonly
141+
********************
142+
Enum [ <user> enum DemoEnum implements UnitEnum ] {
143+
@@ %s %d-%d
144+
145+
- Constants [0] {
146+
}
147+
148+
- Static properties [0] {
149+
}
150+
151+
- Static methods [1] {
152+
Method [ <internal, prototype UnitEnum> static public method cases ] {
153+
154+
- Parameters [0] {
155+
}
156+
- Return [ array ]
157+
}
158+
}
159+
160+
- Properties [1] {
161+
Property [ public protected(set) readonly string $name ]
162+
}
163+
164+
- Methods [0] {
165+
}
166+
}
167+
168+
array(2) {
169+
[0]=>
170+
object(ReflectionAttribute)#%d (1) {
171+
["name"]=>
172+
string(23) "DelayedTargetValidation"
173+
}
174+
[1]=>
175+
object(ReflectionAttribute)#%d (1) {
176+
["name"]=>
177+
string(22) "AllowDynamicProperties"
178+
}
179+
}
180+
Error: Cannot apply #[AllowDynamicProperties] to enum DemoEnum

Zend/tests/attributes/delayed_target_validation/has_runtime_errors.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
#[\DelayedTargetValidation] prevents target errors at compile time
2+
#[\DelayedTargetValidation] has errors at runtime
33
--FILE--
44
<?php
55

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
--TEST--
2+
#[\DelayedTargetValidation] with a successful validator
3+
--FILE--
4+
<?php
5+
6+
#[DelayedTargetValidation]
7+
#[AllowDynamicProperties]
8+
class DemoClass {}
9+
10+
$obj = new DemoClass();
11+
var_dump($obj);
12+
// No warnings
13+
$obj->dynamic = true;
14+
var_dump($obj);
15+
16+
$ref = new ReflectionClass('DemoClass');
17+
echo $ref . "\n";
18+
$attributes = $ref->getAttributes();
19+
var_dump($attributes);
20+
var_dump($attributes[1]->newInstance());
21+
22+
?>
23+
--EXPECTF--
24+
object(DemoClass)#%d (0) {
25+
}
26+
object(DemoClass)#%d (1) {
27+
["dynamic"]=>
28+
bool(true)
29+
}
30+
Class [ <user> class DemoClass ] {
31+
@@ %s %d-%d
32+
33+
- Constants [0] {
34+
}
35+
36+
- Static properties [0] {
37+
}
38+
39+
- Static methods [0] {
40+
}
41+
42+
- Properties [0] {
43+
}
44+
45+
- Methods [0] {
46+
}
47+
}
48+
49+
array(2) {
50+
[0]=>
51+
object(ReflectionAttribute)#%d (1) {
52+
["name"]=>
53+
string(23) "DelayedTargetValidation"
54+
}
55+
[1]=>
56+
object(ReflectionAttribute)#%d (1) {
57+
["name"]=>
58+
string(22) "AllowDynamicProperties"
59+
}
60+
}
61+
object(AllowDynamicProperties)#%d (0) {
62+
}

Zend/zend_attributes.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,20 @@ static void validate_allow_dynamic_properties(
8787
if (target & ZEND_ATTRIBUTE_NO_TARGET_VALIDATION) {
8888
return;
8989
}
90+
if (target & ZEND_ATTRIBUTE_DELAYED_TARGET_VALIDATION) {
91+
// Should not have passed the first time
92+
ZEND_ASSERT((scope->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES) == 0);
93+
// Throw a catchable error at runtime
94+
zend_throw_error(NULL, msg, ZSTR_VAL(scope->name));
95+
return;
96+
}
9097
zend_error_noreturn(E_ERROR, msg, ZSTR_VAL(scope->name) );
9198
}
99+
if (target & ZEND_ATTRIBUTE_DELAYED_TARGET_VALIDATION) {
100+
// Should have passed the first time
101+
ZEND_ASSERT((scope->ce_flags & ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES) != 0);
102+
return;
103+
}
92104
scope->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES;
93105
}
94106

Zend/zend_attributes.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,14 @@
3434
#define ZEND_ATTRIBUTE_IS_REPEATABLE (1<<7)
3535
#define ZEND_ATTRIBUTE_FLAGS ((1<<8) - 1)
3636

37-
/* Not a real flag, just passed to validators when target validation is *
37+
/* Not a real flag, just passed to validators when target validation is
3838
* suppressed; must not conflict with any of the real flags above. */
3939
#define ZEND_ATTRIBUTE_NO_TARGET_VALIDATION (1<<8)
4040

41+
/* Not a real flag, just passed to validators when target validation is
42+
* being run at runtime; must not conflict with any of the real flags above. */
43+
#define ZEND_ATTRIBUTE_DELAYED_TARGET_VALIDATION (1<<9)
44+
4145
/* Flags for zend_attribute.flags */
4246
#define ZEND_ATTRIBUTE_PERSISTENT (1<<0)
4347
#define ZEND_ATTRIBUTE_STRICT_TYPES (1<<1)

ext/reflection/php_reflection.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7309,6 +7309,21 @@ ZEND_METHOD(ReflectionAttribute, newInstance)
73097309
RETURN_THROWS();
73107310
}
73117311

7312+
/* Run the delayed validator function for internal attributes */
7313+
if (ce->type == ZEND_INTERNAL_CLASS) {
7314+
zend_internal_attribute *config = zend_internal_attribute_get(attr->data->lcname);
7315+
if (config != NULL && config->validator != NULL) {
7316+
config->validator(
7317+
attr->data,
7318+
attr->target | ZEND_ATTRIBUTE_DELAYED_TARGET_VALIDATION,
7319+
attr->scope
7320+
);
7321+
if (EG(exception)) {
7322+
RETURN_THROWS();
7323+
}
7324+
}
7325+
}
7326+
73127327
/* Repetition validation is done even if #[DelayedTargetValidation] is used
73137328
* and so can be skipped for internal attributes. */
73147329
if (ce->type == ZEND_USER_CLASS) {

0 commit comments

Comments
 (0)