복붙노트

참조에 의한 PHP Foreach Pass : 마지막 요소 복제? (곤충?)

PHP

참조에 의한 PHP Foreach Pass : 마지막 요소 복제? (곤충?)

방금 작성한 간단한 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
)

이것이 버그일까요, 아니면 정말로 이상한 행동일까요?

해결법

  1. ==============================

    1.첫 번째 foreach 루프 후에 $ item은 여전히 ​​$ arr [2]에서 사용중인 일부 값에 대한 참조입니다. 따라서 참조로 호출하지 않는 두 번째 루프의 각 foreach 호출은 해당 값과 따라서 $ arr [2]를 새 값으로 바꿉니다.

    첫 번째 foreach 루프 후에 $ item은 여전히 ​​$ arr [2]에서 사용중인 일부 값에 대한 참조입니다. 따라서 참조로 호출하지 않는 두 번째 루프의 각 foreach 호출은 해당 값과 따라서 $ arr [2]를 새 값으로 바꿉니다.

    그래서 루프 1, 값과 $ arr [2]는 $ arr [0]이되고 이것은 'foo'입니다. 루프 2에서 값과 $ arr [2]는 $ arr [1]이되며 이는 'bar'입니다. 루프 3, 값과 $ arr [2]는 $ arr [2]가되고, 이는 'bar'입니다 (루프 2 때문에).

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

    루프를 반복 할 때마다 $ item의 값을 echo하고 배열 $ 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]도 같은 위치를 가리키고 있기 때문에 동일한 값으로 $ arr [3]을 업데이트합니다. 루프가 배열의 세 번째 값에 도달하면 루프의 이전 반복에 의해 설정 되었기 때문에 값 막대가 포함됩니다.

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

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

    foreach 루프는 본질적으로 참조 된 항목을 무시할 수있는 특별한 것이 아닙니다. 루프 밖에서처럼 변수를 새로운 값으로 설정할뿐입니다.

  2. ==============================

    2.$ item은 $ arr [2]에 대한 참조이며 animuson이 지적한대로 두 번째 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?
    
  3. ==============================

    3.이것이 공식적으로 버그는 아니지만 내 의견으로는 그렇습니다. 여기서 문제는 다른 프로그래밍 언어 에서처럼 루프가 종료 될 때 $ item이 범위를 벗어날 것으로 예상한다는 것입니다. 그러나 그것은 그럴 듯하지 않습니다 ...

    이것이 공식적으로 버그는 아니지만 내 의견으로는 그렇습니다. 여기서 문제는 다른 프로그래밍 언어 에서처럼 루프가 종료 될 때 $ item이 범위를 벗어날 것으로 예상한다는 것입니다. 그러나 그것은 그럴 듯하지 않습니다 ...

    이 코드는 ...

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

    출력을 제공합니다 ...

    one
    two
    three
    three
    

    다른 사람들이 이미 말했듯이, $ arr [2]의 참조 된 변수를 두 번째 루프로 덮어 쓰지 만 $ item이 범위를 벗어나지 않기 때문에 상황이 발생합니다. 너희들은 뭐라고 생각하니? 벌레?

  4. ==============================

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

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

  5. ==============================

    5.PHP의 올바른 동작은 내 의견에서 유의미한 오류 일 수 있습니다. foreach 루프에서 생성 된 참조 된 변수가 루프 외부에서 사용되면 통지가 발생해야합니다. 이 행동에 매우 쉽게 빠지며, 일어날 때 그것을 발견하기가 매우 어렵습니다. 그리고 개발자는 foreach 문서 페이지를 읽지 않을 것이며, 도움이되지 않습니다.

    PHP의 올바른 동작은 내 의견에서 유의미한 오류 일 수 있습니다. foreach 루프에서 생성 된 참조 된 변수가 루프 외부에서 사용되면 통지가 발생해야합니다. 이 행동에 매우 쉽게 빠지며, 일어날 때 그것을 발견하기가 매우 어렵습니다. 그리고 개발자는 foreach 문서 페이지를 읽지 않을 것이며, 도움이되지 않습니다.

    이런 종류의 문제를 피하기 위해 루프 뒤의 참조를 해제해야한다. 참조에 대한 unset ()은 원래 데이터를 손상시키지 않고 참조를 제거합니다.

  6. ==============================

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

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

    foreach ($arr as &$item) { ... }
    
    foreach ($arr as $anotherItem) { ... }
    
  7. from https://stackoverflow.com/questions/8220399/php-foreach-pass-by-reference-last-element-duplicating-bug by cc-by-sa and MIT license