The danger of: foreach ($array as &$v)

This will be a short post, concerning one problem that can easily arise from the incorrect usage of PHP’s foreach cycle’s ability to pass each array item by reference instead of by value. I ran into this problem a little while ago and it caused me roughly about an hour of general wondering of why the stupid script just doesn’t work.

PHP’s foreach cycle is a very useful feature of this language (as it is for all other languages that have such a feature), and is used by PHP programmers all the time. There are two main versions of this cycle:

foreach ($array as $value)

With the above version, at each iteration of the cycle you get one item from $array in the variable $value. You can use this value, but you cannot modify the original array item. However, you can still modify $array, but in order to modify the specific item, that was passed in each iteration as $value, you need to know the array key of this item. You can do that by using:

foreach ($array as $key => $value)

Now you can modify each array item, by using $array[$key].

But there is a shorter, and (from the first look) easier way to do this. That way is by passing each array item by reference instead as by value. I won’t go into detail as to what passing by reference means, if you don’t know that read about it at the PHP documentation. For people familiar with C, you can think of references as pointers to variables.

You can pass values as references by prepending the variable name with the reference symbol (&) like this:

foreach ($array as &$value)

Now, if you change the variable $value, the corresponding item of $array will change too.

The problem

If you will run the following script:

$array1 = Array(1, 2, 3, 4);

foreach ($array1 as &$v)
	$v = $v * 2;

print_r($array1);

You will get the expected output, which will be the same array, only each of it’s items will be doubled:

Array
(
    [0] => 2
    [1] => 4
    [2] => 6
    [3] => 8
)

Now, if you add another foreach after this one like this:

$array1 = Array(1, 2, 3, 4);

foreach ($array1 as &$v)
	$v = $v * 2;

print_r($array1);

$array2 = Array('A', 'B', 'C', 'D');

foreach ($array2 as $v)
	echo $v."\n";

print_r($array1);

you will get this output:

Array
(
    [0] => 2
    [1] => 4
    [2] => 6
    [3] => 8
)
A
B
C
D
Array
(
    [0] => 2
    [1] => 4
    [2] => 6
    [3] => D
)

Note the last item of $array1 as printed the second time:
[3] => D
It changed from 8.

The reason for this is that the variable $v is kept available after the first foreach() has ended. This is normal, this is how foreach() always works. However, remember that after the cycle, $v now points to the last item of $array1. So if you will modify $v now, you will effectively modify the last item of $array1.

This is exactly what happens when you run the second foreach(). The variable of $v is not reset before running the second foreach() cycle and since the same variable name $v is used, the value of the last item of $array1 is overwritten with the last item of $array2. Actually, it is overwritten with each item of $array2, at each iteration of the cycle, and remains overwritten with the last item after the cycle has ended.

This is a rather likely to happen situation, if you use the passing by reference capability of foreach() and don’t know about the remaining reference to the last array item in the iterator variable after the cycle has finished.

The solution

Either one of:

  • don’t use the passing by reference capability of foreach, and instead use foreach ($array as $key => $value), and use $array[$key] to modify variables. (my prefference)
  • unset the iterator variable after the foreach, by using unset($value).

I prefer the first approach, because it is somewhat clearer. You shouldn’t have to clean up after bad code, if you can simply avoid bad code :)

This side effect of foreach is actually mentioned in PHP documentation. One more reason to read the documentation before doing something :)

1 Star2 Stars3 Stars4 Stars5 Stars (2 votes, average: 3.00 out of 5)
Loading...

One response to “The danger of: foreach ($array as &$v)”

  1. Tabitha

    Hi blogger, i found this post on 25 spot in google’s search results.
    You should reduce your bounce rate in order to rank in google.
    This is major ranking factor nowadays. There is
    very handy wordpress plugin which can help you. Just search in google for:
    Lilas’s Bounce Plugin

Leave a Reply