php中foreach使用&引用后的异常分析及处理办法
2018-02-27 16:31
609 查看
可能在PHP编码中使用&引用变量或者对象或者方法的人不多,但是&引用可以让你的代码变的简单而且节省资源消耗。在这篇文章中我们重点讨论的是foreach中使用&时出现的异常以及解决办法。
这部分代码的功能描述如下:
从简单的表象来分析貌似以上逻辑并没有错,而且我们预测输出的结果应该为
,在该代码块中使用了&v。因为我们这一步要做的事情是处理数组本身的数据所以使用引用对于内存消耗较少。在程序执行中v作为子元素的一个引用,所以我们直接修改$v就可以修改exp相应的子元素的值。那么当循环结束时v作为子元素的一个引用,所以我们直接修改$v就可以修改exp相应的子元素的值。那么当循环结束时v应该就是exp最后一个元素的引用。
那么当我们修改$v的值应该exp的最后一个元素会变化。而且还有一个非常重要的问题就是foreach中使用了引用后引用在foreach结束后任然是存在的。也就是在以上的foreach之外$v依旧引用exp最后一个元素
在这儿我们是做了一个常规的循环来循环exp而且在该循环中我们使用的是变量并没有使用引用。差别就是
在这个循环中其实$v依旧是exp最后一个元素的引用。那么在循环中其实每次都是奖exp当前(current)的值赋值给$v因为引用关系最终改变的是exp最后一个元素的值。
关注NS技术圈获得更多精彩分享以及免费视频
$exp = [ [ 'name' => 'test1', 'age' => 15, 'extension' => 'a:3:{s:4:"nose";s:4:"long";s:5:"mouth";s:3:"big";s:3:"eye";s:5:"small";}' ], [ 'name' => 'test2', 'age' => 25, 'extension' => 'a:3:{s:4:"nose";s:5:"long2";s:5:"mouth";s:4:"big2";s:3:"eye";s:6:"small2";}' ], [ 'name' => 'test4', 'age' => 18, 'extension' => 'a:3:{s:4:"nose";s:5:"long2";s:5:"mouth";s:4:"big2";s:3:"eye";s:6:"small2";}' ], [ 'name' => 'test3', 'age' => 20, 'extension' => 'a:3:{s:4:"nose";s:5:"long3";s:5:"mouth";s:4:"big3";s:3:"eye";s:6:"small3";}' ], ]; foreach ($exp as &$v) { $extension = @unserialize($v['extension']); $v['nose'] = $extension['nose'] ?? ""; $v['mouth'] = $extension['mouth'] ?? ""; $v['eye'] = $extension['eye'] ?? ""; } $newExp = []; foreach ($exp as $v) { if ($v['mouth'] == "big3"){ $newExp[] = $v; } } dump($newExp); exit;
这部分代码的功能描述如下:
1.将exp中的扩展字段混入到exp中 2.如果exp中mouth为big3则赋值给新数组newExp 3.输出newExp
从简单的表象来分析貌似以上逻辑并没有错,而且我们预测输出的结果应该为
... 0 => array:6 [▼ "name" => "test3" "age" => 20 "extension" => "a:3:{s:4:"nose";s:5:"long3";s:5:"mouth";s:4:"big3";s:3:"eye";s:6:"small3";}" "nose" => "long3" "mouth" => "big3" "eye" => "small3" ] ... 但是结果并不是我们所预测的那样,程序输出的结果为: [] 这是为什么呢,我们来逐一分析
foreach引用引发的异常
第一个foreach是以下的代码块foreach ($exp as &$v) { $extension = @unserialize($v['extension']); $v['nose'] = $extension['nose'] ?? ""; $v['mouth'] = $extension['mouth'] ?? ""; $v['eye'] = $extension['eye'] ?? ""; }
,在该代码块中使用了&v。因为我们这一步要做的事情是处理数组本身的数据所以使用引用对于内存消耗较少。在程序执行中v作为子元素的一个引用,所以我们直接修改$v就可以修改exp相应的子元素的值。那么当循环结束时v作为子元素的一个引用,所以我们直接修改$v就可以修改exp相应的子元素的值。那么当循环结束时v应该就是exp最后一个元素的引用。
那么当我们修改$v的值应该exp的最后一个元素会变化。而且还有一个非常重要的问题就是foreach中使用了引用后引用在foreach结束后任然是存在的。也就是在以上的foreach之外$v依旧引用exp最后一个元素
在foreach后$v是否还存在
...
foreach ($exp as &$v) { $extension = @unserialize($v['extension']); $v['nose'] = $extension['nose'] ?? ""; $v['mouth'] = $extension['mouth'] ?? ""; $v['eye'] = $extension['eye'] ?? ""; }
dump($v);
输出结果为:
array:6 [▼
"name" => "test3"
"age" => 20
"extension" => "a:3:{s:4:"nose";s:5:"long3";s:5:"mouth";s:4:"big3";s:3:"eye";s:6:"small3";}"
"nose" => "long3"
"mouth" => "big3"
"eye" => "small3"
]
第二个循环分析
$newExp = []; foreach ($exp as $v) { if ($v['mouth'] == "big3"){ $newExp[] = $v; } } dump($newExp);
在这儿我们是做了一个常规的循环来循环exp而且在该循环中我们使用的是变量并没有使用引用。差别就是
$v和
&$v请仔细看。
在这个循环中其实$v依旧是exp最后一个元素的引用。那么在循环中其实每次都是奖exp当前(current)的值赋值给$v因为引用关系最终改变的是exp最后一个元素的值。
那么在foreach中exp最后子元素的值一直是变的。演变过程如下:
//为了篇幅简略表示 //第一次循环exp变为:也就是第一个元素赋值给了最后一个元素 [ [ 'name' => 'test1', ... ], [ 'name' => 'test2', ... ], [ 'name' => 'test4', ... ], [ 'name' => 'test1', ... ], ] //第二次循环exp变为:也就是第二个元素赋值给了最后一个元素 [ [ 'name' => 'test1', ... ], [ 'name' => 'test2', ... ], [ 'name' => 'test4', ... ], [ 'name' => 'test2', ... ], ] //第三次循环exp变为:也就是第三个元素赋值给了最后一个元素 [ [ 'name' => 'test1', ... ], [ 'name' => 'test2', ... ], [ 'name' => 'test4', ... ], [ 'name' => 'test4', ... ], ] //第四次循环exp变为:也就是第四个元素赋值给了最后一个元素 循环完毕 [ [ 'name' => 'test1', ... ], [ 'name' => 'test2', ... ], [ 'name' => 'test4', ... ], [ 'name' => 'test4', ... ], ]
原因分析
从上可以看出虽然本来exp的最后一个元素复合if条件中的$v['mouth'] == "big3",但是在循环最后一个元素时其本身已经变成了第三个元素,所以mouth=big3的元素不存在了。这个流程有点儿绕,多看几遍就能看得懂。当然你也可以看看PHP的zend引擎中关于foreach的实现以及查看VLD中间代码,例如简单循环的VLD
number of ops: 16 compiled vars: !0 = $arr, !1 = $key, !2 = $row line # * op fetch ext return operands --------------------------------------------------------------------------------- 2 0 > INIT_ARRAY ~0 1 1 ADD_ARRAY_ELEMENT ~0 2 2 ADD_ARRAY_ELEMENT ~0 3 3 ADD_ARRAY_ELEMENT ~0 4 4 ADD_ARRAY_ELEMENT ~0 5 5 ASSIGN !0, ~0 4 6 > FE_RESET $2 !0, ->14 7 > > FE_FETCH $3 $2, ->14 8 > ZEND_OP_DATA ~5 9 ASSIGN !2, $3 10 ASSIGN !1, ~5 5 11 ECHO !1 12 ECHO !2 6 13 > JMP ->7 14 > SWITCH_FREE $2 7 15 > RETURN 1
总结
关注NS技术圈获得更多精彩分享以及免费视频
相关文章推荐
- 使用&引用处理的foreach 和 二维数组的遍历
- PHP中使用foreach和引用导致程序BUG及解决办法
- PHP异常处理定义与使用方法分析
- PHP中使用foreach和引用导致程序BUG的问题介绍
- 转-PHP —— 为什么不要在 FOREACH 中使用变量引用
- 谨慎使用PHP的引用原因分析
- php 异常处理 简单使用
- PHP的foreach语句使用引用变量的问题
- PHP中foreach循环中使用引用要注意的地方
- PHP的foreach中使用引用时需要注意的一个问题和解决方法
- PHP5.1以上版本使用Date函数时遇到警告的处理办法
- AndroidStudio 使用过程中出现的异常(Gradle sync failed)处理办法
- PHP中的&传值引用的问题,在foreach循环的结果能帮解释下输出的结果原理是什么?
- foreach使用&引用传递过程
- php引用详解,php引用传递使用,php引用方法返回,php引用分析!
- PHP中foreach循环中使用引用要注意的地方
- PHP 数组处理使用foreach、list、each等三个函数详解
- PHP 使用回调函数(set_error_handler)处理异常和错误
- 【php】foreach中使用引用引起的问题
- php 异常处理 简单使用