Как или почему передача по ссылке перезаписывает другие ссылки с помощью PHP?

Может ли кто-нибудь объяснить мне, почему передача переменных по ссылкам ведет себя так, как я наблюдаю?

Вот метод из контроллера, над которым я работаю:

public function view($view,$context=array()){
// <snip>
foreach($context as $a=>$b){
$$a = $b;
}
// <snip>
}

Копирует данные $context массив в локальной области видимости, так что когда представление вызывается (включается), он может получить доступ к элементам.

$foo->method(); // first thing
$bar->method(); // second thing

Будучи немного суетливым, я хотел работать с реальными объектами, а не с копиями (что казалось просто расточительным), поэтому я изменил это на следующее:

public function view($view,$context=array()){
// <snip>
foreach($context as $a=>$b){
$$a =& $b;
}
// <snip>
}

Именно тогда я стал свидетелем чего-то, чего я не ожидал.

$foo->method(); // second thing!!
$bar->method(); // second thing

В тестовом примере два объекта передаются в представление, которое затем выводит соответствующие данные. При передаче по ссылке, а не по значению обе переменные заканчивались ссылками на второй объект.

Я не ожидал, что это произойдет. Я бы очень хотел, чтобы кто-то объяснил мне, почему это произошло. Я, вероятно, пропустил что-то очевидное, поэтому, пожалуйста, просветите меня.

В случае, если это уместно (я подозреваю, что это может быть, но я не уверен).

Метод вызывается так:

$data = array();
$data['foo'] =& $this->module()->get_foo();
$data['bar'] =& $this->module()->get_bar();
$this->view('nameOfView',$data);

В этом случае по возвращаемой ссылке возвращается & здесь, наверное, перебор. Опять же, я не так уверен, как хотелось бы. Для целей этого вопроса я просто действительно Я хочу понять, что происходит с перезаписью ссылок в методе view, но не стесняйтесь учить меня чему-то еще, что я должен знать, но явно не знаю.

1

Решение

Потому что переменная $b используется повторно foreach петля.
И в качестве $b переназначен, так же как и все ссылки на него.

Простой пример:

$a = [1, 2, 3];
$c = [];
foreach($a as $b)
{
$c[] = &$b;
}
print_r($c);

Который дает:

Array
(
[0] => 3
[1] => 3
[2] => 3
)

Вы даже можете сделать шаг вперед и выполнить задание после foreach цикл:

$b = 'derp';

который превратит ваш массив в это:

Array
(
[0] => derp
[1] => derp
[2] => derp
)

Теперь, как уже дважды отмечалось в комментариях, есть функция extract(), который, кажется, был сделан именно для того, что вы пытаетесь сделать, и именно так я и предлагаю.

Но ради полноты достаточно просто «исправить» ваш код.
Есть два способа сделать это:

  • Делать $b ссылка вместо копии.
    Вы можете сделать это с помощью $a as &$b в качестве аргумента foreach:

    $a = [1, 2, 3];
    $c = [];
    foreach($a as &$b)
    {
    $c[] = &$b;
    }
    print_r($c);
    
  • Сломать ссылку на $b до того, как он переназначен.
    Вы можете сделать это, позвонив unset($b); в конце вашего цикла:

    $a = [1, 2, 3];
    $c = [];
    foreach($a as $b)
    {
    $c[] = &$b;
    unset($b);
    }
    print_r($c);
    

Оба из вышеперечисленного дадут вам то, что вы изначально ожидали:

Array
(
[0] => 1
[1] => 2
[2] => 3
)

Обратите внимание, что при использовании первого способа и вы изменяете $a после этого, $c может измениться, но не всегда.
Например, прямое назначение ($a[0] = 5;) повлияет $c ($c[0] == 5).
$c остается незатронутым любым другим действием на $a (насколько я могу судить), но после возни с индексами в $a (как с array_shift() или же shuffle()), $c[1] может быть ссылкой на $a[0], так далее.

Если ты не хочешь головной боли, просто иди с extract(),

3

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]