PHP Foreach Pass by Reference: Last Element Duplicating? (Bug?)
After the first foreach loop, $item
is still a reference to some value which is also being used by $arr[2]
. So each foreach call in the second loop, which does not call by reference, replaces that value, and thus $arr[2]
, with the new value.
So loop 1, the value and $arr[2]
become $arr[0]
, which is 'foo'.
Loop 2, the value and $arr[2]
become $arr[1]
, which is 'bar'.
Loop 3, the value and $arr[2]
become $arr[2]
, which is 'bar' (because of loop 2).
The value 'baz' is actually lost at the first call of the second foreach loop.
Debugging the Output
For each iteration of the loop, we'll echo the value of $item
as well as recursively print the array $arr
.
When the first loop is run through, we see this output:
foo
Array ( [0] => foo [1] => bar [2] => baz )
bar
Array ( [0] => foo [1] => bar [2] => baz )
baz
Array ( [0] => foo [1] => bar [2] => baz )
At the end of the loop, $item
is still pointing to the same place as $arr[2]
.
When the second loop is run through, we see this output:
foo
Array ( [0] => foo [1] => bar [2] => foo )
bar
Array ( [0] => foo [1] => bar [2] => bar )
bar
Array ( [0] => foo [1] => bar [2] => bar )
You'll notice how each time array put a new value into $item
, it also updated $arr[3]
with that same value, since they are both still pointing to the same location. When the loop gets to the third value of the array, it will contain the value bar
because it was just set by the previous iteration of that loop.
Is it a bug?
No. This is the behavior of a referenced item, and not a bug. It would be similar to running something like:
for ($i = 0; $i < count($arr); $i++) { $item = $arr[$i]; }
A foreach loop isn't special in nature in which it can ignore referenced items. It's simply setting that variable to the new value each time like you would outside of a loop.
Why php iteration by reference returns a duplicate last record?
I'll guess that you're reusing &$item
here and that you're stumbling across a behavior which has been reported as bug a thousand times but is the correct behavior of references, which is why the manual advises:
Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset().
foreach($arrayOfJsonMods as &$item)
{
//TODO memcached votes
}
unset($item);
See https://bugs.php.net/bug.php?id=29992
foreach loop - php - weird behavior
It's PHP's behavior of a referenced item.
Reason : I think this guy can explain better than me.
Just a little memo.
Alternatively, to fix this, you can add &
to $age
at your third foreach:
foreach( $employee_age as $name => &$age){
echo "Name: $name, Age: $age <br />";
}
PHP Pass by reference in foreach
Because on the second loop, $v
is still a reference to the last array item, so it's overwritten each time.
You can see it like that:
$a = array ('zero','one','two', 'three');
foreach ($a as &$v) {
}
foreach ($a as $v) {
echo $v.'-'.$a[3].PHP_EOL;
}
As you can see, the last array item takes the current loop value: 'zero', 'one', 'two', and then it's just 'two'... : )
foreach and reference
From the docs:
Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset().
So in your case:
<?php
$foo = array('one', 'two', 'three');
foreach ($foo as &$bar)
{
// no-op
}
var_dump($foo);
unset($bar);
foreach ($foo as $bar)
{
// no-op
}
var_dump($foo);
?>
PHP pass by reference bug?
Not a bug, if you want to reuse variable previously used as reference, you need to do this:
unset($var);
Specifically, adding this one line after your first loop:
foreach($array as &$reference){
if($reference == "one") $reference = "one_changed";
}
unset($reference); // <--- this
Resolves all problems:
Array
(
[0] => one
[1] => two
[2] => three
)
Array
(
[0] => one_changed
[1] => two
[2] => three
)
====changed them but did not push onto array====<br>====changed also====Array
(
[0] => one_changed
[1] => two
[2] => three
)
====changed also====Array
(
[0] => one_changed
[1] => two
[2] => three
)
And one more thing. Array elements also can be references, e.g.:
$a = 1;
$c=[];
$c[0] =& $a;
$c[1] =& $a;
print_r($c);
$a=2;
print_r($c);
That will output:
Array
(
[0] => 1
[1] => 1
)
Array
(
[0] => 2
[1] => 2
)
So, to explain what exactly going on in your code... I'd rather pass. You make a referenced loop variable, then you use it as non-referenced variable in another loop and push it as a value in other array... Screw it, I give up.
Strange behavior of foreach when using reference: foreach ($a as &$v) { ... }
This is well-documented PHP behaviour
See the warning on the foreach page of php.net
Warning
Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset().
$a = array('a', 'b', 'c', 'd');
foreach ($a as &$v) { }
unset($v);
foreach ($a as $v) { }
print_r($a);
EDIT
Attempt at a step-by-step guide to what is actually happening here
$a = array('a', 'b', 'c', 'd');
foreach ($a as &$v) { } // 1st iteration $v is a reference to $a[0] ('a')
foreach ($a as &$v) { } // 2nd iteration $v is a reference to $a[1] ('b')
foreach ($a as &$v) { } // 3rd iteration $v is a reference to $a[2] ('c')
foreach ($a as &$v) { } // 4th iteration $v is a reference to $a[3] ('d')
// At the end of the foreach loop,
// $v is still a reference to $a[3] ('d')
foreach ($a as $v) { } // 1st iteration $v (still a reference to $a[3])
// is set to a value of $a[0] ('a').
// Because it is a reference to $a[3],
// it sets $a[3] to 'a'.
foreach ($a as $v) { } // 2nd iteration $v (still a reference to $a[3])
// is set to a value of $a[1] ('b').
// Because it is a reference to $a[3],
// it sets $a[3] to 'b'.
foreach ($a as $v) { } // 3rd iteration $v (still a reference to $a[3])
// is set to a value of $a[2] ('c').
// Because it is a reference to $a[3],
// it sets $a[3] to 'c'.
foreach ($a as $v) { } // 4th iteration $v (still a reference to $a[3])
// is set to a value of $a[3] ('c' since
// the last iteration).
// Because it is a reference to $a[3],
// it sets $a[3] to 'c'.
Related Topics
PHP Algorithm to Generate All Combinations of a Specific Size from a Single Set
Converting HTML to Plain Text in PHP For E-Mail
How to Loop Through a MySQL Result Set
How to Check File Types of Uploaded Files in PHP
The MySQLi Extension Is Missing. Please Check Your PHP Configuration
How to Find Out How "Deep" a PHP Array Is
PHP Namespace Simplexml Problems
PHP Json_Encode Not Working on Arrays Partially
In a PHP/Apache/Linux Context, Why Exactly Is Chmod 777 Dangerous
Causes of MySQL Error 2014 Cannot Execute Queries While Other Unbuffered Queries Are Active
Passing JavaScript Array to PHP Through Jquery $.Ajax
Best Way to Defend Against MySQL Injection and Cross Site Scripting
Including a Remote File in PHP
The Character Encoding of the HTML Document Was Not Declared