How does PHP foreach work?

20

To clarify, this question is not about when foreach is used or what its differences are for other repetition loops , but rather on the operation of foreach itself.

In documentation , little is said about exactly how foreach works, if not more, how to use it. Some loose phrases seem to be an attempt to detail a little better, but out of a specific context they end up not adding much.

To quote:

  

In PHP 7, foreach does not use the array's internal pointer.

That is, we can freely change the internal array pointer within the foreach loop without interfering with its execution.

Test 1: Restarting the internal array pointer at each iteration does not cause the loop to restart

$arr = range(1, 5);

foreach($arr as $item) {
    echo $item, PHP_EOL;
    reset($arr);
}

See working at Repl.it | Ideone

Even restarting the internal pointer of the array loop terminates normally.

  

To directly modify elements of an array within a loop, precede $ value with &.

Which, in principle, indicates that foreach does not create a copy of array to use during the loop, since the reference in the element would be a reference to the element in the copy rather than in the array original. But what we see in practice is that the reference points to the original element.

Test 2: Element reference points to the original array element and not to a copy of it

$arr = range(1, 5);

foreach($arr as &$item) {
    $item *= 2;
}

print_r($arr);

See working at Repl.it | Ideone

However, we can see in practice that the array used by the repeat structure is not exactly the original array , since we can freely change the array > without interfering with the loop.

Test 3: Changing the original array within foreach does not change loop behavior

$arr = range(1, 5);

foreach($arr as $item) {
    echo $item, PHP_EOL;
    $arr[] = $item;
}

See working at Repl.it | Ideone

In this example, at each iteration we add a new element at the end of the array . If foreach used the reference of $arr itself, the loop would be infinite, but what happens in practice is that only the original items are iterated.

In short:

Test 1 shows that the pointer used by foreach is not the pointer to the array ;
  • Test 2 would show that foreach does not make a copy of array , because the reference points to the original array element, not an array of copy;
  • Test 3 shows that foreach does indeed copy a array , because changes made inside the loop are not reflected in the original array / li>

    Tests 2 and 3 are mainly contradictory, since one behaves like a copy while the other does not.

    So the question is: how does foreach of PHP work?

    If the behavior of the structure is different between versions of the language and comparing them would make the response too large, it is acceptable to be answered about its operation in the most current version.

        

  • asked by anonymous 21.09.2018 / 18:31

    2 answers

    9

    I researched a little bit and I came to the following conclusion.,

      

    Test 1: Resetting the internal array pointer to every non-iteration   make the loop restart

    A - Approximate Translation:

      

    Before PHP 7, the internal array pointer was modified as a   array was being iterated with foreach. This is no longer the case ...    link   The item below complements:

    B - Approximate Translation:

      

    When used in value mode by default, foreach will now operate on a copy of the array being iterated instead of the array itself. This means that changes made to the array during iteration will not affect the values that are iterated.    link

    In other words, foreach acts on the array copy. If the "copied" array has 2 items, these two items will be iterated and if you manipulate (as in the examples above, the initial array (the copy) will not be changed). It would look something like this:

    $arrayA = ['AAA','BBB'];
    $arrayB = $arrayA;//o foreach faz algo semelhante/equivalente a isso
    //equivale a
    foreach($arrayB as $k=> $b){
    //foreach($arrayA as $k=> $b){
        $arrayA[] = 'A_B'.$b; 
    }
    print_r($arrayA);
    

    So if I go through $arrayB by changing the values of $arrayA , $arrayB will not have its content modified.

    In the example extracted from the PHP doc you have an example that when using the reset the pointer does not reset the internal pointer of the array copy.

    <?php
    $array = [0, 1, 2];
    foreach ($array as &$val) {
        var_dump(current($array));
    }
    

    link

      

    Test 2: Element reference points to array element   original and not for a copy of it

    The foreach iterates over the copy of the original array, regardless of changes in values during looping / iteration. Any changes will be made but the copy of the array will remain intact. I have no knowledge to speak of how PHP stores this original array for foreach to iterate over but in the example I quoted from $ arrayA and $ arrayB (pretty rough) would be (in my view) the way the foreach "manages" all of it.

    References to foreach: link

    *** Up. To reinforce what I defended in the post

    $arr = ['a' => 'AAA','b'=> 'BBB','c'=>'CCC'];
    foreach ($arr as $k => $v) {
        echo  "$k: $v\n";
        $arr['c']= 'DDDDDD'; 
        print_r($arr);
       //Se o foreach não usasse a cópia do array para fazer a iteração,a 
       // chave c teria esse valor na última iteração($k = 'c')
    }
    print_r($arr);
    

    I look forward to constructive discussions.

        
    25.09.2018 / 19:40
    0

    The way for you to "surround the chicken" is to call the actual address of each iterated value & item, not your $ item copy. And this does not happen by looking at the array itself, but rather at each iterated item.

    <pre>
    <?php
    $arr = range(1, 5);
    
    // Repare que agora com a referência do endereço de fato, o array é alterado.
    echo "O primeiro caso NÃO ALTERA o array.".PHP_EOL;
    foreach($arr as $item) {
        var_dump($item=5);
        print_r($arr);
        echo $item, PHP_EOL;
        reset($arr);
    }
    
    // Repare que agora com a referência do endereço de fato, o array é alterado.
    echo "O segundo caso ALTERA o array.".PHP_EOL;
    foreach($arr as &$item) {
        var_dump($item=5);
        print_r($arr);
        echo $item, PHP_EOL;
        reset($arr);
    }
    ?>
    </pre>
    

    To complement, in fact, yes, it is a copy that it does, but not of the array, but of each iterated value.

        
    02.11.2018 / 01:26