按引用传递如何或为什么用 PHP 覆盖其他引用?
How or why does passing by reference overwrite other references with 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);
在此实例中通过引用获取 return,因此此处的 & 可能有点矫枉过正。再一次,我不像我想的那样确定。出于这个问题的目的,我只是 真的 想了解视图方法中的引用覆盖是怎么回事,但请随时教我其他我应该知道但显然不知道的事情t.
因为变量 $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()
的函数,它似乎是为您正在尝试做的事情而设计的,这就是我建议的方式一起去。
但是为了完整起见,"fix" 您的代码相当简单。
有两种方法可以做到这一点:
将 $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()
。
有人可以向我解释为什么引用变量传递的行为与我所看到的一样吗?
这是我正在处理的控制器中的一个方法:
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);
在此实例中通过引用获取 return,因此此处的 & 可能有点矫枉过正。再一次,我不像我想的那样确定。出于这个问题的目的,我只是 真的 想了解视图方法中的引用覆盖是怎么回事,但请随时教我其他我应该知道但显然不知道的事情t.
因为变量 $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()
的函数,它似乎是为您正在尝试做的事情而设计的,这就是我建议的方式一起去。
但是为了完整起见,"fix" 您的代码相当简单。
有两种方法可以做到这一点:
将
的参数来做到这一点$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()
。