Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handle foreach by-ref in PHP 7

So far we've been using PHP 5.5 and everything seemed to be flowing too good with the code. Since upgrading it to 7 most of the foreach() seems to be having inconsistent behaviour.

For eg: Consider the snippet below:

$array = array('a', 'b', 'c');
self::testForeach($array);
.
.
.
// $array is passed by reference
public static function testForeach(&$array) {

  foreach ($array as $key => $val) {
    //produces a, b as an output in PHP 5
    //produces a, b, c as an output in PHP 7
    var_dump($val);

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

Behaviour in PHP 5.5:

$array is passed by reference to testForeach() function. So removing "c" from $array 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:

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

Changing foreach to pass-by-ref is not a soln for me as there are too many foreach on my project and I can't grep and modify each of them.

Is there any other handling done for such behaviour on the latest version. Any tool/parser which could highlight them?

Any hints/ideas ?

Thanks!

like image 264
jitendrapurohit Avatar asked Sep 16 '16 11:09

jitendrapurohit


2 Answers

Only if you loop the array by reference: foreach ( $array as $key => &$val ) {

Afaik then NO copy is being made. http://php.net/manual/en/control-structures.foreach.php

BE WARNED: in this case the $val remains as a pointer to the last element of the array, a best practice is to unset it.

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


<?php

$array = array();
testForeach( $array );

// $array is passed by reference
function testForeach( &$array )
{
    $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
        var_dump( $val );

        if ( $val == 'b' ) {
            //remove 'c' from the array
            unset( $array[ 2 ] );
        }
    }
    unset($val);
}
like image 112
klodoma Avatar answered Oct 23 '22 08:10

klodoma


Beside it seems odd to modify the array while iterating, in PHP 7 you can force a reference in the iterate variable:

$array = array('a', 'b', 'c');
foreach ($array as $key => &$val) {
  var_dump($val);
  if ($val == 'b') {
    //remove 'c' from the array
    unset($array[2]);
  }
}

Will return on both PHP 5 and 7:

string(1) "a"
string(1) "b"

See in action on 3v4l.org.

like image 3
gmsantos Avatar answered Oct 23 '22 07:10

gmsantos