File tree Expand file tree Collapse file tree 3 files changed +72
-5
lines changed Expand file tree Collapse file tree 3 files changed +72
-5
lines changed Original file line number Diff line number Diff line change
1
+ --TEST--
2
+ GH-19044: Protected properties must be scoped according to their prototype (protected(set) on non-hooked property)
3
+ --FILE--
4
+ <?php
5
+
6
+ class P {
7
+ public mixed $ foo { get => 42 ; }
8
+ }
9
+
10
+ class C1 extends P {
11
+ public protected(set) mixed $ foo = 1 ;
12
+ }
13
+
14
+ class C2 extends P {
15
+ public protected(set) mixed $ foo ;
16
+
17
+ static function foo ($ c ) { return $ c ->foo += 1 ; }
18
+ }
19
+
20
+ var_dump (C2 ::foo (new C1 ));
21
+
22
+ ?>
23
+ --EXPECTF--
24
+ Fatal error: Uncaught Error: Cannot modify protected(set) property C1::$foo from scope C2 in %s:%d
25
+ Stack trace:
26
+ #0 %s(%d): C2::foo(Object(C1))
27
+ #1 {main}
28
+ thrown in %s on line %d
Original file line number Diff line number Diff line change
1
+ --TEST--
2
+ GH-19044: Protected properties must be scoped according to their prototype (abstract parent has implicit set hook)
3
+ --FILE--
4
+ <?php
5
+
6
+ abstract class GP {
7
+ public abstract mixed $ foo { get; }
8
+ }
9
+
10
+ class P extends GP {
11
+ public protected(set) mixed $ foo { get => $ this ->foo ; }
12
+ }
13
+
14
+ class C1 extends P {
15
+ public protected(set) mixed $ foo = 1 ;
16
+ }
17
+
18
+ class C2 extends P {
19
+ public protected(set) mixed $ foo ;
20
+
21
+ static function foo ($ c ) { return $ c ->foo += 1 ; }
22
+ }
23
+
24
+ var_dump (C2 ::foo (new C1 ));
25
+
26
+ ?>
27
+ --EXPECT--
28
+ int(2)
Original file line number Diff line number Diff line change @@ -286,14 +286,25 @@ static zend_never_inline int is_protected_compatible_scope(const zend_class_entr
286
286
}
287
287
/* }}} */
288
288
289
- static zend_never_inline int is_asymmetric_set_protected_property_compatible_scope (const zend_property_info * info , const zend_class_entry * scope ) /* {{{ */
289
+ static int is_asymmetric_set_protected_property_compatible_scope (const zend_property_info * info , const zend_class_entry * scope ) /* {{{ */
290
290
{
291
291
zend_class_entry * ce ;
292
- if (!(info -> prototype -> flags & ZEND_ACC_PROTECTED_SET ) && info -> hooks && info -> hooks [ZEND_PROPERTY_HOOK_SET ]) {
293
- zend_function * hookfn = info -> hooks [ZEND_PROPERTY_HOOK_SET ];
294
- ce = hookfn -> common .prototype ? hookfn -> common .prototype -> common .scope : hookfn -> common .scope ;
292
+ /* we need to identify the common protected(set) ancestor: if the prototype has the protected(set), it's straightforward */
293
+ if (info -> prototype -> flags & ZEND_ACC_PROTECTED_SET ) {
294
+ ce = info -> prototype -> ce ;
295
+ } else if (info -> hooks && info -> hooks [ZEND_PROPERTY_HOOK_SET ]) {
296
+ /* shortcut: the visibility of hooks cannot be overwritten */
297
+ zend_function * hook = info -> hooks [ZEND_PROPERTY_HOOK_SET ];
298
+ ce = zend_get_function_root_class (hook );
295
299
} else {
296
- ce = info -> prototype -> ce ;
300
+ /* we do not have an easy way to find the ancestor which introduces the protected(set), let's iterate */
301
+ do {
302
+ ce = info -> ce ;
303
+ if (!ce -> parent -> properties_info_table ) {
304
+ break ;
305
+ }
306
+ info = ce -> parent -> properties_info_table [OBJ_PROP_TO_NUM (info -> offset )];
307
+ } while (info -> flags & ZEND_ACC_PROTECTED_SET );
297
308
}
298
309
return is_protected_compatible_scope (ce , scope );
299
310
}
You can’t perform that action at this time.
0 commit comments