A Problem and Solution to Use References in foreach of PHP

  • 2021-06-28 11:44:02
  • OfStack

1. Questions
Let's start with an example:

< ?php
$ar = array(1, 2, 3);
var_dump($ar);
foreach ($ar as & $v) {}
foreach ($ar as $v) {}
var_dump($ar);
? >
The output is:

array(3) {
[0]= >
int(1)
[1]= >
int(2)
[2]= >
int(3)
}
array(3) {
[0]= >
int(1)
[1]= >
int(2)
[2]= >
& int(2)
}
???Why has the value of the last element of the array changed without an assignment?

I've found this problem a long time ago. 1 I thought it was bug of PHP at first, and I just threw it away. foreach doesn't use references, it's OK to use foreach $k = > $v then $ar[$k] alters the original array, slightly losing point efficiency.

2. Analysis

Today I spent a little time reading the articles in the reference, which is a little clear. It used to be like this:

When the first foreach using a reference is executed, $v starts at $ar[0], has a storage space of 1, ends at foreach, $v points to $ar[2], and has a storage space of 3.To begin executing the second foreach, note that unlike the first foreach, the second foreach does not use a reference, and that is how you assign the value of $ar to $v in turn.When you go to the first element, assign $ar[0] to $v.The problem is here, $v is not a new variable, but an existing reference to $ar[2]. So when you assign $v, $ar[0] = 1 is written to the actual storage space of $ar[2], which is equivalent to assigning $ar[2].The result of the second foreach execution is that the last element of the array becomes the value of the second last element.A detailed schematic diagram is provided in Reference Article 2.

If this is an error, then the cause of the error is the use of the reference variable.When a reference variable points to other variables, changing the value of the reference variable will of course affect the other variables it points to.Everyone knows it, but in this foreach example, it happens that the same variable is used twice, first as a reference, and then as a normal variable, with unexpected results.The developers of PHP agree that this is due to language characteristics, not bug.Indeed, if you want to fix this problem, one way is to limit the scope of $v in foreach in addition to special handling of foreach. Both ways do not correspond to the current language characteristics of PHP and developers are reluctant to change them, but they are described in the official documentation using Warning.

3. Solutions

Simple, but not perfect, is that after using the referenced foreach, unset dropped $v and the starting example changed to:

< ?php
$ar = array(1, 2, 3);
var_dump($ar);
foreach ($ar as & $v) {}
unset($v);
foreach ($ar as $v) {}
var_dump($ar);
? >
Run result:

array(3) {
[0]= >
int(1)
[1]= >
int(2)
[2]= >
int(3)
}
array(3) {
[0]= >
int(1)
[1]= >
int(2)
[2]= >
int(3)
}


Reference resources

Bug #29992 foreach by reference corrupts the array:https://bugs.php.net/bug.php?id=29992
References and foreach:http://schlueters.de/blog/archives/141-References-and-foreach.html


Related articles: