14

In a blog post "PHP Internals: When does foreach copy", NikiC stated that in a code like this:

Snippet 1

$array = range(0, 100000);
foreach ($array as $key => $value) {
    xdebug_debug_zval('array'); // array is not copied, only refcount is increased
}

foreach will not copy the array because the only thing that foreach modifies about $array is it's internal array pointer.

He also stated that in a code like this:

Snippet 2

$array = range(0, 100000); // line 1
test($array);
function test($array) { 
    foreach ($array as $key => $value) { // line 4
        xdebug_debug_zval('array'); // array is copied, refcount not increased
        // ...
    }
}

foreach will copy the array because if it didn't, the $array variable in line 1 would be changed.

However, the only thing that foreach modifies about $array is it's internal array pointer. So why does it matter if the internal array pointer of the $array variable in line 1 is changed? It didn't matter in snippet 1, why did it matter in snippet 2?

Why does foreach need to copy the array in snippet 2, even though we did not modify it in the loop?

Charles
  • 50,943
  • 13
  • 104
  • 142
Pacerier
  • 86,231
  • 106
  • 366
  • 634
  • 1
    From what I know, that could be not much, you array is always passing as a COPY because you are not passing it as a reference. – JorgeeFG Aug 11 '13 at 14:56
  • 1
    @Jorge, the point is why does php only soft copied (increase refcount) in snippet 1, but hard copied in snippet 2? Why can't we soft copy in snippet 2 as well, since there is no modification to the array? – Pacerier Aug 11 '13 at 14:58
  • @Pacerier http://php.net/manual/en/language.references.pass.php – Alex W Aug 11 '13 at 15:00
  • 1
    @PeeHaa, that explains the first snippet, but not the second one. – Pacerier Aug 11 '13 at 15:00
  • Actually it *is* answered in @nikic's answer on SO and also see the comments (the first two). – PeeHaa Aug 11 '13 at 15:01
  • 2
    I read that blog, i think the reason is clear, because the `$array` variable is not defined in the scope of the function where the `foreach` takes place, one confusion here is that, `foreach` will not `copy` the `$array`, it's better to say that it will be copied by the `test() function` and this is not exactly right. Because while `foreach` iterates the array, it must has access to it's internal pointer to get the `key` and `value`, therefore, it must work on a copy or the original one. –  Aug 11 '13 at 15:03
  • 1
    @Akam, $array is not hard copied by the test() function, only the refcount increases aka soft copied. – Pacerier Aug 11 '13 at 15:08
  • 1
    @PeeHaa, Actually it *isn't* answered. Hence the question. – Pacerier Aug 11 '13 at 15:19
  • I think @Akam's explanation is quite nice. I'd just ignore the whole copy-on-write thing for a moment and look at it like this: In the first case you're looping over the array, so it's expected that the IAP changes. In the second case the array is copied when you pass it to the function (only the copy in the function has its IAP changed). Things are more complicated due to COW, as the array is only copied during the iteration and not when calling the function, but the idea stays the same. – NikiC Aug 12 '13 at 19:09

2 Answers2

2

That is because in the second case, $array is passed by value to the function test(). Hence, a copy of the $array was made inside the function, and the foreach() works on the copy. Things will be different if the $array is passed by reference to the function test().

For information on pass by value vs pass by reference, see this question

Community
  • 1
  • 1
Sutandiono
  • 1,748
  • 1
  • 12
  • 21
  • 1
    A hard copy of `$array` was not made inside the function http://derickrethans.nl/talks/phparch-php-variables-article.pdf. Only the refcount increases as reported by `xdebug_debug_zval`. The `foreach` does not work on the copy, because no copy was made. – Pacerier Aug 11 '13 at 15:23
1

Your question is answered in the article you linked to. It is given in the section

Not referenced, refcount > 1

with the explanation that a copy of the structures is needed because the array pointer moves, and this must not affect the outside array.

Sven
  • 69,403
  • 10
  • 107
  • 109
  • 1
    Can you show an example of how can it fail? Why does it matter if the internal pointer of the outside array changes? In the first snippet, the array pointer moves as well, and it doesn't mandate a hard copy there. – Pacerier Aug 11 '13 at 15:05
  • If you give a variable into a function, you expect the variable to be unchanged after the function returns. Changing the array pointer also is a change to the variable, and this must not happen! – Sven Aug 11 '13 at 15:07
  • 1
    you say changing the internal array pointer is also a change to the variable. Then why doesn't the first snippet do a hard copy? The first snippet also modifies the array. The state of `$array` before the `foreach` and after the `foreach` is different in the first snippet as well. – Pacerier Aug 11 '13 at 15:13