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

4
  • remove "&" from &$item in first foreach :-) Commented Dec 27, 2014 at 23:44
  • 3
    Add unset($item) immediately after the first loop. Commented Dec 27, 2014 at 23:46
  • 1
    possible duplicate of PHP Pass by reference in foreach Commented Dec 27, 2014 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. Commented Dec 27, 2014 at 23:49

3 Answers 3

2

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!

Sign up to request clarification or add additional context in comments.

1 Comment

Yes that works. Thanks a lot for your ubelievable fast, good and detailed explanation! Simply great !!!
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

Comments

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.