首页 > php函数引用传参数组:function set(array &$array, $path, $value)

php函数引用传参数组:function set(array &$array, $path, $value)

function set(array &$array, $path, $value)
{
    print_r($array);
    echo('<br>

<hr>

');
    $segments = explode('.', $path);

    while(count($segments) > 1)
    {
        $segment = array_shift($segments);

        if(!isset($array[$segment]) || !is_array($array[$segment]))
        {
            $array[$segment] = [];
        }

        $array =& $array[$segment];

        print_r($array);
        echo('<br><hr>');
    }

    $array[array_shift($segments)] = $value;

    print_r($array);
    echo('<br><hr>');
}

$arr = ['a' => 1, 'b' => 2, 'c' => 3];

set($arr, 'a.b.d', 4);

print_r($arr);

结果:

Array ( [a] => 1 [b] => 2 [c] => 3 )

Array ( )

Array ( )

Array ( [d] => 4 )

Array ( [a] => Array ( [b] => Array ( [d] => 4 ) ) [b] => 2 [c] => 3 )

为什么最终结果不是Array ( [d] => 4 ),而是 Array ( [a] => Array ( [b] => Array ( [d] => 4 ) ) [b] => 2 [c] => 3 ) ?
这个怎么解: $array =& $array[$segment];


首先,如果语句$array =& $array[$segment]中的&符号去掉,结果就如楼主所期待的最后输出Array([d] => 4)。显然问题的关键就在那一句的&上面。
我们知道php在存储变量和变量的值时分别用了两种数据结构zvalzval_value。在zval中包含四个字段:
refcount_gc 表示引用计数
is_ref_gc 表示是否为引用
value 存储变量标识符
type 变量的具体类型

变量的实际值存储在另一个联合体中,具体结构省略。现在我们来模拟一下过程:
首先$arr = ['a' => 1, 'b' => 2,'c' => 3]创建:
arr{'refcount_gc':1, 'is_ref_gc':0,'value':'arr','type':array}(后面将会省略value和type的书写),同时创建array类型的zval_value。
['a']{'refcount_gc':1, 'is_ref_gc':0, ...}
['b']{'refcount_gc':1, 'is_ref_gc':0, ...}
['c']{'refcount_gc':1, 'is_ref_gc':0, ...}
在调用set方法时,传递的是$arr的一个引用:&arr{'refcount_gc':2, 'is_ref_gc':1, ...}
接着进入函数体,while循环内,第一次由于!is_array($arr['a'])成立,为$arr['a']赋值空数组。
['a']{'refcount_gc':1, 'is_ref_gc':0, ...},并创建类型为array的zval_value。
$arr =& $arr['a'];这一句又将原数组的['a']转为引用['a']:{'refcount_gc':2, 'is_ref_gc':1, ...},它和arr都同时是这个空数组的引用①。
接着第二次循环,由于此时arr是一个空数组的引用,!isset($arr['b'])成立,为$arr['b']赋值空数组。
arr['b']{'refcount_gc':2, 'is_ref_gc':1, ...},并创建类型为array的zval_value,同时由于①,原来数组的['a']也是这个空数组的引用, 也就是在原数组['a']基础上有了['a']['b']②。
$arr =& $arr['b'];这一句又将arr['b']转为引用arr['b']:{'refcount_gc':3, 'is_ref_gc':1, ...},由于②,①中的空数组的['b'],原数组的['a']['b']和arr都同时是这个空数组的引用③。
然后退出循环。
$arr['d'] = 4;为②中的空数组创建索引['d'],且由于③,原数组的['a']['b']、①中数组的['b']也是这个数组的引用,他们都指向了这个(['d'] => 4)的数组。
所以最后打印原数组出来的就是Array ( [a] => Array ( [b] => Array ( [d] => 4 ) ) [b] => 2 [c] => 3 )

简化的图示为:

画的比较随意。。大致按数字步骤体现其过程。。。


问题主要出现在

 while(count($segments) > 1)
    {
        $segment = array_shift($segments);

        if(!isset($array[$segment]) || !is_array($array[$segment]))
        {
            $array[$segment] = [];
        }
        //这里传引用赋值,将$array[$segment]的引用传给了$array
        //所以此后的$array已经不再指向传参近来的那个$array(但传参进来的那个$array并未消失)
        //最简单的方法是将这个&去掉,在输出一遍,你应该就明白了
        $array =& $array[$segment];

        print_r($array);
        echo('<br><hr>');
    }

运行一下,如果不明白有问题可以留言

【热门文章】
【热门文章】