[php] 참조에 의한 PHP Foreach 패스 : 마지막 요소 복제? (곤충?)

방금 작성한 간단한 PHP 스크립트로 매우 이상한 행동을했습니다. 버그를 재현하는 데 필요한 최소한으로 줄였습니다.

<?php

$arr = array("foo",
             "bar",
             "baz");

foreach ($arr as &$item) { /* do nothing by reference */ }
print_r($arr);

foreach ($arr as $item) { /* do nothing by value */ }
print_r($arr); // $arr has changed....why?

?>

결과는 다음과 같습니다.

Array
(
    [0] => foo
    [1] => bar
    [2] => baz
)
Array
(
    [0] => foo
    [1] => bar
    [2] => bar
)

이것이 발생해야하는 버그 또는 정말 이상한 행동입니까?



답변

첫 번째 foreach 루프 $item이후에도에서 사용되는 일부 값에 대한 참조 $arr[2]입니다. 따라서 참조로 호출하지 않는 두 번째 루프의 각 foreach 호출은 해당 값을 $arr[2]새 값으로 대체합니다 .

따라서 루프 1, 값 및 $arr[2] 이 될 $arr[0]‘갑’이다.
루프 2, 가치와 $arr[2]이 될 $arr[1]‘바’입니다.
루프 3의 값 $arr[2]이 될 $arr[2]‘바'(인해 루프 2)이다.

값 ‘baz’는 실제로 두 번째 foreach 루프의 첫 번째 호출에서 손실됩니다.

출력 디버깅

루프가 반복 될 때마다의 값을 에코 $item하고 배열을 재귀 적으로 인쇄합니다 $arr.

첫 번째 루프가 실행되면 다음 출력이 표시됩니다.

foo
Array ( [0] => foo [1] => bar [2] => baz )

bar
Array ( [0] => foo [1] => bar [2] => baz )

baz
Array ( [0] => foo [1] => bar [2] => baz )

루프의 끝 $item에서은 여전히와 같은 위치를 가리 킵니다 $arr[2].

두 번째 루프가 실행되면 다음 출력이 표시됩니다.

foo
Array ( [0] => foo [1] => bar [2] => foo )

bar
Array ( [0] => foo [1] => bar [2] => bar )

bar
Array ( [0] => foo [1] => bar [2] => bar )

배열이 새로운 값을 입력 할 때마다 $item$arr[3] 동일한 위치를 가리키고 있기 때문에 동일한 값으로 업데이트 되는 방법을 알 수 있습니다. 루프가 배열의 세 번째 값에 도달하면 값이 포함됩니다.bar 의 이전 반복에 의해 방금 설정되었으므로 .

버그입니까?

아니요. 이것은 참조 된 항목의 동작이며 버그가 아닙니다. 다음과 같은 것을 실행하는 것과 유사합니다.

for ($i = 0; $i < count($arr); $i++) { $item = $arr[$i]; }

foreach 루프는 참조 된 항목을 무시할 수있는 특성상 특별하지 않습니다. 루프 외부 에서처럼 매번 해당 변수를 새 값으로 설정하는 것입니다.


답변

$item$arr[2]animuson이 지적한 것처럼 두 번째 foreach 루프에 대한 참조 이며 덮어 쓰입니다.

foreach ($arr as &$item) { /* do nothing by reference */ }
print_r($arr);

unset($item); // This will fix the issue.

foreach ($arr as $item) { /* do nothing by value */ }
print_r($arr); // $arr has changed....why?


답변

공식적으로는 버그가 아닐 수 있지만 제 생각에는 그렇습니다. 여기서 문제는 우리가$item 는 다른 많은 프로그래밍 언어 에서처럼 루프가 종료 될 때 범위를 벗어날 . 그러나 그것은 사실이 아닌 것 같습니다 …

이 코드는 …

$arr = array('one', 'two', 'three');
foreach($arr as $item){
    echo "$item\n";
}
echo $item;

출력을 제공합니다 …

one
two
three
three

다른 사람들이 이미 말했듯이 참조 된 변수를 $arr[2]두 번째 루프로 덮어 쓰지만 $item범위를 벗어나지 않았기 때문에 발생 합니다. 어떻게 생각 하시나요? 버그?


답변

더 쉬운 설명은 PHP의 최초 개발자 인 Rasmus Lerdorf의 것 같습니다 : https://bugs.php.net/bug.php?id=71454


답변

PHP sould의 올바른 동작은 내 의견에 NOTICE 오류입니다. foreach 루프에서 생성 된 참조 변수가 루프 외부에서 사용되면 알림이 발생합니다. 이 행동에 빠지기 매우 쉽고, 그것이 일어 났을 때 발견하기가 매우 어렵습니다. 그리고 개발자는 foreach 문서 페이지를 읽지 않을 것입니다. 그것은 도움이 아닙니다.

unset()이러한 종류의 문제를 방지하려면 루프 이후에 참조 해야 합니다. 참조에 대한 unset ()은 원본 데이터를 손상시키지 않고 참조를 제거합니다.


답변

ref 지시문 (&)을 사용하기 때문입니다. 마지막 값은 두 번째 루프로 대체되고 배열이 손상됩니다. 가장 간단한 해결책은 두 번째 루프에 다른 이름을 사용하는 것입니다.

foreach ($arr as &$item) { ... }

foreach ($arr as $anotherItem) { ... }


답변