foreach behaviour php5 vs php7

PHP 7 is considered to be significantly faster than PHP5. Several improvements have been added which need to be made in the snippets using the latter version as it tends to silently fail due to the latest code behaviour.

These changes have been outlined in the manual and this article highlights the foreach() behaviour in both versions and workaround to handle such situations. To understand the same, consider the snippet below which passes an empty array to a function and tends to output two values from an array removing a single value in between.

$test = array();
self::testForeach($test);
.
.
.
// $array is passed by reference
public static function testForeach(&$test) {
  $array = array('a', 'b', 'c');
  foreach ($array as $key => $val) {
    //produces a, b as an output in PHP 5
    //produces a, b, c as an output in PHP 7
    echo $val;

    if ($val == 'b') {
      //remove 'c' from the array
      unset($array[2]);
    }
  }
}

Behaviour in PHP 5.5:

  • $test is passed by reference to testForeach() function.
  • Removing "c" from $test inside the loop would directly modify the original array. Hence, the iterated values would be a, b and not c as it gets removed from the array in between.

Behaviour in PHP 7:

  • $test is passed by reference to `testForeach()` function.
  • When $test is looped over foreach(), a copy is made say $testCopy(according to the doc) which is being iterated over the loop. So removing "c" value from $test would have no effect and will loop all the values contained in the $testCopy.
  • Hence the output - a, b, c.

Workaround/Fix:

There may be several ways to fix/handle this use-case. One way is to pass the iterating variable also as a reference. Eg -

foreach ($array as $key => &$val) {

Note: As $val is passed by ref, it will directly update the main array $test if any modification is made inside the loop to $val including unset(). So if your code contains something like -

$val = 'd';

it will update all values of $test to 'd'. To avoid such a case, proceed with another approach by using a temp variable to handle the looping stuff-

foreach ($array as $key => &$val) {
  $tempVal = $val;

  //produces a, b as an output in PHP 7
  echo $tempVal;

  if ($tempVal == 'b') {
   //remove 'c' from the array
    unset($array[2]);
  }
}