PHP的foreach中使用引用时需要注意的一个问题和解决方法


Posted in PHP onMay 29, 2014

一、问题
先看一个例子:

<?php
$ar = array(1, 2, 3);
var_dump($ar);
foreach ($ar as &$v) {}
foreach ($ar as $v) {}
var_dump($ar);
?>
输出为:

array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
}
array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  &int(2)
}
???为什么没有进行赋值操作,数组最后一个元素的值却发生了改变呢?

我早就发现了这个问题,一开始以为是 PHP 的 bug,就扔着没管它, foreach 中不使用引用就没事, 用 foreach $k => $v 然后 $ar[$k] 来改变原始数组, 略微损失点效率。

二、分析

今天花了点时间,看了 参考 中的文章, 算是稍微明白一点了,原来是这个样子的:

在执行第一个使用引用的 foreach 时, 一开始, $v 指向 $ar[0] 的存储空间,空间内存储着 1 , foreach 结束时, $v 指向 $ar[2] 的存储空间,空间内存储着 3 。 下面要开始执行第二个 foreach 了,注意和第一个 foreach 不同, 第二个 foreach 没有使用引用,那么就是赋值方式, 即将 $ar 的值依次 赋值 给 $v 。 进行到第一个元素时,要将 $ar[0] 赋值给 $v 。 问题就在这里,由于刚刚执行完第一个 foreach, $v 不是一个新变量,而是已经存在的、指向 $ar[2] 的那个 引用 , 如此一来,对 $v 进行赋值的时候,就将 $ar[0] = 1 写入了 $ar[2] 的实际存储空间, 相当于对 $ar[2] 进行赋值。 依此类推,第二个 foreach 执行的结果, 就是数组的最后一个元素变成了倒数第二个元素的值。 参考文章 2 中有详细的示意图。

如果说这是一个错误,那么错误的原因就在于对引用变量的使用。 当引用变量指向和其他变量时,改变引用变量的值当然会影响到他指向的其他变量。 单独说谁都明白,但在这个 foreach 例子中,凑巧了, 同一个变量两次被使用,前一次是引用的身份,后一次是普通变量身份, 就产生了意料之外的效果。 PHP 的开发者也认为,这种情况属于语言特性造成的,不是 bug。 的确,如果要修复这个问题,一种方法是对 foreach 进行特殊处理之外, 另外一种就是限制 foreach 中 $v 的作用域, 这两种方式都与目前 PHP 的语言特性不符,开发人员不愿改, 但还是在 官方文档 中用 Warning 进行了说明。

三、解决方法

简单,但谈不上完美,就是在使用了引用的 foreach 之后, unset 掉 $v , 开始的例子改为:

<?php
$ar = array(1, 2, 3);
var_dump($ar);
foreach ($ar as &$v) {}
unset($v);
foreach ($ar as $v) {}
var_dump($ar);
?>
运行结果:

array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
}
array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
}

参考

Bug #29992 foreach by reference corrupts the array:https://bugs.php.net/bug.php?id=29992
References and foreach:http://schlueters.de/blog/archives/141-References-and-foreach.html

PHP 相关文章推荐
IP138 IP地址查询小偷实现代码
Feb 15 PHP
PHP 异步执行方法,模拟多线程的应用分析
Jun 03 PHP
浅谈PHP强制类型转换,慎用!
Jun 06 PHP
为PHP5.4开启Zend OPCode缓存
Dec 26 PHP
php+ajax实现无刷新动态加载数据技术
Apr 28 PHP
PHP+Mysql+jQuery中国地图区域数据统计实例讲解
Oct 10 PHP
thinkphp微信开发(消息加密解密)
Dec 02 PHP
yii分页组件用法实例分析
Dec 28 PHP
基于命令行执行带参数的php脚本并取得参数的方法
Jan 25 PHP
PHP数据库表操作的封装类及用法实例详解
Jul 12 PHP
利用PHP判断文件是否为图片的方法总结
Jan 06 PHP
深入理解PHP的远程多会话调试
Sep 21 PHP
神盾加密解密教程(一)PHP变量可用字符
May 28 #PHP
CI框架开发新浪微博登录接口源码完整版
May 28 #PHP
PHP+javascript制作带提示的验证码源码分享
May 28 #PHP
微信支付开发教程(一)微信支付URL配置
May 28 #PHP
php中$美元符号与Zen Coding冲突问题解决方法分享
May 28 #PHP
php轻松实现中英文混排字符串截取
May 28 #PHP
分享一段php获取linux服务器状态的代码
May 27 #PHP
You might like
PHP 中的批处理的实现
2007/06/14 PHP
PHP中的output_buffering详细介绍
2014/09/27 PHP
PHP实现的策略模式简单示例
2017/08/25 PHP
Yii2框架操作数据库的方法分析【以mysql为例】
2019/05/27 PHP
ThinkPHP 5.x远程命令执行漏洞复现
2019/09/23 PHP
php实现登录页面的简单实例
2019/09/29 PHP
Javascript 函数对象的多重身份
2009/06/28 Javascript
javascript GUID生成器实现代码
2009/10/31 Javascript
JavaScript面向对象程序设计三 原型模式(上)
2011/12/21 Javascript
兼容IE和FF的图片上传前预览js代码
2013/05/28 Javascript
JS Jquery 遍历,筛选页面元素 自动完成(实现代码)
2013/07/08 Javascript
解决js中window.open弹出的是上次的缓存页面问题
2013/12/29 Javascript
javascript实现漂亮的拖动层,窗口拖拽特效
2015/04/24 Javascript
纯JS代码实现一键分享功能
2016/04/20 Javascript
JavaScript之map reduce_动力节点Java学院整理
2017/06/29 Javascript
layui table设置前台过滤转义等方法
2018/08/17 Javascript
原生JS实现动态添加新元素、删除元素方法
2019/05/05 Javascript
微信小程序获取用户信息及手机号(后端TP5.0)
2019/09/12 Javascript
python之wxPython应用实例
2014/09/28 Python
Python读取ini文件、操作mysql、发送邮件实例
2015/01/01 Python
python解析基于xml格式的日志文件
2017/02/25 Python
分享一下Python数据分析常用的8款工具
2018/04/29 Python
Python实现的txt文件去重功能示例
2018/07/07 Python
django模型类中,null=True,blank=True用法说明
2020/07/09 Python
安装Anaconda3及使用Jupyter的方法
2020/10/27 Python
计算机专业毕业生推荐信
2013/11/25 职场文书
党员思想汇报范文
2013/12/30 职场文书
关于逃课的检讨书
2014/01/23 职场文书
工作迟到检讨书
2014/02/21 职场文书
2014年最新学习全国两会精神心得
2014/03/17 职场文书
幼儿园教研活动总结
2014/04/30 职场文书
党员批评与自我批评发言稿
2014/10/14 职场文书
毕业实习证明范本
2015/06/16 职场文书
建筑工程挂靠协议书
2016/03/23 职场文书
2016年学校“6﹒26国际禁毒日”宣传活动总结
2016/04/05 职场文书
vue项目proxyTable配置和部署服务器
2022/04/14 Vue.js