Search code examples
phpforeachconditional-operator

Unexpected behaviour in foreach using ternary operator and value by reference


Why the following code has the different output in PHP 5.5.* and PHP 7.*:

<?php
$foo = ['bar'=>[['item1'=>'value1']]];
foreach ($foo['bar'] ?: [] as $k => &$arr1) {
  $arr1['item1'] = 'value2';
}

var_dump($foo);

In PHP 5 value of $foo['bar'][0]['item1'] will be modified with the value2, but in PHP 7 it will not.


Solution

  • From the documentation (emphasis mine): Please note that the ternary operator is an expression, and that it doesn't evaluate to a variable, but to the result of an expression. This is important to know if you want to return a variable by reference. The statement return $var == 42 ? $a : $b; in a return-by-reference function will therefore not work and a warning is issued.

    With PHP 5, note how the innermost &array(1) is a reference:

    array(1) {
      ["bar"]=>
      array(1) {
        [0]=>
        &array(1) {
          ["item1"]=>
          string(6) "value2"
        }
      }
    }
    

    But with PHP 7, it is not:

    array(1) {
      ["bar"]=>
      array(1) {
        [0]=>
        array(1) {
          ["item1"]=>
          string(6) "value1"
        }
      }
    }
    

    If you remove ?: [], so the expression you're looping through is simply $foo['bar'], then the code behaves the same with both PHP 5 and 7, i.e. the innermost &array(1) is a reference.

    TLDR: I can't say why this is, or where the difference is explained, but it seems clear that PHP 7 handles the conditional operator ?: differently than does PHP 5.