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 相关文章推荐
php 无限级缓存的类的扩展
Mar 16 PHP
解析VS2010利用VS.PHP插件调试PHP的方法
Jul 19 PHP
php读取本地文件常用函数(fopen与file_get_contents)
Sep 09 PHP
php获取网页请求状态程序示例
Jun 17 PHP
php提示Warning:mysql_fetch_array() expects的解决方法
Dec 16 PHP
php实现的美国50个州选择列表实例
Apr 20 PHP
再Docker中架设完整的WordPress站点全攻略
Jul 29 PHP
weiphp微信公众平台授权设置
Jan 04 PHP
Laravel执行migrate命令提示:No such file or directory的解决方法
Mar 16 PHP
Ubuntu server 11.04安装memcache及php使用memcache来存储session的方法
May 31 PHP
php ZipArchive实现多文件打包下载实例
Oct 31 PHP
PHP7 windows支持
Mar 09 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防范SQL注入的具体方法详解(测试通过)
2014/05/09 PHP
php 使用file_get_contents读取大文件的方法
2014/11/13 PHP
JavaScript面向对象编程
2008/03/02 Javascript
javascript常用方法、属性集合及NodeList 和 HTMLCollection 的浏览器差异
2010/12/25 Javascript
jQuery阻止事件冒泡具体实现
2013/10/11 Javascript
js字符串转成JSON
2013/11/07 Javascript
JQuery实现鼠标移动到图片上显示边框效果
2014/01/09 Javascript
jquery 自定义容器下雨效果可将下雨图标改为其他
2014/04/23 Javascript
z-blog SyntaxHighlighter 长代码无法换行解决办法(jquery)
2014/11/16 Javascript
轻松创建nodejs服务器(2):nodejs服务器的构成分析
2014/12/18 NodeJs
Node.js实现批量去除BOM文件头
2014/12/20 Javascript
javascript之Array 数组对象详解
2016/06/07 Javascript
Angular路由简单学习
2016/12/26 Javascript
Bootstrap超大屏幕的实现代码
2017/03/22 Javascript
Angularjs实现数组随机排序的方法
2018/10/02 Javascript
基于Vue 实现一个中规中矩loading组件
2019/04/03 Javascript
详解Vue前端生产环境发布配置实战篇
2019/05/07 Javascript
vue input输入框关键字筛选检索列表数据展示
2020/10/26 Javascript
layer.confirm点击第一个按钮关闭弹出框的方法
2019/09/09 Javascript
Vue插件之滑动验证码
2019/09/21 Javascript
Python实现读写sqlite3数据库并将统计数据写入Excel的方法示例
2017/08/07 Python
利用Python将时间或时间间隔转为ISO 8601格式方法示例
2017/09/05 Python
python的numpy模块安装不成功简单解决方法总结
2017/12/23 Python
简单瞅瞅Python vars()内置函数的实现
2019/09/27 Python
Pytest单元测试框架如何实现参数化
2020/09/05 Python
Python中return函数返回值实例用法
2020/11/19 Python
用CSS3的box-reflect设置文字倒影效果的方法讲解
2016/03/07 HTML / CSS
匡威帆布鞋美国官网:Converse美国
2016/08/22 全球购物
外贸业务员的岗位职责
2013/11/23 职场文书
中专毕业生的自我鉴定
2013/12/01 职场文书
宾馆总经理岗位职责
2014/02/14 职场文书
生物技术专业求职信
2014/06/10 职场文书
2014年教育工作总结
2014/11/26 职场文书
春季运动会开幕词
2015/01/28 职场文书
公司备用金管理制度
2015/08/04 职场文书
Promise面试题详解之控制并发
2021/05/14 面试题