4

Experiments in PHP 7.1 (docker image nanoninja/php-fpm:7.1)

In next piece of code everything clear:

$arr1 = [1, 2, 3];

foreach ($arr1 as &$value) {
    $value *= 2;
}

We have array $arr1 and multiply all values by 2. Result:

array(3) {
  [0]=>
  int(2)
  [1]=>
  int(4)
  [2]=>
  &int(6)
}

But what happens in this statement:

$arr1 = [1, 2, 3];

foreach ($arr2 = $arr1 as &$value) {
    $value *= 2;
}

Result of both arrays $arr1 and $arr2 will unchangeable:

array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
}

Why is it happens? I know that in PHP > 7 foreach works with copy of array but with copy of which array it works in this case $arr1 or $arr2. And why & don't work?

Timur
  • 488
  • 4
  • 14
  • Can you do an inline declaration like that and pass a variable as a reference in the same spot? Which would the for each be using in that situation? – Anthony Jan 10 '19 at 09:49
  • Possible duplicate of [How does PHP 'foreach' actually work?](https://stackoverflow.com/questions/10057671/how-does-php-foreach-actually-work) – Dharman Jan 10 '19 at 10:06
  • 1
    @Dharman This isn't so much about how foreach works, but that an assignment expression itself returns a value. – user3942918 Jan 10 '19 at 10:14
  • `foreach ($arr2 = $arr1 as ...` is equivalent to `foreach (function() as ...` - in both cases iterator loops over temporary array which is discarded after operation – Agnius Vasiliauskas Jan 10 '19 at 11:23

1 Answers1

6

foreach only works with a copy of the array in the normal by-value mode, not in by-reference mode. So that change in PHP 7 is not relevant to this code.

But in your second code block, you're not using a variable as the array to iterate over, so there's nothing to make a reference to. Instead, you have an expression, and the value of the expression is a copy of the array. It's essentially equivalent to doing:

$temp = $array1 = $array2;
foreach ($temp as &$value) {
    $value *= 2;
}

This will update $temp, but not $array1 or $array2.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • 2
    In case anyone is interested in the opcode details, see the vld dump [here](https://3v4l.org/6SXAv/vld#output). The second `ASSIGN`, using operands `!1, !0` (`$arr2` and `$arr1`, respectively) returns `$4` (value of the assignment expression) which is then used by the `FE_RESET_RW` opcode (basically starts the `foreach`.) Compare with [this one](https://3v4l.org/sjU22/vld#output) where `!0` (`$arr1`) is used directly. – user3942918 Jan 10 '19 at 10:13