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 无限级 SelectTree 类
May 19 PHP
php 使用post,get的一种简洁方式
Apr 25 PHP
php eval函数用法 PHP中eval()函数小技巧
Oct 31 PHP
php简单开启gzip压缩方法(zlib.output_compression)
Apr 13 PHP
PHP处理excel cvs表格的方法实例介绍
May 13 PHP
PHP使用memcache缓存技术提高响应速度的方法
Dec 26 PHP
smarty内置函数config_load用法实例
Jan 22 PHP
php获取本周开始日期和结束日期的方法
Mar 09 PHP
php之static静态属性与静态方法实例分析
Jul 30 PHP
利用Fix Rss Feeds插件修复WordPress的Feed显示错误
Dec 19 PHP
PHP 二维数组和三维数组的过滤
Mar 16 PHP
php微信公众账号开发之前五个坑(一)
Sep 18 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
zf框架的zend_cache缓存使用方法(zend框架)
2014/03/14 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(十五)
2014/06/30 PHP
Composer设置忽略版本匹配的方法
2016/04/27 PHP
laravel 字段格式化 modle 字段类型转换方法
2019/09/30 PHP
JS 日期验证正则附asp日期格式化函数
2009/09/11 Javascript
深入理解Javascript动态方法调用与参数修改的问题
2013/12/10 Javascript
jQuery中end()方法用法实例
2015/01/08 Javascript
javascript通过获取html标签属性class实现多选项卡的方法
2015/07/27 Javascript
JavaScript使ifram跨域相互访问及与PHP通信的实例
2016/03/03 Javascript
分享12个实用的jQuery代码片段
2016/03/09 Javascript
Nodejs从有门道无门菜鸟起飞必看教程
2016/07/20 NodeJs
vuejs父子组件通信的问题
2017/01/11 Javascript
Vue中如何实现轮播图的示例代码
2017/07/27 Javascript
jQuery zTree 异步加载添加子节点重复问题
2017/11/29 jQuery
详解webpack多页面配置记录
2018/01/22 Javascript
Angular使用操作事件指令ng-click传多个参数示例
2018/03/27 Javascript
解决vuejs项目里css引用背景图片不能显示的问题
2018/09/13 Javascript
NodeJs实现简单的爬虫功能案例分析
2018/12/05 NodeJs
JavaScript代码异常监控实现过程详解
2020/02/17 Javascript
手把手教您实现react异步加载高阶组件
2020/04/07 Javascript
Python实现的百度站长自动URL提交小工具
2014/06/27 Python
Python中map,reduce,filter和sorted函数的使用方法
2015/08/17 Python
python利用有道翻译实现&quot;语言翻译器&quot;的功能实例
2017/11/14 Python
python中pylint使用方法(pylint代码检查)
2018/04/06 Python
对Python中range()函数和list的比较
2018/04/19 Python
Python实现的ftp服务器功能详解【附源码下载】
2019/06/26 Python
python批量处理txt文件的实例代码
2020/01/13 Python
Python基于进程池实现多进程过程解析
2020/04/30 Python
pytorch使用horovod多gpu训练的实现
2020/09/09 Python
使用phonegap创建联系人的实现方法
2017/03/30 HTML / CSS
C++面试题:关于链表和指针
2013/06/05 面试题
自主实习接收函
2014/01/13 职场文书
自我介绍演讲稿
2014/01/15 职场文书
物业管理委托协议(2篇)
2014/09/23 职场文书
python process模块的使用简介
2021/05/14 Python
MySQL系列之开篇 MySQL关系型数据库基础概念
2021/07/02 MySQL