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+AJAX实现无刷新注册(带用户名实时检测)
Jan 02 PHP
php去除字符串换行符示例分享
Feb 13 PHP
使用PHP函数scandir排除特定目录
Jun 12 PHP
浅析PHP程序设计中的MVC编程思想
Jul 28 PHP
php实现cookie加密的方法
Mar 10 PHP
TP(thinkPHP)框架多层控制器和多级控制器的使用示例
Jun 13 PHP
tp5(thinkPHP5框架)时间查询操作实例分析
May 29 PHP
YII2.0框架行为(Behavior)深入详解
Jul 26 PHP
PHP递归统计系统中代码行数
Sep 19 PHP
Thinkphp5.0框架的Db操作实例分析【连接、增删改查、链式操作等】
Oct 11 PHP
PHP常用函数之base64图片上传功能详解
Oct 21 PHP
TP - 比RBAC更好的权限认证方式(Auth类认证)
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写入数据库类代码分享
2011/07/26 PHP
destoon实现调用图文新闻的方法
2014/08/21 PHP
javascript中String类的subString()方法和slice()方法
2011/05/24 Javascript
Array, Array Constructor, for in loop, typeof, instanceOf
2011/09/13 Javascript
文本框(input)获取焦点(onfocus)时样式改变的示例代码
2014/01/10 Javascript
JavaScript编程的10个实用小技巧
2014/04/18 Javascript
jQuery层动画定位滑动效果的方法
2015/04/30 Javascript
JS中from 表单序列化提交的代码
2017/01/20 Javascript
原生JS实现圣旨卷轴展开效果
2017/03/06 Javascript
layui导航栏实现代码
2017/05/19 Javascript
ionic2屏幕适配实现适配手机、平板等设备的示例代码
2017/08/11 Javascript
phantomjs导出html到pdf的方法总结
2017/10/19 Javascript
Vue三种常用传值示例(父传子、子传父、非父子)
2018/07/24 Javascript
原生JS forEach()和map()遍历的区别、兼容写法及jQuery $.each、$.map遍历操作
2019/02/27 jQuery
vue2.0项目集成Cesium的实现方法
2019/07/30 Javascript
python pandas中对Series数据进行轴向连接的实例
2018/06/08 Python
Python数据可视化之画图
2019/01/15 Python
Python开启线程,在函数中开线程的实例
2019/02/22 Python
python实现坦克大战游戏 附详细注释
2020/03/27 Python
如何使用Python实现斐波那契数列
2019/07/02 Python
轻松掌握CSS3中的字体大小单位rem的使用方法
2016/05/24 HTML / CSS
CSS3+JavaScript实现炫酷呼吸效果的示例代码
2020/06/15 HTML / CSS
HTML5安全介绍之内容安全策略(CSP)简介
2012/07/10 HTML / CSS
HTML5 Video/Audio播放本地文件示例介绍
2013/11/18 HTML / CSS
Belle Maison倍美丛官网:日本千趣会旗下邮购网站
2016/07/22 全球购物
泰坦健身器材:Titan Fitness
2018/02/13 全球购物
草莓网中国:StrawberryNet中国
2020/08/17 全球购物
超越自我演讲稿
2014/05/21 职场文书
辞旧迎新演讲稿
2014/09/15 职场文书
教师个人学习总结
2015/02/11 职场文书
2015年出纳年终工作总结
2015/05/14 职场文书
写给汽车4S店的创业计划书,拿来即用!
2019/08/09 职场文书
话题作文之财富(600字)
2019/12/03 职场文书
HTML5+CSS+JavaScript实现捉虫小游戏设计和实现
2021/10/16 HTML / CSS
船舶调度指挥系统——助力智慧海事
2022/02/18 无线电
JS前端可扩展的低代码UI框架Sunmao使用详解
2022/07/23 Javascript