PHP указатели ведут себя неожиданно - PullRequest
0 голосов
/ 29 августа 2018

Во время работы над проектом, который проверяет, связаны ли модели Laravel друг с другом, я заметил некоторое (странное?) Поведение указателя в PHP. Ниже приведен минимальный пример для воспроизведения того, что я нашел.

<code><?php

$arr = ['a', 'b', ['c']];


foreach($arr as &$letter) {
    if (!is_array($letter)) {
        $letter = [$letter];    
    }
}

dump($arr);

foreach($arr as $letter) {
    dump($arr);
}

function dump(...$dump) {
    echo '<pre>';
    var_dump($dump);
    echo '
'; }

Сначала я ожидал, что дампы в этом ответе будут возвращать одни и те же данные:

[ ['a'], ['b'], ['c'] ]

Но это не то, что случилось, я получил следующие ответы:

[ ['a'], ['b'], ['c'] ]
[ ['a'], ['b'], ['a'] ]
[ ['a'], ['b'], ['b'] ]
[ ['a'], ['b'], ['b'] ]

Работающий пример можно найти здесь .

Почему указатели действуют таким образом? Как я могу обновить $letter в первом цикле без необходимости делать $arr[$key] = $letter?


Редактировать: Поскольку люди, похоже, не понимают, почему существует второй цикл foreach, это означает, что массив изменяется без переназначения

Ответы [ 2 ]

0 голосов
/ 29 августа 2018

Прежде всего: в PHP нет указателей, есть ссылки. См. Какие ссылки и Какие ссылки не для получения дополнительной информации.

Причина, по которой это происходит, состоит в том, что $letter после цикла foreach все еще содержит ссылку на последний элемент вашего массива (который равен [c]). Таким образом, во втором цикле вы переопределяете не только $letter во время цикла, но и ссылку, на которую он указывает.

Для решения проблемы вам нужно unset($letter) после первого цикла:

<code>$arr = ['a', 'b', ['c']];

foreach($arr as &$letter) {
    if (!is_array($letter)) {
        $letter = [$letter];    
    }
}
unset($letter);   // this is important

dump($arr);

foreach($arr as $letter) {
    dump($arr);
}

function dump(...$dump) {
    echo '<pre>';
    var_dump($dump);
    echo '
'; }
0 голосов
/ 29 августа 2018

Согласно документации PHP :

Ссылка на значение $ и последний элемент массива остаются даже после цикла foreach. Рекомендуется уничтожить его с помощью unset () .

$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
    $value = $value * 2;
}
// $arr is now array(2, 4, 6, 8)

// Without an `unset($value)`, `$value` is still a reference to the last item: `$arr[3]`

foreach ($arr as $key => $value) {
    // $arr[3] will be updated with each value from $arr...
    echo "{$key} => {$value} ";
    print_r($arr);
}
// ...until ultimately the second-to-last value is copied onto the last value

/* output:
   0 => 2 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 2 )
   1 => 4 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 4 )
   2 => 6 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 6 )
   3 => 6 Array ( [0] => 2, [1] => 4, [2] => 6, [3] => 6 ) */
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...