简体   繁体   中英

Why such construction breaks editing by reference?

In PHP 7 I use such construction to process some arrays values by reference

foreach ($this->values['key'] ?? [] as &$sub_value) {
    $sub_value['sub_key'] = 'new value';
}

I use "?? []" to not show error if key is not present or null. The problem is that with "?? []" added PHP does not make any changes in target value, only in local variable (which must be a link, but behaves in this case like I have not specified "&"):

class DefaultSandbox extends PHPUnit_Framework_TestCase
{
    private $values = array (
        'key' =>
            array (
                1 =>
                    array (
                        'external_link' => '',
                        'description' => 'ku1231',
                    ),
                2 =>
                    array (
                        'is_external' => '1',
                        'external_link' => 'http://some.ru',
                        'description' => 'LAME',
                    ),
            ),
    );

    public function testDefault()
    {
        foreach ($this->values['key'] ?? [] as &$sub_value) {
            $sub_value['sub_key'] = 'new value';
        }

        print_r($this->values['key']);
    }

}

Array ( [1] => Array ( [external_link] => [description] => ku1231 )

 [2] => Array ( [is_external] => 1 [external_link] => http://some.ru [description] => LAME ) 

)

Why does this happen? Code without "?? []" works normally:

        foreach ($this->values['key'] ?? [] as &$sub_value) {
            $sub_value['sub_key'] = 'new value';
        }

        print_r($this->values['key']);

Array ( [1] => Array ( [external_link] => [description] => ku1231 [sub_key] => new value )

 [2] => Array ( [is_external] => 1 [external_link] => http://some.ru [description] => LAME [sub_key] => new value ) 

It takes the left side as part of expression so basically it makes a copy of the array. You still modify the element but just in that copy.

The solution is to assign empty array prior to loop if the key value is null.

What is happening is due to the way that ?? works, and that arrays are values, not objects.

?? is equivalent to saying:

(isset($arr) ? $arr : [])

or, more long-windedly (and not entirely accurate, but close enough for this example):

if(isset($arr) {
    $__temp = $arr;
} else {
    $__temp = [];
}

Because arrays are values, the ?: operator (and ?? , by extension), return a new value; not the original variable.

When you iterate over the result, you're iterating over that temporary value, not the original array.

In your case, you will want to do something like:

if(!isset($arr)) {
     $arr = [];
}

before beginning the loop iteration; then you will get the result you expect.


Incidentally, if you iterate over an object, you will get the result you were trying to get:

 $obj = (object)[1, 2, 3]; foreach($obj ?? [] as &$val) { $val++; } 

At the end of that, $obj will contain 2, 3, and 4. This is because the "value" for an object is actually a reference to the object itself, and so what ($obj ?? []) returns is (essentially) the original object.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM