How does PHP foreach work?

Asked

Viewed 905 times

23

To clarify, this question is not about when the foreach is used or differs from it to other loops of repetition, but about the functioning of foreach in itself.


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

Quoting:

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

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

Test 1: Restarting the internal pointer of the array every iteration does not restart the loop

$arr = range(1, 5);

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

See working on Repl.it | Ideone

Even if you restart the internal pointer array the loop is normally finished.

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

Which, at first, indicates that the foreach does not create a copy of array to be used during the loop, as the reference in the element would be a reference to the item in the copy and no longer 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 element array original and not for a copy of it

$arr = range(1, 5);

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

print_r($arr);

See working on Repl.it | Ideone

However, we can see in practice that the array used by the repeating structure is not exactly the array as we can freely alter the array without interfering with the bond.

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

$arr = range(1, 5);

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

See working on Repl.it | Ideone

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

In short:

  1. Test 1 shows that the pointer used by foreach is not the pointer of array;
  2. Test 2 would show that the foreach does not make a copy of the array, because the reference points to the element of array original, not a array copying;
  3. Test 3 shows that the foreach makes, in fact, a copy of the array, as changes made within the loop are not reflected in the array original;

Tests 2 and 3, mainly, are at first contradictory, because one behaves as a copy, while the other does not.

So the question is, how does foreach of PHP works?

If the behavior of the structure differs between versions of the language and compares them would make the answer too large, it is accepted that it be answered about its functioning in the most current version.

  • 1

    This question was asked on the basis of a self-doubt, aided by a chat discussion and inspired by the question How does PHP 'foreach' Actually work?

  • 1

    The accepted answer from the English forum, from the link in your comment, is not good enough for you?

  • 1

    @Leonardoalvesmachado the idea is precisely to bring this type of content (or better, even) to our site, in Portuguese.

  • 2

    Mmm, I get it... It would really enrich the site...

  • 3

    @Leonardoalvesmachado, moreover, many will not understand Soen’s reply because it is in English. Anderson’s initiative is good and I look forward to a response in Portuguese! :)

2 answers

10

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

Test 1: Restarting the array’s internal pointer with each iteration not causes the loop to restart

A - Approximate translation:

Prior to PHP 7, the array’s internal pointer was modified as a array was being iterated with foreach . This is no longer the case... https://php.net/manual/en/migration70.incompatible.php#migration70.incompatible.foreach.array-Pointer The item below complements:

B - Approximate translation:

When used in default value mode, 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 values that are iterated. https://php.net/manual/en/migration70.incompatible.php#migration70.incompatible.foreach.by-value

That is, the foreach acts on the copy of the array. If the "copied" array has 2 items, these two items will be iterated and if you handle (as in the examples cited, the initial array (the copy) will not be changed). It would be something similar to that:

$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);

Soon if I walk $arrayB by changing the values of $arrayA , $arrayB will not have its content modified.

In the example extracted from the PHP doc there is an example that when using 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));
}

https://ideone.com/gencKc

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

The foreach iterates over the copy of the original array, independent 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 talk about how PHP stores this original matrix for foreach to itere about but in the example I cited of $arrayA and $arrayB (pretty crude) it would be (to my point of view) the way that foreach "manage" all this.

References about the foreach: https://php.net/manual/en/migration70.incompatible.php#migration70.incompatible.foreach

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

  • Greetings, Marcos, sorry for the delay in feedback, I was hoping to see if there would be other responses and analyze them all together. If I understand correctly, much of what you put in your answer was already explicit in the question itself, only in other words. The question is, in fact, whether PHP operates on a copy of array, how the reference can act on the array original and not on the copy? And when it is by reference, why even so the internal pointer of the array is not modified if, theoretically, I am iterating the object itself? How PHP manages all this?

  • 1

    Perfect. Maybe I got lost in the hehe explanation. I’m studying a little bit more about to see if I can be more technical and really contribute (not just discuss). It’s just time for me

0

The way for you to "surround the chicken" is to call the actual address of each iterated value &$item, not your copy $item. And this is not by looking at the array itself, but for 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 he makes, but not of the array, but of each iterated value.

  • I disagree on the copy of the iterated value. See the reason https://ideone.com/RimDEK <? php $array = [0]; foreach ($array as &$val) { var_dump($val); $array[1] = 1; $array[2] = 2; }

  • That is why https://secure.php.net/manual/en/migration70.incompatible.php#migration70.incompatible.foreach.by-value

Browser other questions tagged

You are not signed in. Login or sign up in order to post.