1--TEST--
2foreach over hooked properties
3--FILE--
4<?php
5
6#[AllowDynamicProperties]
7class ByRef {
8    public $plain = 'plain';
9    private $_virtualByRef = 'virtualByRef';
10    public $virtualByRef {
11        &get {
12          echo __METHOD__, "\n";
13          return $this->_virtualByRef;
14        }
15        set {
16          echo __METHOD__, "\n";
17          $this->_virtualByRef = $value;
18        }
19    }
20    public $virtualSetOnly {
21        set {
22            echo __METHOD__, "\n";
23        }
24    }
25    public function __construct() {
26        $this->undef = 'dynamic';
27        $this->dynamic = 'dynamic';
28        unset($this->undef);
29    }
30}
31
32#[AllowDynamicProperties]
33class ByVal extends ByRef {
34    private $_virtualByVal = 'virtualByVal';
35    public $virtualByVal {
36        get {
37          echo __METHOD__, "\n";
38          return $this->_virtualByVal;
39        }
40        set {
41          echo __METHOD__, "\n";
42          $this->_virtualByVal = $value;
43        }
44    }
45    public $backed = 'backed' {
46        get {
47          echo __METHOD__, "\n";
48          return $this->backed;
49        }
50        set {
51          echo __METHOD__, "\n";
52          $this->backed = $value;
53        }
54    }
55    public string $backedUninitialized {
56        get {
57          echo __METHOD__, "\n";
58          $this->backedUninitialized ??= 'backedUninitialized';
59          return $this->backedUninitialized;
60        }
61        set {
62          echo __METHOD__, "\n";
63          $this->backedUninitialized = $value;
64        }
65    }
66}
67
68function testByRef($object) {
69    foreach ($object as $prop => &$value) {
70        echo "$prop => $value\n";
71        $value = strtoupper($value);
72    }
73    unset($value);
74    var_dump($object);
75}
76
77function testByVal($object) {
78    foreach ($object as $prop => $value) {
79        echo "$prop => $value\n";
80        $object->{$prop} = strtoupper($value);
81    }
82    var_dump($object);
83}
84
85testByVal(new ByVal);
86testByVal(new ByRef);
87testByRef(new ByRef);
88
89class A {
90    private $changed { get => 'A'; }
91    protected $promoted { get => 'A'; }
92    protected $protected { get => 'A'; }
93    private $shadowed = 'A';
94
95    public function test() {
96        foreach ($this as $k => $v) {
97            var_dump($k, $v);
98        }
99    }
100}
101
102#[AllowDynamicProperties]
103class B extends A {
104    public $changed { get => 'B'; }
105    public $promoted { get => 'B'; }
106}
107
108$b = new B;
109$b->shadowed = 'Global';
110$b->test();
111
112?>
113--EXPECTF--
114plain => plain
115ByRef::$virtualByRef::get
116virtualByRef => virtualByRef
117ByRef::$virtualByRef::set
118ByVal::$virtualByVal::get
119virtualByVal => virtualByVal
120ByVal::$virtualByVal::set
121ByVal::$backed::get
122backed => backed
123ByVal::$backed::set
124ByVal::$backedUninitialized::get
125backedUninitialized => backedUninitialized
126ByVal::$backedUninitialized::set
127dynamic => dynamic
128object(ByVal)#%d (6) {
129  ["plain"]=>
130  string(5) "PLAIN"
131  ["_virtualByRef":"ByRef":private]=>
132  string(12) "VIRTUALBYREF"
133  ["_virtualByVal":"ByVal":private]=>
134  string(12) "VIRTUALBYVAL"
135  ["backed"]=>
136  string(6) "BACKED"
137  ["backedUninitialized"]=>
138  string(19) "BACKEDUNINITIALIZED"
139  ["dynamic"]=>
140  string(7) "DYNAMIC"
141}
142plain => plain
143ByRef::$virtualByRef::get
144virtualByRef => virtualByRef
145ByRef::$virtualByRef::set
146dynamic => dynamic
147object(ByRef)#%d (3) {
148  ["plain"]=>
149  string(5) "PLAIN"
150  ["_virtualByRef":"ByRef":private]=>
151  string(12) "VIRTUALBYREF"
152  ["dynamic"]=>
153  string(7) "DYNAMIC"
154}
155plain => plain
156ByRef::$virtualByRef::get
157virtualByRef => virtualByRef
158dynamic => dynamic
159object(ByRef)#%d (3) {
160  ["plain"]=>
161  string(5) "PLAIN"
162  ["_virtualByRef":"ByRef":private]=>
163  string(12) "VIRTUALBYREF"
164  ["dynamic"]=>
165  string(7) "DYNAMIC"
166}
167string(7) "changed"
168string(1) "A"
169string(8) "promoted"
170string(1) "B"
171string(9) "protected"
172string(1) "A"
173string(8) "shadowed"
174string(1) "A"
175