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(1)
Oct 09 PHP
PHPWind 发帖回帖Api PHP版打包下载
Feb 08 PHP
Godaddy空间Zend Optimizer升级方法
May 10 PHP
PHP获取本周第一天和最后一天示例代码
Feb 24 PHP
php的array数组和使用实例简明教程(容易理解)
Mar 20 PHP
PHP实现对文本数据库的常用操作方法实例演示
Jul 04 PHP
PHP使用trim函数去除字符串左右空格及特殊字符实例
Jan 07 PHP
php文件类型MIME对照表(比较全)
Oct 07 PHP
PHP 文件上传后端处理实用技巧方法
Jan 06 PHP
php 读写json文件及修改json的方法
Mar 07 PHP
php ActiveMQ的安装与使用方法图文教程
Feb 23 PHP
WordPress免插件实现面包屑导航的示例代码
Aug 20 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/05/18 PHP
PHP中time(),date(),mktime()区别介绍
2013/09/28 PHP
php封装的mysqli类完整实例
2016/10/18 PHP
PHP实现webshell扫描文件木马的方法
2017/07/31 PHP
jQuery 打造动态渐变按钮 详细图文教程
2010/04/25 Javascript
JavaScript 基础篇之运算符、语句(二)
2012/04/07 Javascript
javascript运行机制之this详细介绍
2014/02/07 Javascript
js完美实现@提到好友特效(兼容各大浏览器)
2015/03/16 Javascript
javaScript实现滚动新闻的方法
2015/07/30 Javascript
js原生跨域_用script标签的简单实现
2016/09/24 Javascript
jQuery Mobile和HTML5开发App推广注册页
2016/11/07 Javascript
javascript 正则表达式分组、断言详解
2017/04/20 Javascript
详谈Node.js之操作文件系统
2017/08/29 Javascript
微信小程序之多列表的显示和隐藏功能【附源码】
2018/08/06 Javascript
js实现旋转的星空效果
2019/11/01 Javascript
环形加载进度条封装(Vue插件版和原生js版)
2019/12/04 Javascript
JsonServer安装及启动过程图解
2020/02/28 Javascript
Vue的全局过滤器和私有过滤器的实现
2020/04/20 Javascript
用python写asp详细讲解
2013/12/16 Python
浅析Python多线程下的变量问题
2015/04/28 Python
python 判断是否为正小数和正整数的实例
2017/07/23 Python
解决pyqt5中QToolButton无法使用的问题
2019/06/21 Python
flask实现验证码并验证功能
2019/12/05 Python
Python datetime 格式化 明天,昨天实例
2020/03/02 Python
Python生成器实现简单&quot;生产者消费者&quot;模型代码实例
2020/03/27 Python
Django 如何使用日期时间选择器规范用户的时间输入示例代码详解
2020/05/22 Python
python中的错误如何查看
2020/07/08 Python
Lombok插件安装(IDEA)及配置jar包使用详解
2020/11/04 Python
Bjorn Borg官方网上商店:国际运动时尚品牌
2016/08/27 全球购物
OSPF有什么优点?为什么OSPF比RIP收敛快?
2013/02/13 面试题
我的大学四年规划书范文2014
2014/09/26 职场文书
给老师的一封感谢信
2015/01/20 职场文书
教师节倡议书2015
2015/04/27 职场文书
答辩状格式范本
2015/05/22 职场文书
thinkphp 获取控制器及控制器方法
2021/04/16 PHP
python如何进行基准测试
2021/04/26 Python