Note that there are some explanatory texts on larger screens.

plurals
  1. POHow does PHP 'foreach' actually work?
    text
    copied!<p>Let me prefix this by saying that I know what <code>foreach</code> is, does and how to use it. This question concerns how it works under the bonnet, and I don't want any answers along the lines of "this is how you loop an array with <code>foreach</code>".</p> <hr> <p>For a long time I assumed that <code>foreach</code> worked with the array itself. Then I found many references to the fact that it works with a <em>copy</em> of the array, and I have since assumed this to be the end of the story. But I recently got into a discussion on the matter, and after a little experimentation found that this was not in fact 100% true.</p> <p>Let me show what I mean. For the following test cases, we will be working with the following array:</p> <pre><code>$array = array(1, 2, 3, 4, 5); </code></pre> <p><a href="http://codepad.org/7DIeObk9" rel="noreferrer">Test case 1</a>:</p> <pre><code>foreach ($array as $item) { echo "$item\n"; $array[] = $item; } print_r($array); /* Output in loop: 1 2 3 4 5 $array after loop: 1 2 3 4 5 1 2 3 4 5 */ </code></pre> <p>This clearly shows that we are not working directly with the source array - otherwise the loop would continue forever, since we are constantly pushing items onto the array during the loop. But just to be sure this is the case:</p> <p><a href="http://codepad.org/nirz6Ufh" rel="noreferrer">Test case 2</a>:</p> <pre><code>foreach ($array as $key =&gt; $item) { $array[$key + 1] = $item + 2; echo "$item\n"; } print_r($array); /* Output in loop: 1 2 3 4 5 $array after loop: 1 3 4 5 6 7 */ </code></pre> <p>This backs up our initial conclusion, we are working with a copy of the source array during the loop, otherwise we would see the modified values during the loop. <em>But...</em></p> <p>If we look in the <a href="http://php.net/manual/en/control-structures.foreach.php" rel="noreferrer">manual</a>, we find this statement:</p> <blockquote> <p>When foreach first starts executing, the internal array pointer is automatically reset to the first element of the array.</p> </blockquote> <p>Right... this seems to suggest that <code>foreach</code> relies on the array pointer of the source array. But we've just proved that we're <em>not working with the source array</em>, right? Well, not entirely.</p> <p><a href="http://codepad.org/6n20ooMy" rel="noreferrer">Test case 3</a>:</p> <pre><code>// Move the array pointer on one to make sure it doesn't affect the loop var_dump(each($array)); foreach ($array as $item) { echo "$item\n"; } var_dump(each($array)); /* Output array(4) { [1]=&gt; int(1) ["value"]=&gt; int(1) [0]=&gt; int(0) ["key"]=&gt; int(0) } 1 2 3 4 5 bool(false) */ </code></pre> <p>So, despite the fact that we are not working directly with the source array, we are working directly with the source array pointer - the fact that the pointer is at the end of the array at the end of the loop shows this. Except this can't be true - if it was, then <a href="http://codepad.org/7DIeObk9" rel="noreferrer">test case 1</a> would loop forever.</p> <p>The PHP manual also states:</p> <blockquote> <p>As foreach relies on the internal array pointer changing it within the loop may lead to unexpected behavior.</p> </blockquote> <p>Well, let's find out what that "unexpected behavior" is (technically, any behavior is unexpected since I no longer know what to expect).</p> <p><a href="http://codepad.org/JJp79xSd" rel="noreferrer">Test case 4</a>:</p> <pre><code>foreach ($array as $key =&gt; $item) { echo "$item\n"; each($array); } /* Output: 1 2 3 4 5 */ </code></pre> <p><a href="http://codepad.org/IfwJyTCL" rel="noreferrer">Test case 5</a>:</p> <pre><code>foreach ($array as $key =&gt; $item) { echo "$item\n"; reset($array); } /* Output: 1 2 3 4 5 */ </code></pre> <p>...nothing that unexpected there, in fact it seems to support the "copy of source" theory.</p> <hr> <p><strong>The Question</strong></p> <p>What is going on here? My C-fu is not good enough for me to able to extract a proper conclusion simply by looking at the PHP source code, I would appreciate it if someone could translate it into English for me.</p> <p>It seems to me that <code>foreach</code> works with a <em>copy</em> of the array, but sets the array pointer of the source array to the end of the array after the loop.</p> <ul> <li>Is this correct and the whole story?</li> <li>If not, what is it really doing?</li> <li>Is there any situation where using functions that adjust the array pointer (<code>each()</code>, <code>reset()</code> et al.) during a <code>foreach</code> could affect the outcome of the loop?</li> </ul>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload