引用类型(Reference)在许多计算机语言中都被使用,而且是作为一个非常强大而实用的特性存在。它有类似指针(Pointer)的实现,却又有不同于指针的表现。例如C++的引用,可以让不同变量指向同一个对象,同时又保有直接使用dot来获取对象成员,不用繁琐的使用dereference运算符(*)和Pointer to Member运算符(->)。Java和C#中就直接以引用为主要类型,尽量让开发人员避免使用指针。

PHP中也引入了引用类型,在对对象赋值传递上,基本可视为是同于Java/C#的引用传递(具体请见Objects and references)。但同时又支持在基础类型上通过引用运算符(&)来获得内容的引用。不过在实际的使用中,PHP的引用类型因为整个PHP设计结构而存在着许多的问题,使得在程序出现非预计的结果。

引用变量可被赋予新的引用

在C++中,引用类型的变量只能在其定义时被赋予引用值,所以我们只要追踪到变量的定义处就可以知道变量是在操作哪个内容。

但是PHP不同,PHP里模糊了变量的定义,可以不定义就使用的变量。所以可以让变量被多次赋予引用值。

$x = 21;
$y = 7;$z = &$x;
$z = &$y;var_dump($x,$y,$z);

初次看起来,让人的感觉是$z变成了对$x的引用,然后让$z的内容变成了对$y的引用,也就是说$x和$z都成对$y的引用。但是实际输出结果是:

int(21)
int(7)
int(7)

从结果上看出,$x保持不变,只是$z被改变成了对$y的引用。相当于先unset了$z变量然后赋予了新值。

$z = &$x;
unset($z);
$z = &$y;

这其实是比较合理逻辑,就比如下边的代码,我们并不是得到类似于“指向指针的指针(Pointer point to a Pointer)”那样的“引用引用的引用(Reference refer to a Referenece)”,只是多个引用到同一块内容的引用变量。

$x = 21;
$y = &$x;
$z = &$y

引用数组元素会让该元素变成引用类型

对于变量上取引用,并不会造成原变量类型的改变,但是如果取的是数组中的元素,却会让该元素也变成引用类型。

在看问题代码前,首先要指出的是:

Array assignment always involves value copying. Use the reference operator to copy an array by reference.

也就是说PHP的数组赋值是copy而非引用,赋值过程会创建新的数组赋予被赋值的变量。在新变量上的数组操作并不会影响到原数组变量中的内容。

$a = array(21, 7);
$b = $a;
$b[0] = 7;
var_dump($a);
echo '<br/>';
var_dump($b);//Output:
//array(2) { [0]=> int(21) [1]=> int(7) }
//array(2) { [0]=> int(7) [1]=> int(7) }

下边我们再来看看如果引用数组中的元素,会有什么异常。

$a = array(21, 7);
$c = & $a[0];
$b = $a;
$b[0]= "21";
$b[1]= "7";var_dump($a);
echo '<br/>';
var_dump($b);
echo '<br/>';
var_dump($c);
echo '<br/>';// Output:
// array(2) { [0]=> &string(2) "21" [1]=> int(7) }
// array(2) { [0]=> &string(2) "21" [1]=> string(1) "7" }
// string(2) "21"

代码中$b跟之前的只是简单的赋值,只是在之前多了一部取第一个元素的引用,但理应还是拷贝了一个新的数组。可是结果却是对$b的修改,同时也改变了$a的第一个元素,而第二个元素没有影响。

从输出中我们还看到了一个不寻常的地方,就是数组第一个元素的类型多一个‘&’符号。而这个正是取引用运算符。也就是说数组的第一个元素已经变成了引用类型。所以赋值时也是引用拷贝,而非值拷贝。

这个问题十分奇怪,在开发中也造成了许多不必要的困扰,原本以为拷贝出来的数组并没有跟原数组有关联,但是就因为这意外出现的引用类型,让我在操作时也影响到了原数组。

我也不清楚这算是PHP中的bug,还是有意如此设计。在网上找了很久也没有对该方便的相关解释,只有Float Middle的《PHP: References To Array Elements Are Risky》和 Symmetric Designs的《Problems w/accessing a PHP array by reference》里有谈到这个,但是也没有讲原因。

之后又在PHP的Bug Report中看到几篇有联系的报告(Bug6417, Bug7412, Bug15025, Bug20993)。有些说这是个Bug,而且已经在后边的版本被修复。具体我也没有明白,只能避免在数组上使用引用。

更有趣的事情是,如果unset那些引用,只留下一个,那么数组元素又会变成不含有引用的正常类型。

unset($b);
unset($c);
var_dump($a);// Output:
//array(2) { [0]=> string(2) "21" [1]=> int(7) }

避免使用PHP的引用

这个其实这是PHP Array Manual里面提到的要注意的地方,最常发生在foreach的之中,希望通过引用来改变远数组的值(可参见该篇文章)。

其实想通过使用foreach配合引用来改变数组元素的值,主要是因为PHP的数组是Associative Array,这种数组“不定长度,索引可以不连续,可同时用字符串和整数当索引”,所以我们无法用for循环简单增加整数索引。

当然我们可以像下边的代码那样通过$key直接对数组元素改变值,但是这可能存在一定的效率问题。

foreach ($array_var as $key => $value)$array_var [$key] = $newValue;

另一个常用的引用的地方是在函数调用中使用引用传递参数。其主要原因是希望通过这种方法让函数实现返回多个返回值。比如我们希望用一个表示指示函数是否在执行中出现error而导致返回值是无效的。

但是因为PHP的函数是可以返回不同的类型的,所以并不需要传入引用参数来作为表示。即使真的需要多个返回值,也可以通过返回“以字符串为主键的数组”作为解决方案,只不过可能需要在文档中指出每个元素都是对应那个结果。

有一个比较好操作方式,应该是每当引用变量不再需要使用时,就即时对该变量使用unset让它切换与内容之间的联系。而且即使该变量不是引用类型,我们确认它不再被使用,对它调用unset也不会有什么问题。至少保证在之后对该变量重新赋值时,并不会影响到之前的结果。


  1. Problems w/accessing a PHP array by reference - Symmetric Designs
  2. PHP: References To Array Elements Are Risky – Float Middle
  3. References and foreach - Johannes Schlüter
  4. References Explained - PHP Manual

转载于:https://www.cnblogs.com/ider/archive/2012/09/06/php_reference_issue.html

谨慎使用PHP的引用相关推荐

  1. apa引用要在文中吗_如何在研究论文中引用文献

    欢迎点击「云麦」↑关注我们! 本文主要介绍四类研究论文的引用方式:使用不同的引用方式 引用句式 不同格式 成功引用 方法一.使用不同的引用方式 研究论文需要引证.需要通过他人的作品论证自己的观点的时候 ...

  2. 引用别人的观点的时候要注意什么?

    在撰写科研论文时,经常需要引用别人的观点来支持自己的观点.在引用其他的作者的观点时,应当注意以下几点: 一,引用要合适.准确.如果你引用的观点确实能够证明自己的观点,那这样是能让自己的文章更有说服力. ...

  3. JavaScript语义基础

    变量(Variables) Variables是你存储数据的容器.声明一个变量需要使用关键字var,然后输入变量的名称. 1 var myvar; 定义一个变量后,可以赋予变量一个值: 1 myvar ...

  4. Java内存配太大导致fullgc_记一次因为短命大对象导致fullGC的问题

    写在前面 java内存申请和释放均是由jvm在控制.而释放往往会出现各种各样的问题,经常一个引用没处理好就引起内存泄漏,最后引发OOM.如果发生在重要业务系统还可能出现严重的生产事故. 因此内存使用一 ...

  5. 【Qbot】1.ChatGPT简介与Q群机器人部署教程

    最近OpenAi推出的ChatGPT火出NLP圈子,看到不少人说强人工智能已经出现,于是赶紧来体验感受一下. 该项目计划长期进行维护更新,欢迎star:https://github.com/zstar ...

  6. 使用Python做QQ机器人

    目录 事前准备 下载框架 具体实现 事前准备 一台电脑(windows7以上) python 3.8.10以上 一个不用的QQ号(如果没有可参考使用一个手机号注册多个QQ) 下载框架 我使用的是 go ...

  7. 「GoCN酷Go推荐」​QQ机器人 go-cqhttp

    什么是 go-cqhttp?✦ QQ机器人,可以做的事儿太多了,比如一个UP主需要群发多个QQ群,以便通知粉丝们开播:再比如可以检测群内或发给自己的消息,而通过代码直接回复做的简单回复.比如检测群内有 ...

  8. 【0基础QQ机器人开发】基于go-cqhttp的QQ机器人开发教程,仅供自学

    文章目录 一.本文目的: 二.实现历程: 三.开发过程 1.准备工作 1.cq-http的下载地址:[Releases · Mrs4s/go-cqhttp (github.com)](https:// ...

  9. windows布局go-cqhttp

    视频教程 windows布局go-cqhttp_哔哩哔哩_bilibili 官网:https://docs.go-cqhttp.org/ go-cqhttp文件下载地址:https://github. ...

最新文章

  1. java初学者笔记总结day9
  2. 不要网上乱拷贝代码了!一段网上找的代码突然炸了!
  3. MySQL百万级、千万级数据多表关联SQL语句调优
  4. android studio 怎么做屏幕适配?
  5. python【蓝桥杯vip练习题库】BASIC-5查找整数
  6. 敏捷个人手机应用:如何使用时中法目标
  7. Windows Form -----内容(2)
  8. MySQL管理员指南
  9. lacp静态和动态区别_lacp静态与动态区别
  10. 漫画:什么是 “图灵测试” ?
  11. Spring Cloud 一:注册中心
  12. python设计模式1-单例模式
  13. ndoejs中中间件的使用
  14. OpenCV-图像像素遍历操作的三种方法对比(程序提速)
  15. HP DV3 笔记本 重装系统
  16. ubuntu16.04安装mongodb
  17. 算法-codeup1928-日期差值
  18. jmail 发送html,jmail发送html格式的邮件
  19. 汇编语言第一课作业1.1
  20. 不可不知的P2P(Peer-To-Peer)

热门文章

  1. php数组指针什么用,php数组指针用法详解
  2. 如何用计算机寒假计划表,寒假学习计划表
  3. python中字符串的布尔值_Python基础之字符串,布尔值,整数,列表,元组,字典,集合...
  4. python脚本怎么使用_如何使用Python脚本
  5. idea启动springboot卡_写给新手看的 Spring Boot 入门学习指南
  6. 中国航信官笔试计算机基础,中国航信笔试题目
  7. matplotlib 散点图_Python学习 —— matplotlib绘制三维曲线图和三维散点图
  8. 15、计算机图形学——基于AABB进行光线追踪的加速(上)
  9. activiti 工作流_activiti工作流引擎入门
  10. 第2章 S交换机管理平面安全