You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Dereferencing an array in foreach unwraps the reference #11665
Bisect lead me to 61d00a6 as the first bad commit, although looking at the generated opcodes makes me wonder why there is no W fetch so the root cause is probably different EDIT: I'm blind
@usarise Not quite - maybe that's a side-effect of the change, but that change refers to the sequencing when unwrapping happens, when a read as a temporary value and a write of the same reference happen to be arguments for the same operation.
I don't think we should fix anything here. For me this behaviour is as expected. The expression is a temporary, and foreach by reference over a temporary iterates a copy. There is also a very simple workaround, just bind a variable first before the loop or encapsulate it in a function.
Fixing this edge case would have a negative effect on OPCache. See also 61d00a6cf3d
If anything, it looks to me like PHP 7.3 fixed a reference leak on read operation. A temporary should not leak a reference. This is exactly the change that usarise referred to.
foreach ([&$a] as &$v): The temporary is the array [&$a] itself. Its element 0 is a reference to $a. When an array is copied, references inside it stay references. Foreach binds $v to element 0, and that element is already a reference to $a, so $v++ changes $a. No read fetch is involved, so nothing unwraps the reference.
foreach ([&$a][0] as &$v): The temporary is the result of [0]. The read fetch unwraps the reference, so the temporary holds the value of $a, the array [1]. Its elements are plain values, not references. $v binds to an element of that copy, so $v++ does not reach $a.
Activity
ndossche commentedon Jul 10, 2023
Bisect lead me to 61d00a6 as the first bad commit,
although looking at the generated opcodes makes me wonder why there is no W fetch so the root cause is probably differentEDIT: I'm blindusarise commentedon Jul 10, 2023
Is not it natural effect of backward incompatible change?
bwoebi commentedon Jul 10, 2023
@usarise Not quite - maybe that's a side-effect of the change, but that change refers to the sequencing when unwrapping happens, when a read as a temporary value and a write of the same reference happen to be arguments for the same operation.
kamil-tekiela commentedon Nov 7, 2026
Simplified:
I don't think we should fix anything here. For me this behaviour is as expected. The expression is a temporary, and foreach by reference over a temporary iterates a copy. There is also a very simple workaround, just bind a variable first before the loop or encapsulate it in a function.
This is similar to:
Fixing this edge case would have a negative effect on OPCache. See also 61d00a6cf3d
If anything, it looks to me like PHP 7.3 fixed a reference leak on read operation. A temporary should not leak a reference. This is exactly the change that usarise referred to.
Compare it to:
foreach ([&$a] as &$v): The temporary is the array[&$a]itself. Its element 0 is a reference to$a. When an array is copied, references inside it stay references. Foreach binds$vto element 0, and that element is already a reference to$a, so$v++changes$a. No read fetch is involved, so nothing unwraps the reference.foreach ([&$a][0] as &$v): The temporary is the result of[0]. The read fetch unwraps the reference, so the temporary holds the value of$a, the array[1]. Its elements are plain values, not references.$vbinds to an element of that copy, so$v++does not reach$a.