7

This is the code that I don't understand (as the output).

<?php
$x = ['test1', 'test2', 'test3', 'test4'];
echo "First FOREACH\n";
foreach ($x as &$y)
{
    echo $y."\n";
}
echo "\n\nSecond FOREACH\n";
foreach ($x as $y)
{
    echo $y."\n";
}

?>

Output:

First FOREACH
test1
test2
test3
test4

Second FOREACH
test1
test2
test3
test3

PS: I'm running it under:

php -v
PHP 5.6.11-1ubuntu3.1 (cli) 
Copyright (c) 1997-2015 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2015 Zend Technologies
    with Zend OPcache v7.0.6-dev, Copyright (c) 1999-2015, by Zend Technologies
Tim Biegeleisen
  • 502,043
  • 27
  • 286
  • 360
Sorin Trimbitas
  • 1,467
  • 18
  • 35
  • 1
    @John, how does echoing the argument modify it? And, from what I see when running the code, the second loop does test1/test1. Something the OP may have put in the question if they weren't so damned obnoxious :-) – paxdiablo Dec 30 '15 at 08:25
  • John .. you did not understood the issue. Please run the code and you'll see what I mean. PS: the new line is not appending at all in such a code, there is no change of the $y variable anywhere. – Sorin Trimbitas Dec 30 '15 at 08:26
  • As the output shows, the final element of the array is being affected. @paxdiablo if you have a great explanation, we would love to hear it. – Tim Biegeleisen Dec 30 '15 at 08:29
  • @TimBiegeleisen .. the question remains .. why is this issue? I encountered it in code and spent 30 minutes until I realized the code is not the issue. – Sorin Trimbitas Dec 30 '15 at 08:31
  • The first foreach uses a reference to the original value, whereas the second creates a copy of the value. So after the first foreach runs, the original array is still untouched. But The first foreach run, the original array could have been modified since it was handled by reference. – Hardik Solanki Dec 30 '15 at 08:48
  • I finally understood. Thank you all for the help. I learned a new thing today :) – Sorin Trimbitas Dec 30 '15 at 08:51
  • 1
    @Tim, I didn't actually *have* an answer, great or otherwise :-) It just seemed strange that an echo would change the data which is why I questioned John. In any case, the answers that are now below seem to be clear as to what's happening. It wasn't the `echo` at all, rather the `as`. – paxdiablo Dec 30 '15 at 10:09

5 Answers5

5

It's more of a feature, it's documented in the foreach manpage

Warning Reference of a $value and the last array element remain even after the foreach loop. It is recommended to destroy it by unset().

There are some relevant comments on the manpage, here's one of them http://php.net/manual/en/control-structures.foreach.php#111688

More info on how this happens here http://schlueters.de/blog/archives/141-References-and-foreach.html

Alex Andrei
  • 7,315
  • 3
  • 28
  • 42
5

After the first foreach statement you have $y pointing to the last array item:

$x = ['test1', 'test2', 'test3', 'test4'];
$y =& $x[3];

Whenever you assign a value to $y original array will be modified.

When second foreach begins, on every iteration next value from $x is put into $y. So on every iteration original array will look like:

// first iteration
$x = ['test1', 'test2', 'test3', 'test1'];
// second iteration
$x = ['test1', 'test2', 'test3', 'test2'];
// third iteration
$x = ['test1', 'test2', 'test3', 'test3'];
// fourth iteration
$x = ['test1', 'test2', 'test3', 'test3'];
Ostin
  • 1,511
  • 1
  • 12
  • 25
0

It happened because of your first loop, $y is still a reference to the last array item, so it's overwritten each time.

When you use reference in loop, its recommended to destroy it by using unset() function.

For suppose, when you create a Global variable, it means you are creating a reference.

And one more example, when we use $this keyword within an object, we are creating the reference of this object instead of copy again.

devpro
  • 16,184
  • 3
  • 27
  • 38
0

I'd say you get something like.

First FOREACH test1 test2 Second FOREACH test1 test1

Because you are using the & with your $y in the first foreach so you make $y a reference to $x, the whole array $x. And $y will stay a reference until you unset it.
So...
Because you reuse $y in the next foreach, PHP is setting $x twice to the first element of $y being 'test1'. $x will be ['test1','test1'].

Always be careful while using references and always unset them when you no longer need them.

Calidris
  • 11
  • 5
  • After someone edited my question with 4 elements, it seems the last one is repeated, not the first one as I originally thought. – Sorin Trimbitas Dec 30 '15 at 08:43
0

Ok i think it happens like this:

when your first loop is done. the last element in the array is still referenced with the variable $y

think of your array like this when the first loop is done :

['test1', 'test2', 'test3', &'test4']

Note that i have inserted a & at the last element because it is reference with the variable $y

Now when the second loop begins. Each element in $x will be referenced as $y and remember in your array $x the last element is still being referenced by variable $y from the loop.

So the last element in array $x is modified in each iteration of the second loop. as $y

['test1', 'test2', 'test3', &'test1'] <-- $y as being first element, notice that the last element is also $y ['test1', 'test2', 'test3', &'test2'] $y being 'test2' ['test1', 'test2', 'test3', &'test3'] $y as 'test3' ['test1', 'test2', 'test3', &'test3'] $y being the last element which is 'test3'

John Pangilinan
  • 953
  • 1
  • 8
  • 25