Mar 8, 2015

PHP reference in foreach loop -- gotcha!

When I wrote PHP code similar to the following, I thought it was innocuous, but I was wrong.

  $arr = ['A', 'B', 'C', 'D'];

  foreach ($arr as $index => &$value) {
    $value = strtolower($value);
  }


  foreach ($arr as $index => $value) {
    . . .

  }

Besides lowercasing each string as intended, the last element of the array was overwritten by the next to last element, i.e. the array ended up with the values ['a', 'b', 'c', 'c'].

This happens consistently with any array with two or more elements. The last element is overwritten with the value preceding it.

Although I don't fully understand what's going on, I know that it has something to do with the use of the reference in the first foreach loop. After the first loop finishes executing, the variable $value continues to be a reference, and that leads to the unexpected behavior.

I have omitted the body of the second loop because it is irrelevant to the issue at hand.

There are at least three ways to fix this problem.

1)  In the second loop, use a different variable.

  foreach ($arr as $index => $item) {
    . . .

  }
 
2)  Unset the variable before using it further.

  unset($value);

3)  Use the variable as a reference also in the second loop, rather than a non-reference.

  foreach ($arr as $index => &$value) {
    . . .

  }

I don't find any of these fixes ideal, but all of them do eliminate the problem.

Source:

What References Do
http://php.net/manual/en/language.references.whatdo.php