PHP中基礎中的三大坑,foreach遍歷,引用機制&,數組。php
今天咱們在講講foreach中的一些奇怪現象。segmentfault
在講解以前,能夠先看看我其餘相關的文章,屬於同一個大的知識點,都看看有助於理解。數組
當咱們使用foreach時,內部究竟發生了什麼?(PHP5)函數
△△△寫前聲明:如下結論都基於PHP5版本,由於時代在進步,在PHP7中內部的結構體模塊和引用模塊均發生重大變化,PHP7的foreach輸出規則也旋即發生變化。固然,因爲PHP7想要普及還須要一到兩年(今年是2016)時間,因此這篇文章仍是有些價值的,至少可讓你先理解一下PHP內部實現。指針
有疑問能夠直接在評論中拋出,我會一一解答。code
本文適合有必定基礎的PHPer。blog
那麼開始上圖,這是鳥哥(惠新宸,PHP7核心開發組成員,開發組中惟一的一箇中國人[撒花])在Think 2015 PHP技術峯會上的一個演講截圖,他在講述PHP5的foreach和PHP7的foreach區別,咱們把他演講中說起到PHP5的部分拿出來看看。
開發
那麼咱們着重看下這幅圖的三段代碼執行流程。
我講講三段代碼的運行原理
code1.php
<?php $a = array(1,2,3); foreach ($a as $key => $value) { var_dump(current($a)); //output int(2) int(2) int(2) } ?>
輸出值爲: int(2) int(2) int(2)。
同窗們可能納悶了,乍一看並無發生明顯的寫時複製(相關文章)或者強制分裂(相關文章),怎麼會是三個'2'呢。
關鍵點在於current()函數上:
foreach循環開始,拷貝一個數組出來,而後refcount_gc=2(foreach原理不太瞭解的同窗,能夠看看個人另外一篇文章:當咱們使用foreach時,內部究竟發生了什麼?(PHP5) )
此時原數組($a)和拷貝數組(我這裏命名爲$a_copy)的指針均指向下標1,
隨後進入大括號執行體,current()操做的參數必須是引用數組(紅線部分),若是不是引用數組的話會強制轉換成引用數組(即結構體中is_ref__gc從0 -> 1)。
根據強制分裂原理,一個結構體的is_ref__gc的值從0 -> 1的時候,若是refcount_gc=2時,就會發生url"強制分裂了"。
強制分裂後, 原數組($a)和拷貝數組(我這裏命名爲$a_copy)結構體已經不同,可是foreach操做的是拷貝數組($a_copy),原數組被丟在半道上了,因此三次輸出var_dump(current($a))均爲2
code2.php
<?php $a = array(1,2,3); $b = &$a; // 結構體中:refcount_gc=2;is_ref_gc=1; foreach ($a as $value) { var_dump(current($a)); //output: int(2) int(3) bool(false) } ?>
這段代碼和code1.php原理差很少,只是在foreach前進行了一次引用賦值,結構體變化成:refcount_gc=2;is_ref_gc=1; 隨後foreach遍歷數組,此時原數組($a),拷貝數組(我這裏命名爲$a_copy)和$b的指針均指向下標1而且均爲引用數負責(is_ref_gc)。
接着進入大括號執行體:var_dump(current($a)); 前面說道",current()操做的參數必須是引用數組(紅線部分),若是不是引用數組的話會強制轉換成引用數組(即結構體中is_ref__gc從0 -> 1)。"
此處參數$a已經爲引用數組,不會發生強制分裂,原數組($a)和拷貝數組($a_copy)爲同一個結構體,正常輸入爲:int(2) int(3) bool(false)
code3.php
<?php $a = array(1,2,3); $b = $a; // 結構體中:refcount_gc=2 foreach ($a as $key => $value) { var_dump(current($a)); //output int(1) int(1) int(1) } ?>
第三段代碼和前面兩個又有所不一樣了,此次$b=$a是使用傳值賦值,那麼此處爲何爲3個int(1)呢?此次的緣由在於foreach的機制:
foreach循環以前,須要判斷數組的 refcount計數,若是大於1,拷貝數組本身成爲一個新的結構體,循環過程操做的是新的結構體。
運行流程: line:3; // $a,$b共用一個結構體 line:4; //foreach開始遍歷數組,進行refcount判斷,拷貝數組是一個新的結構體,foreach操做的是新的結構體。那麼遍歷數組時,全程與原數組無關。 Line:5: //打印數組當前單元,因爲原數組和拷貝數組已不是一個結構體,因此原數組的指針沒變過,打印出int(1)。
若是仍是有些不明覺厲的話,能夠反覆看多幾遍,對了,再次推薦看下這幾篇文章,都能理解的話對foreach掌握也差很少夠用了。