4

Consider the following code:

$a = [1, 2, 3];

foreach ($a as $x) {
    foreach ($a as $y) {
        echo "$x $y\n";
    }
}

As one would expect, it gives the following result:

1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3

Although happy with the result, I'm surprised it works, because according to the manual, it relies on the array's internal pointer:

When foreach first starts executing, the internal array pointer is automatically reset to the first element of the array. This means that you do not need to call reset() before a foreach loop.

As foreach relies on the internal array pointer, changing it within the loop may lead to unexpected behavior.

So we could expect that using a nested foreach inside the first one, would share the same internal pointer, and produce the following result:

1 1
1 2
1 3

Because the first foreach would see the state of the internal pointer left by the nested foreach.

I can think of two possible explanations to the actual behaviour:

  • The PHP manual is wrong / outdated, and PHP uses a separate pointer specific to every foreach;
  • The nested foreach internally triggers a duplication of the array in memory, so they would each have their own pointer.

Just for my culture, how does this work internally?

Community
  • 1
  • 1
BenMorel
  • 34,448
  • 50
  • 182
  • 322
  • 1
    Related: http://stackoverflow.com/questions/10057671/how-foreach-actually-works/14854568#14854568 – NikiC Jun 01 '15 at 18:13

1 Answers1

4

Short answer. PHP indeed copies the array (that is, the array structure) in this situation. Foreach can do that in various scenarios to prevent problems like this. Some people believe that foreach always copies the array, but that is not the case (that would be inefficient).

I found a great blog post that describes this behavior in more detail:

PHP internals: When does foreach copy?

The summary:

  • foreach will copy the array structure if and only if the iterated array is not referenced and has a refcount > 1
  • foreach will additionally copy the array values if and only if the previous point applies and the iteration is done by reference
GolezTrol
  • 114,394
  • 18
  • 182
  • 210