6

下面的代码说明了 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();

?>

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

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*
                )

    )

)

奇怪的是,如果在第二个函数中,中间变量将用于从堆栈中获取值,结果又可以了。我不明白发生了什么事。由于一个assinment,我如何多次获得根元素作为其自身的子元素?

最初,我需要从 XML 构建树(使用 sax 解析器)并打算让“当前根”指向当前级别的树节点,并将其推入/弹出堆栈并向其中添加子元素,但是,令人惊讶的是,由于上述两个函数显示的问题,我未能实施此方案。

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

4

3 回答 3

0

这是因为 PHP 在数组中引用的奇怪方式。使用右侧的引用进行普通(非引用)赋值通常不会将左侧变为引用,但数组内的引用会保留在赋值中,即使没有引用运算符也是如此。

我在下面评论了您的代码以帮助解释正在发生的事情,我希望我已经正确解释了这一点,现在是凌晨 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;
于 2012-10-04T23:52:33.357 回答
0

只是一种解决方法,使用 unset() 而不是 array_pop()。并且因为当您使用 array_pop() 时输入数组的数组指针被重置,所以我使用 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();

?>
于 2012-10-04T23:53:11.243 回答
0

一张图片价值1000字。花了一些时间来了解到底发生了什么。

我必须使用 Xdebug 正确转储内部数据并查看引用计数和写入时复制的效果。

第一篇文章中代码的问题是分配

$croot = array_pop($stack);

当 croot 为 'level_1' 时,通过复制完成,即 croot 的元素(与 level_1 相同)由堆栈中的数据填充,并且在此操作后 croot 与原始 root 不同。

图像将更好地解释。

内存中的 PHP 变量

于 2012-10-10T19:54:46.010 回答