让我在前面说我知道foreach
是什么,做什么以及如何使用它。这个问题涉及它在引擎盖下是如何工作的,我不希望任何类似“这就是你如何用foreach
循环数组”的答案。
很长一段时间以来,我都认为foreach
适用于数组本身。然后我发现许多引用提到它适用于数组的复制这一事实,从那以后我就认为这是故事的结束。但我最近开始讨论这个问题,经过一些实验发现这实际上并不是100%正确的。
让我展示我的意思。对于以下测试用例,我们将使用以下数组:
$array = array(1, 2, 3, 4, 5);
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 */
这清楚地表明我们没有直接使用源数组——否则循环将永远继续,因为我们在循环期间不断地将项目推送到数组上。但为了确保情况是这样的:
foreach ($array as $key => $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 */
这支持了我们最初的结论,我们正在循环过程中使用源数组的副本,否则我们会在循环过程中看到修改后的值。可是…
如果我们查看手册,我们会发现以下语句:
当Foreach第一次开始执行时,内部数组指针会自动重置为数组的第一个元素。
对……这似乎表明foreach
依赖于源数组的数组指针。但我们刚刚证明了我们是不使用源数组,对吧?好吧,不完全是。
// Move the array pointer on one to make sure it doesn't affect the loopvar_dump(each($array));
foreach ($array as $item) {echo "$item\n";}
var_dump(each($array));
/* Outputarray(4) {[1]=>int(1)["value"]=>int(1)[0]=>int(0)["key"]=>int(0)}12345bool(false)*/
所以,尽管我们不直接使用源数组,但我们直接使用源数组指针-指针位于循环结束时数组末尾的事实表明了这一点。除非这不可能是真的-如果是,那么测试用例1将永远循环。
PHP手册还指出:
由于Foreach依赖于内部数组指针,因此在循环中更改它可能会导致意外行为。
好吧,让我们找出“意外行为”是什么(从技术上讲,任何行为都是意外的,因为我不再知道会发生什么)。
foreach ($array as $key => $item) {echo "$item\n";each($array);}
/* Output: 1 2 3 4 5 */
foreach ($array as $key => $item) {echo "$item\n";reset($array);}
/* Output: 1 2 3 4 5 */
没有什么出乎意料的,事实上,它似乎支持“源复制”理论。
该问题
这是怎么回事?我的C-fu不够好,无法通过查看PHP源代码来提取正确的结论,如果有人能帮我翻译成英文,我将不胜感激。
在我看来,foreach
适用于数组的复制,但将源数组的数组指针设置为循环后数组的末尾。
foreach
期间使用调整数组指针(each()
、reset()
等)的函数是否会影响循环的结果?