языковая основа
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;
}
Предупреждение: последний элемент массива
$value
цитируется вforeach
Все еще остается после цикла. Рекомендуется использоватьunset()
уничтожить его.
Note:
foreach
не поддерживается@
способность подавлять дезинформацию
Foreach прост, но может иметь неожиданное поведение, особенно когда в коде используются ссылки.
исследование проблемы
Вопрос 1: Почему результат выполнения следующего кода не 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
:
цикл | Примечание | $возвратное значение |
---|---|---|
Цикл 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 есть напоминание:
Предупреждение: ссылка $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
*/
Как вы можете видеть из этого вопроса, ссылки могут иметь побочные эффекты. Если вы не хотите изменять содержимое данных из-за непреднамеренной модификации, лучше всего вовремя отменить эти ссылки.
Вопрос 2: Почему результат выполнения следующего кода не 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)
Как насчет «А» все время?
Сначала используйте 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
Foreach, новая функция PHP7
- [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) | пропустит неустановленные данные |
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] цикл над простыми объектами (непроходимый)
Зацикливание на простых объектах, будь то цикл по значению или цикл по ссылке, ведет себя так же, как цикл по массиву по ссылке, но с более точным управлением позицией.
- [x] То же поведение, что и раньше для объектов Traversable
stackoverflow
Приведенное выше объяснение, Traversable Objects реализует интерфейс Iterator или IteratorAggGRegate.
Если объект реализуетIterator
илиIteratorAggregate
интерфейс, который можно назвать итеративным объектом
Ссылаться на
- https://wiki.php.net/rfc/php7_foreach
- 97fe15db4356f8fa1b3b8eb9bb1baa8141376077