4

If the PHP program shown below is stored in bug.php, then this command

php bug.php

will produce this output when using PHP 7.0.33-0ubuntu0.16.04.1 (cli) ( NTS ) Zend Engine v3.0.0, Copyright (c) 1998-2017 Zend Technologies with Zend OPcache v7.0.33-0ubuntu0.16.04.1, Copyright (c) 1999-2017, by Zend Technologies

Output:

Initial array
Array
(
    [0] => aa
    [1] => bb
)
j = 0  line = aa
element 0 is aa

j = 1  line = bb    <----------------------------------------------
element 1 is ***bb  <----------------------------------------------

Final array
Array
(
    [0] => aa
    [1] => ***bb
)

Notice the marked lines above. Element $A[1] is "***bb", yet $line - which the foreach should associate with key 1 - is "bb".

Is this a bug, or is there some PHP subtlety I am unaware of?

<?php

    $A = array
    (
    "aa",
    "bb",
    );

    echo "Initial array\n";
    print_r($A);

    foreach ($A as $j => $line)
    {
        echo "j = $j  line = $line\n";
        echo "element $j is {$A[$j]}\n\n";

        if ($j == 0)
            $A[1] = "***" . $A[1];
    }

    echo "Final array\n";
    print_r($A);

?>
Roger House
  • 481
  • 1
  • 5
  • 12
  • 3
    You modified `$A[1]` on the first pass through the `foreach` so it now contained `***bb` which you then display the 2nd pass through the `foreach`. Seems completely correct from where I'm sitting. – Dave Mar 04 '19 at 20:34
  • 1
    What is your expected output it is not clear. – nerdlyist Mar 04 '19 at 20:44
  • 1
    @Dave I don't think you understand the question and example fully. – JBES Mar 04 '19 at 20:51

3 Answers3

1

The array is passed to the foreach as a copy - unless you pass it as a reference (indicate that by adding a & to the $value) it will not reflect the updates of the value you are doing in the first iteration.

Consider this:

foreach ($A as $j => &$line)
{
    echo "j = $j  line = $line\n";
    echo "element $j is {$A[$j]}\n\n";

    if ($j == 0)
       $A[1] = "***" . $A[1];
}
/* Will output:

 * j = 0  line = aa
 * element 0 is aa
 *
 * j = 1  line = ***bb
 * element 1 is ***bb
 */

You see the &line that mean we are passing into the block a reference and now any change is reflected to the array and not to a $line copy of the initial array you passed into the loop block.

Hope I was clear...

I notice the docs say that exactly:

It says that exactly in documentation :)

In order to be able to directly modify array elements within the loop precede $value with &. In that case the value will be assigned by reference.

Shlomi Hassid
  • 6,500
  • 3
  • 27
  • 48
0

Unless the array is referenced, foreach() operates on a copy of the specified array and not the array itself. foreach has some side effects on the array pointer. Don't rely on the array pointer during or after the foreach without resetting it.

Thus, when you amend $A[1], you're amending the original array, not the copy that the foreach() loop subsequently uses.

This important distinction seems to currently be missing from the official documentation, but you can read more about it here: How does PHP 'foreach' actually work?

dearsina
  • 4,774
  • 2
  • 28
  • 34
0

As mentioned in the notes on php.net, unless the array is referenced foreach operates on a copy of the specified array and not the array itself. You are outputting a variable from the original copy and then the changed array.

JBES
  • 1,512
  • 11
  • 18