2
votes

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

?>
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
What is your expected output it is not clear. - nerdlyist
@Dave I don't think you understand the question and example fully. - JBES

3 Answers

0
votes

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.

0
votes

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?

0
votes

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.