PHP的引用,你知道多少?

湖光山色
• 阅读 5632

真的是变懒了,一个月一篇的节凑都很难保证了。

最近面试他人的过程中,问了一些关于PHP引用的知识,发现很多同学对这方面知之甚少,还有很多工作中基本没有使用过。甚至有人告诉我要少用引用,引用会带来一些诡异的问题。我心里默默说,避免诡异的问题是要去理解引用而不是少用引用。今天一起来解析解析。

场景假设

先从一个引用的所谓诡异问题开始。假设我们有这个场景:我们从数据库中读取了一组订单数据,需要把订单的每条数据单独做些处理。


$list = [
    ['orderid' => '123', 'total_fee' => 10, 'name' => 'zhangsan'],
    ['orderid' => '456', 'total_fee' => 17, 'name' => 'lisi'],
    ['orderid' => '789', 'total_fee' => 14, 'name' => 'wangwu'],
];

foreach ($list as &$item) {
    // 对订单做了些什么处理
}

// 有了一些其它操作

$result = [];// 需要返回的结果
foreach ($list as $item) {// 重新映射名字
    $result[] = [
        'order_id' => $item['orderid'],
        'amount' => $item['total_fee'],
    ];
}

上面的程序会输出如下结果:

var_dump($result);

array(3) {
  [0]=>
  array(2) {
    ["order_id"]=>
    string(3) "123"
    ["total_fee"]=>
    int(10)
  }
  [1]=>
  array(2) {
    ["order_id"]=>
    string(3) "456"
    ["total_fee"]=>
    int(17)
  }
  [2]=>
  array(2) {
    ["order_id"]=>
    string(3) "456"
    ["total_fee"]=>
    int(17)
  }
}

这就是经常遇到的一种所谓的诡异问题,先用引用循环处理数据,后面又用了与引用相同的临时变量继续处理数据。这里就是:$item。很多同学说预防这种问题,就要少用引用。这种态度太消极了,引用在很多地方带来了代码书写的简洁,并且针对大数组使用引用能够节省大量的内存。

诡异问题解析

现在我们来分析下上面问题出现的原因。先来看引用的定义

引用意味着用不同的名字访问同一个变量内容。

那么在这部分代码中

foreach ($list as &$item) {
    // 对订单做了些什么处理
}

$item 最后跟 $list[2] 指向了同一个变量内容。并且在 foreach 循环完后,$item 并没有被销毁,因此在后续如果同名的话,会继续生效。图示如下:
PHP的引用,你知道多少?

那么再接下来的的另一个循环中。

foreach ($list as $item) {// 重新映射名字
    $result[] = [
        'order_id' => $item['orderid'],
        'amount' => $item['total_fee'],
    ];
}

每当 $list 把变量赋值给 $item 的时候,都同时改变了 $list[2] 的值。因此才会出现上面诡异的情况。我来逐步给大家演示下:

  • 第一次循环 $list[0]$item 指向 orderid=123 的订单,由于 $item$list[2] 的引用,此时导致 $orders[2] 也指向了 orderid=123 的订单;
  • 第二次循环 $list[1], $item 指向 orderid=456 的订单,因此 $list[2] 也指向了 orderid=456
  • 第三次循环 $list[2]的时候,明显其值已经变成了 orderid=456 的订单。

通过上面的分析,我相信大家对引用所谓的诡异有了了解。那么又该如何避免这种情况出现呢?其实很简单,每次使用完引用后,记得 unset 调引用。在后面便可毫无顾忌的继续使用了。具体到本例子就是:

foreach ($list as &$item) {
    // 对订单做了些什么处理
}
unset($item);

// 有了一些其它操作

foreach ($list as $item) {// 重新映射名字
}

引用的妙用

前面我说过,引用可以写出简洁的代码。无限级分类的使用便是一个使用场景。比如说我们有个分类的数据:

$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],
];

如果我想得到下面这种形式

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

如果使用引用,可以非常简单的得出结果。

$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']];
    }
}

大家可以试试不用引用的方式,把无限级实现出来试试,比较下代码。


年底了。没钱给大家发红包,给大家推荐一家上海的好公司。为大家跳槽助力。
PHP的引用,你知道多少?
PHP的引用,你知道多少?

公司网站:https://www.yimishiji.com/

手机网站:https://m.yimishiji.com/

公司目前正在招聘高级PHP工程师,要求:

  • 2-5年的PHP开发经验;
  • 本科学历;
  • 至少熟悉Laravel、Yii2框架中的一种;
  • 有电商、生鲜相关的经验加分;
  • 有博客、GitHub的加分。

待遇优厚:五险一金;每日内购零农残、有机食材水果;薪资15k-30k。

公司使用的是PHP7语法,对新技术是保持激进的态度。对于上海的小伙伴或者想去上海的小伙伴,强烈建议去看看。

  • 公司地址:上海市长宁区天山西路789号中山国际广场B座一米市集
  • CTO邮箱:alex.chang@yimishiji.com
点赞
收藏
评论区
推荐文章
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
cpp加油站 cpp加油站
4年前
c++中引用面试点7连问以及引用真的不分配内存吗
本篇文章从面试官的口吻连问7个引用有关的问题,并且从汇编的层面上对引用进行深入分析,让你充分理解引用的概念和原理。首先还是看一下思维导图:1.引用的背景和概念说到引用,首先要说一下'&'标识符,其实c语言中这个符号只是用来取地址的,并没有引用的概念,直到c对这个标识符的作用进行了扩充,才有了引用这个概念。所谓引用,其实就是给变量取了一个别名,一个简单
Wesley13 Wesley13
4年前
java 复制Map对象(深拷贝与浅拷贝)
java复制Map对象(深拷贝与浅拷贝)CreationTime2018年6月4日10点00分Author:Marydon1.深拷贝与浅拷贝  浅拷贝:只复制对象的引用,两个引用仍然指向同一个对象
2022年最新iOS面试题(附答案)
最近大家都要准备去面试或者已经在面试的,这里我给大家准备了挺多资料,可以私信我拿,看看了解下。底下就是我整理出来的一些面试题iOS类(class)和结构体(struct)有什么区别?Swift中,类是引用类型,结构体是值类型。值类型在传递和赋值时将进行复制,而引用类型则只会使用引用对象的一个"指向"。所以他们两者之间的区别就是两个类型的区别。举个简单的
Easter79 Easter79
4年前
tomcat调试servlet
有时候web程序会引用到workspace下面的其它工程,在tomcat调试的时候,经常会出现找不到所引用的jar的问题。 而抛出的异常也很诡异,有时候会是:<strongclassNotFound</strong,或者<strongNoClassDefineFound</strong, 有时候甚至会是<bXXXcan'tbe
Wesley13 Wesley13
4年前
java 强引用,软引用,弱引用,虚引用
强引用(StrongReference)强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。我们平常使用的大多数是强引用。软引用(SoftReference)如果
Wesley13 Wesley13
4年前
5个php实例,细致说明传值与传引用的区别
今天有个同事问我传值和传引用有什么不同,这让我想起了,刚学php的时候,那个时候做过很多项目,做东西多,就以为自己php掌握的差不多了,随着时间的推移,越深入的学习,越觉得自己知道的真的很少,很少。哈哈,会用只是初级阶段,要了解原理是什么,这样才能更好去运用,费话不多说1.传值:是把实参的值赋值给行参,那么对行参的修改,不会影响实参的值
Wesley13 Wesley13
4年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Stella981 Stella981
4年前
JVM调优总结一
数据类型   Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。基本类型的变量保存原始值,即:他代表的值就是数值本身;而引用类型的变量保存引用值。“引用值”代表了某个对象的引用,而不是对象本身,对象本身存放在这个引用值所表示的地址的位置。基本类型包括:byte,short,int,long,cha
Wesley13 Wesley13
4年前
PHP垃圾回收机制
php5.3之前使用的垃圾回收机制是单纯的“引用计数”,也就是每个内存对象都分配一个计数器,当内存对象被变量引用时,计数器1;当变量引用撤掉后,计数器1;当计数器0时,表明内存对象没有被使用,该内存对象则进行销毁,垃圾回收完成。“引用计数”存在问题,就是当两个或多个对象互相引用形成环状后,内存对象的计数器则不会消减为0;这时候,这一组内存对象已经
Stella981 Stella981
4年前
JVM调优总结(一)基本概念
数据类型Java虚拟机中,数据类型可以分为两类:基本类型和引用类型。    基本类型:保存原始值,即:他代表的值就是数值本身;    引用类型:保存引用值。“引用值”代表了某个对象的引用,而不是对象本身,对象本身存放在这个引