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 相关文章推荐
福利彩票幸运号码自动生成器
Oct 09 PHP
PHP4实际应用经验篇(6)
Oct 09 PHP
在smarty模板中使用PHP函数的方法
Apr 23 PHP
探讨PHP中OO之静态关键字以及类常量的详解
Jun 07 PHP
ThinkPHP的Widget扩展实例
Jun 19 PHP
PHP实现将浏览历史页面网址保存到cookie的方法
Jan 26 PHP
php实现的单一入口应用程序实例分析
Sep 23 PHP
magento后台无法登录解决办法的两种方法
Dec 09 PHP
PHP使用ActiveMQ实例
Feb 05 PHP
PHP多个图片压缩成ZIP的方法
Aug 18 PHP
PHP PDOStatement::bindColumn讲解
Jan 30 PHP
laravel框架创建授权策略实例分析
Nov 22 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
2019十大人气国漫
2020/03/13 国漫
php框架Phpbean说明
2008/01/10 PHP
PHP MSSQL 存储过程的方法
2008/12/24 PHP
简单的PHP留言本实例代码
2010/05/09 PHP
php全排列递归算法代码
2012/10/09 PHP
PHP编写的图片验证码类文件分享
2016/06/06 PHP
php设计模式之单例模式用法经典示例分析
2019/09/20 PHP
laravel 实现登陆后返回登陆前的页面方法
2019/10/03 PHP
firefox中用javascript实现鼠标位置的定位
2007/06/17 Javascript
获取URL地址中的文件名和参数的javascript代码
2009/09/02 Javascript
JQuery index()方法使用代码
2010/06/02 Javascript
JS如何将UTC格式时间转本地格式
2013/09/04 Javascript
jquery如何通过name名称获取当前name的value值
2013/12/20 Javascript
JavaScript中消除闭包的一般方法介绍
2015/03/16 Javascript
基于jquery fly插件实现加入购物车抛物线动画效果
2016/04/05 Javascript
原生js实现打字动画游戏
2017/02/04 Javascript
Node.js利用js-xlsx处理Excel文件的方法详解
2017/07/05 Javascript
AngularJS基于MVC的复杂操作实例讲解
2017/12/31 Javascript
详解Immutable及 React 中实践
2018/03/01 Javascript
vue项目打包后打开页面空白解决办法
2018/06/29 Javascript
Layui给数据表格动态添加一行并跳转到添加行所在页的方法
2018/08/20 Javascript
javascript删除数组元素的七个方法示例
2019/09/09 Javascript
python实战教程之自动扫雷
2018/07/13 Python
python3通过udp实现组播数据的发送和接收操作
2020/05/05 Python
selenium自动化测试入门实战
2020/12/21 Python
纯CSS3实现漂亮的input输入框动画样式库(Text input love)
2018/12/29 HTML / CSS
VELTRA台湾:世界自由行专家
2017/08/15 全球购物
英国家用电器购物网站:Hughes
2018/02/23 全球购物
银行实习的自我鉴定
2013/12/10 职场文书
餐饮企业总经理岗位职责范文
2014/02/18 职场文书
市场营销毕业生自荐信范文
2014/04/01 职场文书
《恐龙》教学反思
2014/04/27 职场文书
教师节学生演讲稿
2014/09/03 职场文书
初中团委工作总结
2015/08/13 职场文书
深入理解python多线程编程
2021/04/18 Python
Python实现排序方法常见的四种
2021/07/15 Python