有人能解释一下PHP中这两个引用用法的区别吗?

时间:2021-03-19 12:38:24

The code below illustrates the strange behaviour of PHP references:

下面的代码演示了PHP引用的奇怪行为:

<?php

function this_works()
{
    $root = array('name'=>'root', 'children'=>array());
    $level_1 = array('name'=>'level_1', 'children'=>array());
    $item1 = array('name'=>'level_2_1', 'children'=>array());
    $item2 = array('name'=>'level_2_2', 'children'=>array());

    $croot = &$root;

    $croot['children'][] = &$level_1;

    $croot = &$level_1;

    $croot['children'][] = &$item1;
    $croot['children'][] = &$item2;

    $croot = &$root;

    print_r($croot);
}    

function this_fails()
{    
    $root = array('name'=>'root', 'children'=>array());
    $level_1 = array('name'=>'level_1', 'children'=>array());
    $item1 = array('name'=>'level_2_1', 'children'=>array());
    $item2 = array('name'=>'level_2_2', 'children'=>array());
    $croot = &$root;

    $stack = array();

    $croot['children'][] = &$level_1;
    $crootref = &$croot;

    array_push($stack, $crootref);

    $croot = &$level_1;

    $croot['children'][] = &$item1;
    $croot['children'][] = &$item2;

    # this works, assignment below - doesn't... WHY?
    #$x = array_pop($stack);
    #var_dump($x);

    $croot = array_pop($stack);

    print_r($croot);
}    

this_works();
echo "------------------\n";
this_fails();

?>

First function provides expected results, while the second fails and claims about recursion loop:

第一个函数提供了预期的结果,第二个函数失败,并声明递归循环:

Array
(
    [name] => root
    [children] => Array
        (
            [0] => Array
                (
                    [name] => level_1
                    [children] => Array
                        (
                            [0] => Array
                                (
                                    [name] => level_2_1
                                    [children] => Array
                                    (
                                    )

                                )

                            [1] => Array
                                (
                                    [name] => level_2_2
                                    [children] => Array
                                        (
                                        )

                                )

                           )

                )

        )

)
------------------
Array
(
    [name] => root
    [children] => Array
        (
            [0] => Array
                (
                    [name] => root
                    [children] => Array
 *RECURSION*
                )

    )

)

What is strange, is that if in the second function, intermediate variable will be used to get value from stack, results are OK again. I don't understand what is going on. How do I get root element as a child of itself many times due to one assinment?

奇怪的是,如果在第二个函数中,使用中间变量从堆栈中获取值,那么结果还是可以的。我不明白是怎么回事。我如何得到根元素作为它自己的子元素由于一个assinment?

Originally, I needed to build the tree from XML (using sax parser) and intented to have 'current root' that points to tree node at the current level and push/pop it to/from stack and add child elements to it, but, surprisingly, I failed to implement this scheme due to issues demonstrated by two functions above.

最初,我需要从XML构建树(使用sax解析器),主要有“当前根”指向树节点的当前水平,推动/流行/从堆栈并将子元素添加到它,但是,令人惊讶的是,我未能实现这个计划由于问题证明了上面两个函数。

So, what is wrong with such approach?

那么,这种方法有什么问题呢?

3 个解决方案

#1


0  

It's because of the weird way PHP does references inside arrays. Doing a normal (not by reference) assignment with a reference on the right side does not usually turn the left side into a reference, but references inside arrays are preserved in assignments, even without the reference operator.

这是因为PHP在数组中引用的方式很奇怪。在右边做一个正常的(不是通过引用)赋值,通常不会把左边变成一个引用,但是数组中的引用被保留在赋值中,即使没有引用操作符。

I've commented your code below to help explain what is happening, I hope I've explained this correctly, it's 1am and I'm getting tired.

我已经评论了你下面的代码以帮助解释发生了什么,我希望我已经正确地解释了这一点,现在是凌晨1点,我开始累了。

$root = array('name'=>'root', 'children'=>array());
$level_1 = array('name'=>'level_1', 'children'=>array());
// $croot and $root now point to the same variable
$croot = &$root;

$stack = array();

$croot['children'][] = &$level_1;
// $crootref, $croot and $root all now point to the same variable
$crootref = &$croot;
// $stack[0], $crootref, $croot and $root all now point to the same variable.
// $stack[0]['children'][0], $level_1, $croot['children'][0] point to the same variable
array_push($stack, $crootref);
// $croot, $level_1 and $stack[0]['children'][0] now point to the same variable
// Infinite loop is caused as $stack[0]['children'][0] is now an alias for $croot
// which contains $croot['children'][0] which is an alias for $stack[0]['children'][0]
// which is an alias for $croot which contains....
$croot = &$level_1;

#2


0  

Just a workaround, using unset() instead of array_pop(). And because when you use array_pop() the array pointer of the input array is resseted i use reset() to simulate the same results, here you go:

只是一个变通方法,使用unset()而不是array_pop()。因为当您使用array_pop()时,输入数组的数组指针是resseted,我使用reset()来模拟相同的结果,在这里:

<?php

function this_fails_fixed()
{    
    $root = array('name'=>'root', 'children'=>array());
    $level_1 = array('name'=>'level_1', 'children'=>array());
    $item1 = array('name'=>'level_2_1', 'children'=>array());
    $item2 = array('name'=>'level_2_2', 'children'=>array());
    $croot = &$root;

    $stack = array();

    $croot['children'][] = &$level_1;
    $crootref = &$croot;

    array_push($stack, $crootref);

    $croot = &$level_1;

    $croot['children'][] = &$item1;
    $croot['children'][] = &$item2;

    unset($croot[count($croot)-1]);
        reset($croot);

    print_r($croot);
}    

this_fails_fixed();

?>

#3


0  

One picture worths 1000 words. It took some time to understand what's exactly is going on.

一幅画胜过1000字。我们花了一些时间才明白到底发生了什么。

I had to use Xdebug to dump internal data properly and see refcounts and effects of copy on write.

我必须使用Xdebug来正确地转储内部数据,并查看副本的ref计数和效果。

The issue with the code in 1st post is that assignment

第一个帖子的代码问题是这个任务。

$croot = array_pop($stack);

(croot =最后一美元堆栈);

when croot is 'level_1' is done by copy, i.e. elements of croot (same as level_1) are populated with data from stack and croot is not the same as original root after this operation.

当croot是‘level_1’时,是通过copy来完成的,即croot的元素(与level_1相同)被来自stack的数据填充,并且croot在这个操作之后与原来的根并不相同。

The image will explain better.

这张照片能更好地解释。

有人能解释一下PHP中这两个引用用法的区别吗?

#1


0  

It's because of the weird way PHP does references inside arrays. Doing a normal (not by reference) assignment with a reference on the right side does not usually turn the left side into a reference, but references inside arrays are preserved in assignments, even without the reference operator.

这是因为PHP在数组中引用的方式很奇怪。在右边做一个正常的(不是通过引用)赋值,通常不会把左边变成一个引用,但是数组中的引用被保留在赋值中,即使没有引用操作符。

I've commented your code below to help explain what is happening, I hope I've explained this correctly, it's 1am and I'm getting tired.

我已经评论了你下面的代码以帮助解释发生了什么,我希望我已经正确地解释了这一点,现在是凌晨1点,我开始累了。

$root = array('name'=>'root', 'children'=>array());
$level_1 = array('name'=>'level_1', 'children'=>array());
// $croot and $root now point to the same variable
$croot = &$root;

$stack = array();

$croot['children'][] = &$level_1;
// $crootref, $croot and $root all now point to the same variable
$crootref = &$croot;
// $stack[0], $crootref, $croot and $root all now point to the same variable.
// $stack[0]['children'][0], $level_1, $croot['children'][0] point to the same variable
array_push($stack, $crootref);
// $croot, $level_1 and $stack[0]['children'][0] now point to the same variable
// Infinite loop is caused as $stack[0]['children'][0] is now an alias for $croot
// which contains $croot['children'][0] which is an alias for $stack[0]['children'][0]
// which is an alias for $croot which contains....
$croot = &$level_1;

#2


0  

Just a workaround, using unset() instead of array_pop(). And because when you use array_pop() the array pointer of the input array is resseted i use reset() to simulate the same results, here you go:

只是一个变通方法,使用unset()而不是array_pop()。因为当您使用array_pop()时,输入数组的数组指针是resseted,我使用reset()来模拟相同的结果,在这里:

<?php

function this_fails_fixed()
{    
    $root = array('name'=>'root', 'children'=>array());
    $level_1 = array('name'=>'level_1', 'children'=>array());
    $item1 = array('name'=>'level_2_1', 'children'=>array());
    $item2 = array('name'=>'level_2_2', 'children'=>array());
    $croot = &$root;

    $stack = array();

    $croot['children'][] = &$level_1;
    $crootref = &$croot;

    array_push($stack, $crootref);

    $croot = &$level_1;

    $croot['children'][] = &$item1;
    $croot['children'][] = &$item2;

    unset($croot[count($croot)-1]);
        reset($croot);

    print_r($croot);
}    

this_fails_fixed();

?>

#3


0  

One picture worths 1000 words. It took some time to understand what's exactly is going on.

一幅画胜过1000字。我们花了一些时间才明白到底发生了什么。

I had to use Xdebug to dump internal data properly and see refcounts and effects of copy on write.

我必须使用Xdebug来正确地转储内部数据,并查看副本的ref计数和效果。

The issue with the code in 1st post is that assignment

第一个帖子的代码问题是这个任务。

$croot = array_pop($stack);

(croot =最后一美元堆栈);

when croot is 'level_1' is done by copy, i.e. elements of croot (same as level_1) are populated with data from stack and croot is not the same as original root after this operation.

当croot是‘level_1’时,是通过copy来完成的,即croot的元素(与level_1相同)被来自stack的数据填充,并且croot在这个操作之后与原来的根并不相同。

The image will explain better.

这张照片能更好地解释。

有人能解释一下PHP中这两个引用用法的区别吗?