让我先说一下,我知道foreach是什么、做什么以及如何使用它。这个问题涉及到它在引擎盖下的工作方式,我不希望有任何类似于“这是如何使用foreach循环数组”的答案。
在很长一段时间里,我假设foreach使用数组本身。然后,我发现了许多关于它与数组副本一起工作的事实的引用,因此我认为这就是故事的结尾。但我最近就此事进行了一次讨论,经过一点实验后发现,事实上这并非100%正确。
让我表明我的意思。对于以下测试用例,我们将使用以下阵列:
$array = array(1, 2, 3, 4, 5);
测试用例1:
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 */
这清楚地表明,我们没有直接使用源数组,否则循环将永远继续,因为我们在循环期间不断将项推到数组上。但为了确保情况是这样的:
测试用例2:
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依赖于源数组的数组指针。但我们已经证明我们没有使用源阵列,对吗?嗯,不完全是。
测试用例3:
// 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]=>
int(1)
["value"]=>
int(1)
[0]=>
int(0)
["key"]=>
int(0)
}
1
2
3
4
5
bool(false)
*/
因此,尽管我们不是直接使用源数组,但我们是直接使用源阵列指针-指针位于循环末尾的数组末尾这一事实表明了这一点。但这不可能是真的——如果是,那么测试用例1将永远循环。
PHP手册还指出:
由于foreach依赖于内部数组指针,因此在循环中更改它可能会导致意外行为。
好吧,让我们来看看“意外行为”是什么(从技术上讲,任何行为都是意外的,因为我不再知道会发生什么)。
测试用例4:
foreach ($array as $key => $item) {
echo "$item\n";
each($array);
}
/* Output: 1 2 3 4 5 */
测试用例5:
foreach ($array as $key => $item) {
echo "$item\n";
reset($array);
}
/* Output: 1 2 3 4 5 */
……没有什么出乎意料的,事实上,它似乎支持“复制源”理论。
问题
这是怎么回事?我的C-fu不够好,我无法通过查看PHP源代码得出正确的结论,如果有人能为我将其翻译成英语,我将不胜感激。
在我看来,foreach使用数组的副本,但在循环后将源数组的数组指针设置到数组的末尾。
这是正确的吗?如果没有,它到底在做什么?在foreach期间使用调整数组指针的函数(each()、reset()等)是否会影响循环的结果?