招聘
标簽(空格分隔): 招聘 PHP 國貿
語言基礎
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 雖然簡單, 不過它可能出現一些意外行為, 特别是代碼涉及到引用的時候。
問題研究
問題一: 如下代碼運作結果為何不是 2/4/6 ?
<?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 | 由于 是一個引用, 是以 , 相當于 | [2, 2, 3] |
循環 1-2 | | [2, 4, 3] |
循環 1-3 | | [2, 4, 6] |
循環 2-1 | 隐含操作 被觸發, 由于此時 仍是 的引用, 相當于 | [2, 4, 2] |
循環 2-2 | , 即 | [2, 4, 4] |
循環 2-3 | , 即 | [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掉這些引用。
問題二: 如下代碼運作結果為何不是 0=>a 1=>b 2=>c
<?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
PHP7新特性之foreach
- [x]
循環對數組内部指針不再起作用, 在PHP7之前, 當資料通過foreach疊代時, 數組指針會移動。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
接口, 即可稱之為疊代對象
參考
- https://wiki.php.net/rfc/php7...
- 97fe15db4356f8fa1b3b8eb9bb1baa8141376077