IT story

PHP Foreach Pass by Reference : 마지막 요소 복제?

hot-time 2020. 6. 7. 10:17
반응형

PHP Foreach Pass by Reference : 마지막 요소 복제? (곤충?)


방금 작성한 간단한 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]'foo'가됩니다.
루프 2, 값 $arr[2]이되고 $arr[1]'bar'가됩니다.
루프 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의 이전 반복에 의해 설정 되었기 때문에 값이 포함됩니다 .

버그입니까?

No. This is the behavior of a referenced item, and not a bug. It would be similar to running something like:

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

A foreach loop isn't special in nature in which it can ignore referenced items. It's simply setting that variable to the new value each time like you would outside of a loop.


$item is a reference to $arr[2] and is being overwritten by the second foreach loop as animuson pointed out.

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?

While this may not officially be a bug, in my opinion it is. I think that the problem here is we have the expectation for $item to go out of scope when the loop is exited as it would in a lot of other programming languages. However that doesn't seem to be the case...

This code...

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

Gives the output...

one
two
three
three

As other people already said, you're overwriting the referenced variable in $arr[2] with your second loop, but it's only happening because $item never went out of scope. What do you guys think... bug?


An easier explanation, seems from Rasmus Lerdorf, original creator of PHP: https://bugs.php.net/bug.php?id=71454


The correct behaviour of PHP sould be a NOTICE error in my oppinion. If a referenced variable created in a foreach loop is used outside the loop it should cause a notice. Very easy to fall for this behaviour, very difficult to spot it when it happened. And no developer is going to read the foreach documentation page, it's not a help.

You should unset() the reference after your loop to avoid this sort of issue. unset() on a reference will just remove the reference without harming the original data.


that's because you use by ref directive (&). last value will be replace by the second loop and it corrupt your array. the simplest solution is to use different name for second loop:

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

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

참고URL : https://stackoverflow.com/questions/8220399/php-foreach-pass-by-reference-last-element-duplicating-bug

반응형