Цикл foreach
До сих пор бегун считал круги сам: счётчик $i, условие и шаг. Но часто нужно другое — пройти по готовому набору значений: по ценам товаров в корзине, по оценкам за тест. Для этого в PHP есть цикл foreach.
Представьте ряд коробок вдоль беговой дорожки. В каждой лежит одна вещь — например, цена товара. Бегун идёт вдоль ряда от первой коробки к последней и у каждой получает ксерокопию того, что лежит внутри. Сколько коробок, столько и витков.
Вот сумма цен в корзине:
<?php
$prices = [10, 20, 30];
$total = 0;
foreach ($prices as $price) {
$total += $price;
}
echo $total, "\n"; // 60Давайте разберём по строкам:
$prices = [10, 20, 30];кладёт три цены в одну переменную — это ряд из трёх коробок.$total = 0;заводит сумму.foreach ($prices as $price)проходит по ряду: на каждом витке в$priceпопадает копия очередной цены.$total += $price;прибавляет эту цену к сумме.- После трёх витков
echoпечатает60.
Счётчика, условия и шага здесь нет: foreach сам знает, где ряд начинается и где кончается. Если ряд пустой, то есть [], тело не выполнится ни разу.
Список в квадратных скобках
Запись [10, 20, 30] создаёт список — набор значений, которые хранятся по порядку. Каждое значение в нём называют элементом.
Элементы пронумерованы с нуля: у первого номер 0, у второго — 1, у третьего — 2. Номер элемента называют индексом. В PHP список — частный случай массива, то есть набора значений в одной переменной.
Элемент читают по индексу в квадратных скобках, а функция count возвращает число элементов:
echo $prices[0], ' ', $prices[2], "\n"; // 10 30
echo count($prices), "\n"; // 3Обратите внимание: элементов три, а последний индекс — 2. Индекс последнего элемента всегда на единицу меньше, чем count.
Тот же обход можно записать и через for с индексом: for ($i = 0; $i < count($prices); $i++) и $prices[$i] в теле. foreach короче и не даёт ошибиться в границах: не нужно помнить, что индексы идут от 0 до count минус один.
Переменная цикла — копия
На каждом витке foreach кладёт в $price копию элемента, а не сам элемент. Поэтому присваивание переменной $price список не меняет:
foreach ($prices as $price) {
$price = $price * 2;
}
echo $prices[0], ' ', $prices[1], "\n"; // 10 20
echo $price, "\n"; // 60Разберём, что произошло:
- На каждом витке
$priceполучает копию:10, потом20, потом30. $price = $price * 2;удваивает копию, а коробка в ряду остаётся прежней.- После цикла в списке по-прежнему
10,20и30. $priceживёт и после цикла, как$iвfor: в ней последняя удвоенная копия,60.
На собеседовании любят спрашивать, меняет ли foreach элементы, если присвоить что-то переменной цикла. Ответ: нет, переменная цикла — копия, и список остаётся прежним.
Картинка с ксерокопией верна, пока в цикле нет знака &. Запись foreach ($prices as &$price) отдаёт в цикл саму коробку, а не копию, и тогда присваивание меняет элемент. После такого цикла $price остаётся привязанной к последнему элементу списка; в этом уроке запись не нужна.
Частая ошибка
По строке пытаются пройти циклом foreach, чтобы получить её буквы:
$word = 'php';
foreach ($word as $letter) { // ✗
echo $letter, "\n";
}Warning: foreach() argument must be of type array|object, string givenСтрока — не список, и foreach по ней не ходит. В сообщении сказано, что цикл принимает array или object, то есть массив или объект, а получил string.
Это предупреждение, а не остановка: тело не выполняется ни разу, а код после цикла работает дальше. Чтобы пройти по буквам, их кладут в список: foreach (['p', 'h', 'p'] as $letter).
Главное
[10, 20, 30]— список: элементы хранятся по порядку, индексы начинаются с0, аcountвозвращает число элементов.foreach ($prices as $price)проходит по всем элементам по порядку, без счётчика и условия.- Переменная цикла — копия элемента: присваивание ей список не меняет.
- По строке
foreachне ходит: PHP выдаётWarning, и тело не выполняется.