1

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

Bernd
  • 675
  • 2
  • 8
  • 30
  • remove "&" from &$item in first foreach :-) – Eugen Dec 27 '14 at 23:44
  • 3
    Add `unset($item)` immediately after the first loop. – raina77ow Dec 27 '14 at 23:46
  • 1
    possible duplicate of [PHP Pass by reference in foreach](http://stackoverflow.com/questions/3307409/php-pass-by-reference-in-foreach) – raina77ow Dec 27 '14 at 23:47
  • Yes that would be the easiest way :-). But the snippet above is just a piece of code that I've stripped down to show the essential problem. The reason I can't remove it is that in the real code indeed I need to modify the array from within the loop. Of course a 'for' - loop also works well, but I don't like building workarounds just because I don't understand the initial problem. – Bernd Dec 27 '14 at 23:49

3 Answers3

1

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!

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • Yes that works. Thanks a lot for your ubelievable fast, good and detailed explanation! Simply great !!! – Bernd Dec 27 '14 at 23:59
0

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
AndrewVT
  • 335
  • 1
  • 8
0

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.

rpaskett
  • 456
  • 1
  • 3
  • 13