foreach ($array as $value) выполняет блок по одному разу для каждого элемента, а $value содержит текущий элемент. Чтобы получить и ключ, пишите foreach ($array as $key => $value).
foreach работает с любым массивом, какие бы у него ни были ключи, и вам не нужно вести счётчик или проверять длину. Это цикл, который вы будете писать в PHP чаще всего.
Обход ключей и значений
В $key => $value имена выбираете вы: $name => $age, $id => $user, $k => $v. В списке ключи это позиции 0, 1, 2. В ассоциативном массиве это строковые ключи, которые вы задали.
Последний цикл показывает деталь, которая удивляет: строковый ключ, похожий на целое число, например "1", хранится как целое 1. "01" не является каноническим целым, поэтому остаётся строкой.
Индекс в foreach
В списке ключ и есть индекс, так что достаточно foreach ($items as $i => $item). В ассоциативном массиве числовой позиции нет, поэтому заведите счётчик. Чтобы обработать первый или последний элемент особым образом, сравнивайте ключ с array_key_first() или array_key_last().
Последний цикл ставит запятую между именами, но не после последнего. Для ровно такой задачи короче implode(', ', array_keys($scores)); проверка через array_key_last() нужна, когда последнему проходу требуется другая логика, а не просто другой разделитель.
Изменение значений массива в foreach (по ссылке)
$value это копия. Присваивание ей ничего не меняет в массиве. Чтобы изменить массив, проходите по ссылке через &$value или записывайте обратно через ключ.
Работают оба способа. У варианта с ключом после цикла не остаётся скрытого состояния, поэтому многие команды предпочитают его. Если нужно построить новый массив, а не менять текущий, есть ещё array_map().
Похожий вопрос: что будет, если добавлять элементы во время обхода? Обычный foreach проходит массив в том виде, в каком он был в начале цикла, поэтому $prices[] = ... внутри него не создаёт лишних проходов. Цикл по ссылке проходит живой массив и новые элементы посещает.
Ловушка с unset() при foreach по ссылке
После foreach ($arr as &$v) переменная $v всё ещё ссылается на последний элемент массива. Если позже использовать имя $v в обычном цикле, каждый проход будет записывать в этот последний элемент. Запустите и посмотрите на последний элемент:
Второй цикл присваивает $letters[2] сначала A, потом B, а потом собственное значение последнего элемента, а к последнему проходу в этой ячейке уже лежит B. Исправление занимает одну строку: ставьте unset($v); сразу после каждого foreach по ссылке.
break и continue в foreach
break выходит из цикла, continue переходит к следующему элементу. Частый шаблон это поиск: идти по циклу, пока не найдётся нужное, а потом break, чтобы остаток массива не проверялся.
Во вложенных циклах break 2 выходит из внутреннего цикла и из того, что вокруг него, а continue 2 переходит к следующему проходу внешнего цикла.
Вложенные массивы и foreach с list()
Строки данных это массивы внутри массива. Обходите строки, а потом каждую строку, или вытаскивайте поля прямо в строке foreach деструктуризацией: [$a, $b] для списков, ['key' => $var] для ассоциативных строк. list($a, $b) это старое написание [$a, $b].
На странице о деструктуризации подробнее о синтаксисе [...] =, включая пропуск элементов и вложенные шаблоны.
HTML-список или таблица через foreach
В шаблоне форма с двоеточием foreach (...): и endforeach; сохраняет читаемость цикла между тегами. Этот блок собирает таблицу, а заголовки столбцов это ссылки: нажмите на один во вкладке Page, и тот же скрипт выполнится снова с заданным $_GET['sort'], и строки вернутся в новом порядке.
Две привычки из этого блока стоит перенять: значение sort из URL сверяется со списком разрешённых столбцов до использования, а каждое значение, которое попадает в HTML и может содержать пользовательский текст, проходит через htmlspecialchars().
foreach по null, пустому массиву или объекту
Пустой массив не проблема: тело просто не выполнится. А null, строка или число проблема: PHP печатает предупреждение и пропускает цикл. Для $tags = null; foreach ($tags as $tag) выводится:
Warning: foreach() argument must be of type array|object, null given in /var/www/html/index.php on line 4
Чаще всего это случается с данными, которых иногда нет, например с необязательным полем в декодированном JSON. Подставьте пустой массив через ??:
foreach ($data['tags'] ?? [] as $tag) {
echo $tag;
}
object в этом сообщении не случаен: foreach по объекту проходит его свойства, видимые из места, где выполняется цикл (снаружи класса это публичные), а ключом служит имя свойства. Классы, реализующие Iterator или IteratorAggregate, и генераторы сами решают, что увидит foreach.
foreach, for или array_map
| Что | Когда |
|---|---|
foreach | Нужен каждый элемент, с ключом или без. Работает с любым массивом, списком или ассоциативным. |
for | Вы считаете (от 1 до 100, каждый второй элемент) или нужны соседи по индексу ($a[$i - 1]). Смотрите цикл for. |
array_map() | Вы строите новый массив, преобразуя каждый элемент, часто в одну строку с fn. |
array_filter() | Вы оставляете только элементы, прошедшие проверку. |
Быстрый способ решить: если тело цикла только вычисляет новое значение для каждого элемента, array_map(fn($x) => $x * 2, $a) скажет это одной строкой. Если в нём есть побочные эффекты (вывод, сохранение, подсчёт нескольких вещей сразу) или нужен break, пишите foreach.
Часто задаваемые вопросы
Как получить ключ и значение в цикле foreach PHP?
Используйте форму с =>: foreach ($array as $key => $value) { echo "$key: $value"; }. В обычном списке ключ это индекс (0, 1, 2); в ассоциативном массиве это строковый ключ, например 'name'.
Как получить индекс в цикле foreach PHP?
В списке ключ уже является индексом: foreach ($items as $i => $item). Для ассоциативного массива заведите свой счётчик: $i = 0; перед циклом и $i++ в конце каждого прохода. array_key_first() и array_key_last() подскажут, какой ключ первый, а какой последний.
Как изменить значения массива внутри foreach в PHP?
Проходите по ссылке через &: foreach ($prices as &$price) { $price *= 2; } unset($price);. Или записывайте через ключ: foreach ($prices as $key => $price) { $prices[$key] = $price * 2; }. Простое $price = ... меняет только копию.
Почему foreach по ссылке в PHP дублирует последний элемент?
После foreach ($arr as &$v) переменная $v всё ещё ссылается на последний элемент. Второй foreach ($arr as $v) записывает каждое значение в эту последнюю ячейку, и в итоге там повторяется предпоследнее значение. Вызывайте unset($v); сразу после цикла по ссылке.
Как выйти из цикла foreach в PHP?
break; сразу останавливает цикл, а continue; переходит к следующему элементу. Во вложенных циклах break 2; выходит и из внутреннего, и из внешнего цикла.