您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關如何在PHP中使用foreach,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
語言基礎
foreach 語法結構提供了遍歷數組的簡單方式。
php5之前, foreach僅能用于數組 php5+, 利用foreach可以遍歷對象
foreach僅能夠應用于數據和對象,如果嘗試應用于其他數據類型的變量,或者未初始化的變量將發出錯誤信息。
有兩種語法:
/* 遍歷給定的 array_expression 數據。每次循環中, 當前單元的值被賦給$value并且數組內部的指針向前移一步(因此下次循環中將會得到下一個單元) */ foreach (array_expression as $value) { // statement } foreach (array_expression as $value) : // statement endforeach;
/* 同上,只除了當前單元格的鍵名也會在每次循環中被賦給變量$key */ foreach (array_expression as $key => $value) { // statement } foreach (array_expression as $key => $value) : // statement endforeach;
還能夠自定義遍歷對象!
當
foreach
開始執行時, 數組內部的指針會自動指向第一個單元. 這意味著不需要在foreach
循環之前調用reset()
由于foreach
依賴內部數組指針, 在循環中修改其值將可能導致意外的行為
可以很容易通過在 $value 之前加上 & 來修改數組元素. 此方法將以引用
賦值, 而不是拷貝一個值.
<?php $arr = [1, 2, 3, 4]; foreach($arr as &$value) { $value = $value * 2; } // $arr is now [2, 4, 6, 8] unset($value); // 最后取消掉引用
$value
的引用僅在被遍歷的數組可以被引用時才可用(例如是個變量)。
以下代碼無法運行:
<?php /* 此段代碼可以運行 運行結果: 1-2 2-4 3-6 4-8 */ foreach (array(1, 2, 3, 4) as &$value) { echo $value, '-'; $value = $value * 2; echo $value, PHP_EOL; }
Warning: 數組最后一個元素的
$value
引用在foreach
循環之后仍會保留。建議使用unset()
來將其銷毀。
Note:
foreach
不支持用@
來抑制錯誤信息的能力
foreach 雖然簡單, 不過它可能出現一些意外行為, 特別是代碼涉及到引用的時候。
<?php $arr = [1, 2, 3]; foreach ($arr as $k => &$v) { $v = $v * 2; } foreach ($arr as $k => $v) { echo $v, PHP_EOL; } /* 輸出: 2 4 4 */
我們可以認為 foreach($arr as &$v)
結構隱含了如下操作, 分別將數組當前的 鍵
和 值
賦值給 $k
和 $v
. 具體展開形如:
<?php foreach ($arr as $k => $v) { $k = currentKey(); $v = currentVal(); // 繼續運行用戶代碼 }
根據上述理論, 現在我們重新來分析下第一個foreach
:
循環 | 備注 | $arr值 |
---|---|---|
循環 1-1 | 由于$v 是一個引用, 因此 $v = &$arr[0] , $v = $v * 2 相當于 $arr[0] * 2 | [2, 2, 3] |
循環 1-2 | $v = &$arr[1] | [2, 4, 3] |
循環 1-3 | $v = &$arr[2] | [2, 4, 6] |
循環 2-1 | 隱含操作 $v = $arr[0] 被觸發, 由于此時 $v 仍是 $arr[2] 的引用, 相當于 $arr[2] = $arr[0] | [2, 4, 2] |
循環 2-2 | $v = $arr[1] , 即$arr[2] = $arr[1] | [2, 4, 4] |
循環 2-3 | $v = $arr[2] , 即$arr[2] = $arr[2] | [2, 4, 4] |
如何解決此類問題呢? PHP手冊上有一段提醒:
Warning: 數組最后一個元素的 $value 引用在 foreach 循環之后仍會保留。建議使用 unset() 來將其銷毀。
<?php $arr = [1, 2, 3]; foreach ($arr as $k => &$v) { $v = $v * 2; } unset($v); foreach ($arr as $k => $v) { echo $v, PHP_EOL; } /* 輸出: 2 4 6 */
從這個問題可以看出, 引用很可能會伴隨副作用。如果不希望無意識的修改導致數據內容變更, 最好及時unset掉這些引用。
<?php $arr = ['a', 'b', 'c']; foreach ($arr as $k => $v) { echo key($arr), "=>", current($arr), PHP_EOL; } foreach ($arr as $k => &$v) { echo key($arr), "=>", current($arr), PHP_EOL; } /* #php5.6 1=>b 1=>b 1=>b 1=>b 2=>c => #php7 0=>a 0=>a 0=>a 0=>a 0=>a 0=>a */
按照手冊中的說法, key和current分別是獲取數據中當前元素的鍵值。
那為何 key($arr)
一直是0,current($arr)
一直是'a'呢?
先用vld查看編譯后的 opcode
:
? demo /usr/local/Cellar/php/7.2.7/bin/php -dvld.active=1 a.php Finding entry points Branch analysis from position: 0 Jump found. (Code = 77) Position 1 = 2, Position 2 = 15 Branch analysis from position: 2 Jump found. (Code = 78) Position 1 = 3, Position 2 = 15 Branch analysis from position: 3 Jump found. (Code = 42) Position 1 = 2 Branch analysis from position: 2 Branch analysis from position: 15 Jump found. (Code = 62) Position 1 = -2 Branch analysis from position: 15 filename: /Users/jianyong/demo/a.php function name: (null) number of ops: 17 compiled vars: !0 = $arr, !1 = $v, !2 = $k line #* E I O op fetch ext return operands ------------------------------------------------------------------------------------- 2 0 E > ASSIGN !0, <array> 4 1 > FE_RESET_R $4 !0, ->15 2 > > FE_FETCH_R ~5 $4, !1, ->15 3 > ASSIGN !2, ~5 5 4 INIT_FCALL 'key' 5 SEND_VAR !0 6 DO_ICALL $7 7 ECHO $7 8 ECHO '%3D%3E' 9 INIT_FCALL 'current' 10 SEND_VAR !0 11 DO_ICALL $8 12 ECHO $8 13 ECHO '%0A' 14 > JMP ->2 15 > FE_FREE $4 7 16 > RETURN 1 branch: # 0; line: 2- 4; sop: 0; eop: 1; out1: 2; out2: 15 branch: # 2; line: 4- 4; sop: 2; eop: 2; out1: 3; out2: 15 branch: # 3; line: 4- 5; sop: 3; eop: 14; out1: 2 branch: # 15; line: 5- 7; sop: 15; eop: 16; out1: -2 path #1: 0, 2, 3, 2, 15, path #2: 0, 2, 15, path #3: 0, 15, 0=>a 0=>a 0=>a
[x] foreach
循環對數組內部指針不再起作用, 在PHP7之前, 當數據通過foreach迭代時, 數組指針會移動。
<?php $array = [0, 1, 2]; foreach ($array as &$val) { var_dump(current($array)); }
版本 | 結果 | 說明 |
---|---|---|
PHP5 | int(1) int(2) bool(false) | 數組指針會移動 |
PHP7 | int(0) int(0) int(0) | 數據指針不再移動 |
[x] 按照值進行循環時, 對數組的修改是不會影響循環。
foreach
按照值進行循環的時候(by-value), foreach是對該數組的一個拷貝進行操作. 所以在循環過程中修改不影響循環結果
<?php $arr = [0, 1, 2]; $ref = &$arr; foreach ($arr as $val) { var_dump($val); unset($arr[1]); }
版本 | 結果 | 說明 |
---|---|---|
PHP5 | int(0) int(2) | 會將unset的數據跳過 |
PHP7 | int(0) int(1) int(2) | 對數組的改動不影響循環 |
[x] 按照引用進行循環的時候, 對數組的修改會影響循環
<?php $arr = [0, 1, 2]; $ref = &$arr; foreach ($arr as &$val) { var_dump($val); unset($arr[1]); }
版本 | 結果 |
---|---|
PHP5 | int(0) int(2) |
PHP7 | int(0) int(2) |
[x] 對簡單對象plain(non-Traversable)的循環
在簡單對象的循環, 不管是按照值循環還是引用循環, 和按照引用對數組循環的行為是一樣的, 不過對位置的管理會更加精確
[x] 對迭代對象(Traversable objects)對象行為和之前一致
stackoverflow
上面的解釋, Traversable objects is one that implements Iterator or IteratorAggregate interface
如果一個對象實現了 Iterator
或者 IteratorAggregate
接口, 即可稱之為迭代對象
看完上述內容,你們對如何在PHP中使用foreach有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。