Why does foreach copy the array when we did not modify it in the loop? [duplicate]
Asked Answered
L

2

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?

Luminary answered 11/8, 2013 at 14:51 Comment(9)
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.Ambulant
@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?Luminary
@Luminary php.net/manual/en/language.references.pass.phpFlo
@PeeHaa, that explains the first snippet, but not the second one.Luminary
Actually it is answered in @nikic's answer on SO and also see the comments (the first two).Richy
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.Annulation
@Akam, $array is not hard copied by the test() function, only the refcount increases aka soft copied.Luminary
@PeeHaa, Actually it isn't answered. Hence the question.Luminary
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.Taffeta
C
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

Clarion answered 11/8, 2013 at 15:13 Comment(1)
A hard copy of $array was not made inside the function 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.Luminary
B
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.

Ballerina answered 11/8, 2013 at 15:0 Comment(3)
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.Luminary
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!Ballerina
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.Luminary

© 2022 - 2024 — McMap. All rights reserved.