title: How does PHP 'foreach' actually work?
body: <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>