• 技术文章 >后端开发 >php教程

    PHP 引用详解 - 踩坑与妙用

    步履不停步履不停2019-08-23 17:56:55原创432

    最近在关注「PHP 引用」这一话题,看过不少深度文章,对 PHP 里的「引用」有了更深的理解。

    0x00

    首先看如下代码:

    $foo['hello'] = '0';
    $bar = &$foo['hello']; // 引用!
    $tipi = $foo;
    $tipi['hello'] = '1';
    
    print_r($foo);

    问:输出 0 还是输出 1?答案是 1。

    原理何在?

    PHP 内核使用 zval 结构存储变量,在 PHP 代码里,我们利用 xdebug_debug_zval 函数一探究竟。

    修改如上代码:

    $foo['hello'] = '0';
    
    xdebug_debug_zval('foo');
    $bar = &$foo['hello']; // 引用!
    xdebug_debug_zval('foo');
    
    $tipi = $foo;
    $tipi['hello'] = '1';
    
    print_r($foo);

    输出如下:

    foo: (refcount=1, is_ref=0)=array ('hello' => (refcount=1, is_ref=0)='0')
    foo: (refcount=1, is_ref=0)=array ('hello' => (refcount=2, is_ref=1)='0')

    $foo['hello'] 从非引用变量(is_ref=0)变为引用变量(is_ref=1),而引用计数则为 refcount=2

    为什么会这样?

    根据 PHP: 引用做什么 - Manual 的解释:

    $a =& $b; 这意味着 $a 和 $b 指向了同一个变量。

    $a 和 $b 在这里是完全相同的,这并不是 $a 指向了 $b 或者相反,而是 $a 和 $b 指向了同一个地方。

    结合我们的例子,也就是说,当 $bar = &$foo['hello']; 执行时,$bar$foo['hello'] 都成为了「引用变量」,且它们「指向了同一个地方」。

    那么当我们复制此数组时,也复制了它 hello 元素的引用;当 $tipi['hello'] = '1'; 执行时,就修改了 tipi['hello']$foo['hello'] 以及 $bar 所指向的「同一个地方」。

    于是,$foo['hello'] 的值理所当然地成为了 1

    0x01

    略有深入引用的 PHPer 应该都试过这种语法:

    for ($list as &$value) {
        $value = 'foo';
    }

    PHP 在控制结构后不会回收变量,此处不多讲解;于是刚刚的坑,其实可以延伸一下。

    $foo['hello'] = '0';
    $foo['world'] = 'A';
    foreach($foo as &$value) { // 引用!
        // Do nothing.
    }
    $tipi = $foo;
    $tipi['hello'] = '1';
    $tipi['world'] = 'B';
    print_r($foo);

    此处输出如下:

    Array
    (
        [hello] => 0
        [world] => B
    )

    hello 正常,而 world 被修改为 B!原因可以结合 xdebug_debug_zval 函数自行探究。

    所以,随手 unset($value); 是个好习惯。

    0x02

    其实引用也不全是坑。好处还是大大地有。

    举例:

    $catList = [
        '1' => ['id' => 1, 'name' => '颜色', 'parent_id' => 0],
        '2' => ['id' => 2, 'name' => '规格', 'parent_id' => 0],
        '3' => ['id' => 3, 'name' => '白色', 'parent_id' => 1],
        '4' => ['id' => 4, 'name' => '黑色', 'parent_id' => 1],
        '5' => ['id' => 5, 'name' => '大', 'parent_id' => 2],
        '6' => ['id' => 6, 'name' => '小', 'parent_id' => 2],
        '7' => ['id' => 7, 'name' => '黄色', 'parent_id' => 1],
    ];

    如何实现将如上顺序表转换为层级树?

    过去,或者说通常情况下我们首先想到的是递归回溯。

    不过,利用 PHP 的引用特性,可以将时间复杂度降低到 O(n)

    $treeData = [];
    foreach ($catList as $item) {
        if (isset($catList[$item['parent_id']]) && !empty($catList[$item['parent_id']])) {
            // 子分类
            $catList[$item['parent_id']]['children'][] = &$catList[$item['id']];
        } else {
            // 一级分类
            $treeData[] = &$catList[$item['id']];
        }
    }
    
    var_export($treeData);

    更多PHP相关技术文章,请访问PHP教程栏目进行学习!

    以上就是PHP 引用详解 - 踩坑与妙用的详细内容,更多请关注php中文网其它相关文章!

    第八期线上培训班
    本文原创发布php中文网,转载请注明出处,感谢您的尊重!
    专题推荐:php 引用
    上一篇:php架构师主要是做什么的 下一篇:PHP保留类及特殊类

    相关文章推荐

    • 什么是php引用变量?PHP中引用是如何实现• php引用变量是什么?php引用变量的实例讲解• PHP引用是什么?php中引用的介绍(代码实例)

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网