通过引用传递如何或为什么用PHP覆盖其他引用?

通过引用传递如何或为什么用PHP覆盖其他引用?,php,pass-by-reference,byref,Php,Pass By Reference,Byref,有人能给我解释一下为什么引用变量传递会以我所看到的方式进行 以下是我正在处理的控制器的一个方法: public function view($view,$context=array()){ // <snip> foreach($context as $a=>$b){ $$a = $b; } // <snip> } 作为一个有点大惊小怪的人,我想处理实际的对象而不是副本(这看起来很浪费),所以我将其改为: public

有人能给我解释一下为什么引用变量传递会以我所看到的方式进行

以下是我正在处理的控制器的一个方法:

public function view($view,$context=array()){
    // <snip>
    foreach($context as $a=>$b){
        $$a = $b;
    }
    // <snip>
}
作为一个有点大惊小怪的人,我想处理实际的对象而不是副本(这看起来很浪费),所以我将其改为:

public function view($view,$context=array()){
    // <snip>
    foreach($context as $a=>$b){
        $$a =& $b;
    }
    // <snip>
}
在测试用例中,两个对象被传递到视图,然后视图输出相关数据。当通过引用而不是通过值传递时,这两个变量都以对第二个对象的引用结束

我没想到会发生这种事。我非常希望有人向我解释为什么会发生这种情况。我可能错过了一些明显的东西,所以请教育我

以防相关(我怀疑可能相关,但我不确定)

该方法的调用方式如下:

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

在这个实例中,通过引用获取返回,因此这里的&可能有些过分。再说一次,我没有我想的那么确定。出于这个问题的目的,我真的很想了解视图方法中引用覆盖的情况,但请随意教我其他我应该知道但显然不知道的内容。

因为变量
$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
)
现在,正如我在评论中两次提到的,有一个函数叫做,它似乎完全是为了你想做的事情而做的,这就是我建议的方法

但为了完整性起见,“修复”代码相当简单。
有两种方法可以做到这一点:

  • $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
中的索引后(与
数组移位()
洗牌()
一样,
$c[1]
可能是对
$a[0]
的引用,等等


如果您不想头痛,只需使用
extract()

我现在无法详细解释,但一般来说,使用foreach循环变量的引用会带来麻烦。但请注意,不会复制对象。如果有一个对象数组,则会复制该数组本身,包括对对象的引用,但对象本身仍然是原始对象,因此优化是无用的。我认为在你的第二个循环中,你正在用对迭代中最后一个对象的引用替换数组中的所有项。我只是读到了“作为一个小题大做的人,我想处理实际对象而不是副本”。两个观察结果:a)和b)(即,当您分配$$a=$b时,您没有在处理实例的副本)-也许这使您的问题成为一个非问题(尽管可能很有趣)。我过去也是一个大惊小怪的人,担心过度复制。但后来我了解到,“拷贝”是“惰性拷贝”,这意味着只有当您实际修改数据的“拷贝”时,才会进行拷贝。在此之前,您正在读取内存中相同的数据(在大多数情况下,我发现我很少修改输入参数)。这与您的函数不一样吗?对引用的解释都有
//第二点
,就是foreach中只有一个变量
$b
,它的值改变了,对它的引用没有改变。PHP引用可以是“有趣的”-/这是一个可靠的答案。我知道现在发生了什么,是的,确实会使用extract.being'picky'-只有一个变量$b,所以一切都指向它。它包含
foreach
循环的最后一个值。@RyanVincent,但有多个变量,每个变量在某个点被称为
$b
:p
foreach
循环变量可以保存对“循环对象”的引用参见
foreach($a as和$b)
-注意
和$b
。唉,这在这里没有发生。发生的情况是引用了循环变量
$b
。因此,每个人都可以引用“循环变量”,而不是它所指向的对象!。因此,每个人
都会看到“循环变量-$b”的最后一个值。这是循环中的最后一个值。
$a = [1, 2, 3];
$c = [];
foreach($a as $b)
{
    $c[] = &$b;
    unset($b);
}
print_r($c);
Array
(
    [0] => 1
    [1] => 2
    [2] => 3
)