인덱스 - php foreach문




PHP 'foreach'는 실제로 어떻게 작동합니까? (5)

예제 3에서는 배열을 수정하지 않습니다. 다른 모든 예제에서는 내용이나 내부 배열 포인터를 수정합니다. 이것은 대입 연산자의 의미 때문에 PHP 배열에서 중요합니다.

PHP의 배열에 대한 대입 연산자는 게으른 클론처럼 작동합니다. 배열을 포함하는 다른 변수에 하나의 변수를 할당하면 대부분의 언어와 달리 배열이 복제됩니다. 그러나 필요하지 않으면 실제 복제가 수행되지 않습니다. 즉, 변수 중 하나가 수정 된 경우에만 복제가 수행됩니다 (copy-on-write).

다음은 그 예입니다.

$a = array(1,2,3);
$b = $a;  // This is lazy cloning of $a. For the time
          // being $a and $b point to the same internal
          // data structure.

$a[] = 3; // Here $a changes, which triggers the actual
          // cloning. From now on, $a and $b are two
          // different data structures. The same would
          // happen if there were a change in $b.

테스트 케이스로 돌아 가면 foreach 가 배열에 대한 참조로 반복자를 생성한다고 쉽게 상상할 수 있습니다. 이 참조는 예제에서 변수 $b 와 똑같이 작동합니다. 그러나 반복자는 참조와 함께 루프 중에 만 살고 두 번 모두 무시됩니다. 이제는 모든 경우를 제외하고는 루프가 진행되는 동안 배열이 수정되고이 추가 참조는 살아 있음을 알 수 있습니다. 이것은 복제를 유발하고, 그것은 무슨 일이 일어나고 있는지를 설명합니다!

다음은이 copy-on-write 비헤이비어의 또 다른 부작용에 대한 훌륭한 기사입니다 : PHP 삼자 연산자 : 빠르거나 없습니까?

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 */

이것은 우리의 초기 결론을 뒷받침합니다. 우리는 루프 동안 소스 배열의 사본으로 작업하고 있습니다. 그렇지 않으면 루프 중에 수정 된 값을 볼 수 있습니다. 그러나...

manual 보면 다음과 같은 내용을 알 수 있습니다.

foreach가 처음 실행을 시작하면 내부 배열 포인터가 자동으로 배열의 첫 번째 요소로 재설정됩니다.

Right ... 이것은 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()each() 를 조정하는 함수를 사용하여 루프의 결과에 영향을 줄 수있는 상황이 있습니까?

foreach() 작업 할 때주의해야 할 점 :

a) foreach 는 원래 배열의 예상 복사본 에서 작동합니다. 이것은 예측 된 prospected copymanual 생성되지 않을 때까지 foreach ()가 SHARED 데이터 저장소를 가질 것임을 의미합니다.

b) 예상 복사본을 만드는 것은 무엇입니까? 예상 복사는 copy-on-write 정책에 따라 생성됩니다. 즉, foreach ()에 전달 된 배열이 변경 될 때마다 원래 배열의 복제본이 만들어집니다.

c) 원래의 배열과 foreach () 반복자는 DISTINCT SENTINEL VARIABLES 를 가질 것입니다. 즉 원래의 배열과 foreach를위한 것입니다. 아래의 테스트 코드를 참조하십시오. SPL , Iterators배열 반복자 .

question PHP에서 'foreach'루프에서 값이 재설정되었는지 확인하는 방법은 무엇입니까? 귀하의 질문에 대한 사례 (3,4,5)를 다룹니다.

다음 예제에서는 each () 및 reset ()이 foreach () 반복기의 SENTINEL 변수 (for example, the current index variable) 에 영향을주지 않음을 보여줍니다.

$array = array(1, 2, 3, 4, 5);

list($key2, $val2) = each($array);
echo "each() Original (outside): $key2 => $val2<br/>";

foreach($array as $key => $val){
    echo "foreach: $key => $val<br/>";

    list($key2,$val2) = each($array);
    echo "each() Original(inside): $key2 => $val2<br/>";

    echo "--------Iteration--------<br/>";
    if ($key == 3){
        echo "Resetting original array pointer<br/>";
        reset($array);
    }
}

list($key2, $val2) = each($array);
echo "each() Original (outside): $key2 => $val2<br/>";

산출:

each() Original (outside): 0 => 1
foreach: 0 => 1
each() Original(inside): 1 => 2
--------Iteration--------
foreach: 1 => 2
each() Original(inside): 2 => 3
--------Iteration--------
foreach: 2 => 3
each() Original(inside): 3 => 4
--------Iteration--------
foreach: 3 => 4
each() Original(inside): 4 => 5
--------Iteration--------
Resetting original array pointer
foreach: 4 => 5
each() Original(inside): 0=>1
--------Iteration--------
each() Original (outside): 1 => 2

PHP 7 참고 사항

이 답변에 대한 업데이트는 PHP 7에서 더 이상 적용되지 않습니다 : PHP 7에서 " 이전 버전과 호환되지 않는 변경 사항 "에서 설명했듯이 foreach는 배열 복사본에서 작동하므로 배열 자체의 변경 사항 foreach 루프에는 반영되지 않습니다. 링크에서 자세한 내용.

설명 ( php.net 에서 인용) :

첫 번째 형식은 array_expression에 지정된 배열을 반복합니다. 각 반복에서 현재 요소의 값이 $ value에 할당되고 내부 배열 포인터가 1만큼 앞당겨집니다 (다음 반복에서는 다음 요소를 보게됩니다).

따라서 첫 번째 예에서는 배열에 하나의 요소 만 있고 포인터가 이동하면 다음 요소가 존재하지 않으므로 새로운 요소를 추가 한 후 foreach는 이미 마지막 요소로 "결정"했기 때문에 끝납니다.

두 번째 예제에서는 두 요소로 시작하고 foreach 루프는 마지막 요소에 있지 않으므로 다음 반복에서 배열을 평가하므로 배열에 새 요소가 있음을 알게됩니다.

나는 이것이 문서의 설명의 각 반복 부분에서 모든 결과라고 믿습니다. foreach{} 의 코드를 호출하기 전에 모든 논리를 수행한다는 것을 의미합니다.

테스트 케이스

이것을 실행하면 :

<?
    $array = Array(
        'foo' => 1,
        'bar' => 2
    );
    foreach($array as $k=>&$v) {
        $array['baz']=3;
        echo $v." ";
    }
    print_r($array);
?>

이 결과물을 얻을 수 있습니다 :

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

즉, 수정 사항을 수락하고 변경 사항을 적용했기 때문입니다. 그러나 이렇게하면 :

<?
    $array = Array(
        'foo' => 1,
        'bar' => 2
    );
    foreach($array as $k=>&$v) {
        if ($k=='bar') {
            $array['baz']=3;
        }
        echo $v." ";
    }
    print_r($array);
?>

당신은 얻을 것이다 :

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

즉, 배열은 수정되었지만 foreach이미 배열의 마지막 요소에 있을 때 수정 했으므로 더 이상 반복하지 않기로 결정했습니다. 새 요소를 추가했지만 "너무 늦게"추가했습니다. 끝내지 않았다.

자세한 설명 은 PHP foreach가 실제로 어떻게 작동합니까? 이 문제의 배경을 설명합니다.


PHP 매뉴얼에서 제공하는 문서에 따라.

각 반복에서 현재 요소의 값이 $ v에 할당되고 내부
배열 포인터가 1 씩 앞당겨집니다 (다음 반복에서는 다음 요소를 보게됩니다).

첫 번째 예제에 따라

$array = ['foo'=>1];
foreach($array as $k=>&$v)
{
   $array['bar']=2;
   echo($v);
}

$arrayforeach 실행 당 하나의 요소 만 가지므로 1을 할당하고 $v포인터를 이동시키는 다른 요소가 없습니다.

그러나 두 번째 예 :

$array = ['foo'=>1, 'bar'=>2];
foreach($array as $k=>&$v)
{
   $array['baz']=3;
   echo($v);
}

$array두 요소가 있으므로 $ array는 0 인덱스를 평가하고 포인터를 1만큼 이동합니다. 루프의 첫 번째 반복의 경우 $array['baz']=3;참조로 전달됩니다.


PHP의 foreach 루프를 사용할 수 있습니다 Indexed arrays, Associative arrays하고 Object public variables.

foreach 루프에서 PHP가하는 첫 번째 작업은 반복되는 배열의 복사본을 만드는 것입니다. 그런 다음 PHP copy는 원래의 배열보다는 이 새로운 배열 을 반복 합니다. 아래 예제에서이를 증명할 수 있습니다.

<?php
$numbers = [1,2,3,4,5,6,7,8,9]; # initial values for our array
echo '<pre>', print_r($numbers, true), '</pre>', '<hr />';
foreach($numbers as $index => $number){
    $numbers[$index] = $number + 1; # this is making changes to the origial array
    echo 'Inside of the array = ', $index, ': ', $number, '<br />'; # showing data from the copied array
}
echo '<hr />', '<pre>', print_r($numbers, true), '</pre>'; # shows the original values (also includes the newly added values).

이 외에도, PHP는 iterated values as a reference to the original array value뿐만 아니라 사용할 수 있습니다. 아래에 설명되어 있습니다.

<?php
$numbers = [1,2,3,4,5,6,7,8,9];
echo '<pre>', print_r($numbers, true), '</pre>';
foreach($numbers as $index => &$number){
    ++$number; # we are incrementing the original value
    echo 'Inside of the array = ', $index, ': ', $number, '<br />'; # this is showing the original value
}
echo '<hr />';
echo '<pre>', print_r($numbers, true), '</pre>'; # we are again showing the original value

주 : 그것은 original array indexes으로 사용하는 것을 허용하지 않습니다 references.

출처 : http://dwellupper.io/post/47/understanding-php-foreach-loop-with-examples





php-internals