可能在PHP编码中使用&引用变量或者对象或者方法的人不多,但是&引用可以让你的代码变的简单而且节省资源消耗。在这篇文章中我们重点讨论的是foreach中使用&时出现的异常以及解决办法。
$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则赋值给新数组newExp3.输出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应该就是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: 16compiled vars: !0 = $arr, !1 = $key, !2 = $rowline # * 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
以上是php中foreach使用&引用後的異常處理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本文討論了PHP中的crypt()和password_hash()的差異,以進行密碼哈希,重點介紹其實施,安全性和對現代Web應用程序的適用性。

文章討論了通過輸入驗證,輸出編碼以及使用OWASP ESAPI和HTML淨化器之類的工具來防止PHP中的跨站點腳本(XSS)。

自動加載PHP會在需要時自動加載類文件,從而通過減少內存使用和增強代碼組織來提高性能。最佳實踐包括使用PSR-4和有效組織代碼。

本文討論了在PHP中管理文件上傳大小的管理,重點是2MB的默認限制以及如何通過修改PHP.INI設置來增加它。

本文討論了PHP 7.1中引入的PHP中的無效類型,允許變量或參數為指定類型或NULL。它突出顯示了諸如提高可讀性,類型安全性和明確意圖的好處,並解釋瞭如何聲明

本文討論了unset()和unlink()功能在編程中的差異,重點關注其目的和用例。 unset()從內存中刪除變量,而unlink()從文件系統中刪除文件。兩者都對效率至關重要


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

SublimeText3 Linux新版
SublimeText3 Linux最新版

記事本++7.3.1
好用且免費的程式碼編輯器