Modifying an array during a foreach loop

  • This question is always on my mind. I usually alter the array while looping through its keys, based on my gut feelings:



    foreach (array_keys($array) as $key) {
    $array[$key] = perform_changes_on($array[$key]);
    }


    I'm aware of other methods, though. The array item can be passed by reference on the foreach call:



    foreach ($array as &$item) {
    $item = perform_changes_on($item);
    }


    Finally, the array can be modified directly during the loop:



    foreach ($array as $key => $value) {
    $array[$key] = perform_changes_on($value);
    }


    What are the performance and security implications of each approach? Is there a recommended approach?



    UPDATE: What I'm actually worried about is that the two last approaches modify the array while I'm looping it.


    OP gave actual code and yet it's closed because the code is not part of a "concrete implementation"? honestly, what ?

  • chx

    chx Correct answer

    9 years ago

    If you are worried about modifying the array while looping, don't because foreach copies the array. Try



    $array = array('foo');
    foreach ($array as $k => &$item) {
    $array[] = 'bar';
    }
    var_dump($array);


    And see it terminates just fine. foreach ($array as $k => &$v) is a shorthand for foreach (array_keys($array) as $k) $v = &$array[$k] so while there still is a copy of the array (that's why I used &$item in my example so you can see, if you modify the array then it'll be modified in the reference!



    $array = array('foo', 'bar');
    foreach ($array as $k => $item) {
    echo "$item\n";
    if (!$k) {
    $array[1] = 'baz';
    }
    }
    $array = array('foo', 'bar');
    foreach ($array as $k => &$item) {
    echo "$item\n";
    if (!$k) {
    $array[1] = 'baz';
    }
    }


    the first dump foo and bar, the second foo and baz.


    "foreach ($array as $k => $v) is a shorthand for foreach (array_keys($array) as $k) $v = &$array[$k]" On a very picky note, that's not a shorthand as much as a difference in calling a function and a language construct (though for practical reasons, I suppose it's a shorthand). Also, it would be shorthand for a copy, not a reference. (Also, PHP is copy-on-write, so you can typically ignore extra copies.)

    Obviously that wanted to be `foreach ($array as $k => &$v)` edited and fixed.

    Would like to point out that `if(!$k)` is TRUE for 0, otherwise this statement is always false. So essentially you are overwriting the second array element before ever seeing it.

    I do that and by doing that, I am demonstrating that the first foreach works from a copy and the second doesn't.

    @chx You might want to run your first snippet then. $item is a copy of a value from $array. `$array` is never copied (though all elements of `$array` will be copied if the foreach loop completes). The first snippet will dump foo and baz just like the second one.

    (Note that in your answer you've used dump to mean the outputting during the loop whereas I've used it to mean outputting the entire array after the loop. Basically as in: http://paste.ee/p/hzqGz -- in short, I'm responding to your comment under the assumption that it's along the lines of what you posted in your answer: "foreach copies the array.")

    Funny. I ran that snippet. I get foo, bar, foo, baz.

    @chx, Isn't this just an implementation detail that might be changed in the future? The documentation (specification) doesn't state that `foreach` must perform as such.

    @Corbin: I suppose @chx means that all `$item`s for every key-value-pair will be copied at instantiation, so that even though `$array[1]` will be altered to `baz` in the first loop, the second loop will still print `bar` as the value of `$item` even though in the actual array it will have already been updated.

    Although the first snippet works on 5.x the way Chx describes (copying the array, iterating over the copy). As of PHP 7.0 the array is no longer copied. The snippet will instead iterate until the memory limit is reached. A good StackOverflow answer that goes into detail on this can be found here: https://stackoverflow.com/questions/10057671/how-does-php-foreach-actually-work/14854568#14854568

License under CC-BY-SA with attribution


Content dated before 7/24/2021 11:53 AM