Bernd
Bernd

Reputation: 825

PHP foreach loop modifies array without any assignment

I'm really getting crazy with this since two days now. I'd greatly appreciate if someone could give me a hint. I definitely can't understand why this PHP code:

$arr [] = [ "name" => "Chapter 1" ];
$arr [] = [ "name" => "Chapter 2" ];

foreach ( $arr as &$item )
    echo $item['name']."<br>";

echo "============<br>";

foreach ( $arr as $item )
    echo $item['name']."<br>";

gives this output:

Chapter 1
Chapter 2
============
Chapter 1
Chapter 1      (I would expect 'Chapter 2' here)

It looks like the first loop modifies the array, even though there is no assignment in the loop. Strangely enough, everything works as expected, when I remove the ampersand.

The thing I don't understand is, why is the array getting modified at all, even though I don't do anything with the reference variable '&$item' variable (except echoing it).

I also tried reset() between the loops. But it didn't change anything, and according to the manual it shouldn't be necessary anyway in such a case (at lease from my understanding) because the loops start after each other and are not nested somehow.

Thanks a lot!

Bernd

Upvotes: 1

Views: 327

Answers (3)

rpaskett
rpaskett

Reputation: 456

The array is not technically getting modified in your code sample, but a reference point is being attached to one of your array values. If you modify your example to read:

$arr [] = [ "name" => "Chapter 1" ];
$arr [] = [ "name" => "Chapter 2" ];
echo '<pre>';
var_dump($arr);

foreach ( $arr as &$item )
    echo $item['name']."<br>";

echo "============<br>";

var_dump($arr);

foreach ( $arr as $item )
    echo $item['name']."<br>";

echo $item['name'];

You can examine the var dump output and see the reference pointer in the second var dump:

array(2) {
    [0]=>
    array(1) {
        ["name"]=>
        string(9) "Chapter 1"
    }
    [1]=>
    array(1) {
        ["name"]=>
        string(9) "Chapter 2"
    }
}
Chapter 1
Chapter 2
============
array(2) {
    [0]=>
    array(1) {
        ["name"]=>
        string(9) "Chapter 1"
    }
    [1]=>
    &array(1) {
        ["name"]=>
        string(9) "Chapter 2"
    }
}
Chapter 1
Chapter 1

My theory is it has something to do with how PHP is handling reference pointers internally. As others have suggested, just run unset($item) in between the loops and you can mitigate the issue.

Upvotes: 0

Niet the Dark Absol
Niet the Dark Absol

Reputation: 324640

After any loop completes, the variables used in it still exist. For instance:

for( $i=0; $i<10; $i++) {
    // do something
}
echo $i; // 10

The same applies to references. After the loop is done, $item is still a reference to the last item in the array. Thus when you write foreach($arr as $item) a second time, you're now using that reference to the last element and repeatedly re-assigning it, which results in the last element of the array being assigned (by reference) the same thing as the second-to-last item.

To fix, be sure to clean up:

unset($item); // delete the reference

In theory you should clean up after any loop, but in pretty much all other cases it won't matter. It's just that in this case, it does!

Upvotes: 1

AndrewVT
AndrewVT

Reputation: 335

You can read more here http://pl.php.net/manual/en/control-structures.foreach.php

"In order to be able to directly modify array elements within the loop precede $value with &. In that case the value will be assigned by reference."

Example:

$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
    $value = $value * 2;
}
// $arr is now array(2, 4, 6, 8)
unset($value); // break the reference with the last element

Upvotes: 0

Related Questions