Цикл foreach: синтаксис и примеры применения в PHP
Цикл foreach в PHP предназначен для обхода элементов массивов и объектов, реализующих интерфейс Traversable. Он является одним из самых удобных и безопасных способов перебора, так как не требует управления счётчиком или проверки длины. Конструкция автоматически проходит по каждому элементу и завершается, когда элементы заканчиваются.
Основной синтаксис и базовое решение
Наиболее эффективный и простой способ - использовать foreach с одной переменной для значения:
$colors = ['red', 'green', 'blue'];
foreach ($colors as $color) {
echo $color . ' ';
}red green blue
Здесь на каждой итерации переменной $color присваивается текущий элемент массива. После завершения цикла переменная $color содержит последнее значение. Эта форма подходит для любого массива, если не нужны ключи.
Различные варианты применения
Как перебрать массив, получая ключи и значения?
Используется вторая форма foreach с указанием переменной для ключа:
$ages = ['Иван' => 25, 'Мария' => 30, 'Пётр' => 28];
foreach ($ages as $name => $age) {
echo "$name: $age лет
";
}Иван: 25 лет
Мария: 30 лет
Пётр: 28 лет
Типичная ошибка:
При попытке изменить элемент массива через переменную значения изменения не сохраняются после цикла. Например:
$nums = [1, 2, 3];
foreach ($nums as $n) {
$n *= 2;
}
print_r($nums);Array ( [0] => 1 [1] => 2 [2] => 3 )
Чтобы изменить исходный массив, нужно использовать ссылку &$value.
Как изменять элементы массива во время обхода?
Применяется синтаксис с амперсандом перед переменной значения:
$numbers = [10, 20, 30];
foreach ($numbers as &$num) {
$num *= 2;
}
unset($num); // важно удалить ссылку после цикла
print_r($numbers);Array ( [0] => 20 [1] => 40 [2] => 60 )
Проблема:
После цикла переменная $num остаётся ссылкой на последний элемент. Последующее использование этой переменной может непреднамеренно изменить массив. Всегда стоит вызывать unset() после цикла с ссылкой.
Как обойти вложенные массивы (массивы массивов)?
Для многомерных массивов можно использовать foreach внутри другого foreach или применить синтаксис list():
$matrix = [
[1, 2],
[3, 4],
];
foreach ($matrix as $row) {
foreach ($row as $val) {
echo $val . ' ';
}
echo '
';
}1 2
3 4
В PHP 7.0+ доступен синтаксис с деструктуризацией:
$pairs = [['a', 1], ['b', 2]];
foreach ($pairs as [$letter, $number]) {
echo "$letter => $number
";
}a => 1
b => 2
Как прервать или продолжить итерацию?
Внутри foreach работают операторы break и continue:
$items = [1, 2, 0, 4, 5];
foreach ($items as $item) {
if ($item === 0) {
break;
}
echo $item . ' ';
}1 2
continue пропускает текущую итерацию:
foreach ([1, 2, 3] as $v) {
if ($v === 2) continue;
echo $v . ' ';
}1 3
Как перебрать объект?
Цикл foreach также работает с публичными свойствами объекта (если объект не реализует Iterator):
class User {
public $name = 'John';
public $email = 'john@example.com';
}
$user = new User();
foreach ($user as $key => $value) {
echo "$key: $value
";
}name: John
email: john@example.com
Для кастомной логики итерации объект должен реализовать интерфейс Iterator.
Как обойти массив без использования переменной значения?
Иногда требуется только ключи, а значение не нужно. Можно опустить переменную значения:
$arr = ['x', 'y'];
foreach ($arr as $k => $v) {
// используется только $k
}
// Альтернатива - array_keys() и foreach по ключамЕсли переменная значения не нужна, можно использовать foreach ($arr as $k => $v) и просто не обращаться к $v. Удалить её нельзя, но это не вызывает ошибок.
Расширенные примеры использования
Рассмотрим более сложные сценарии, где foreach проявляет свою гибкость.
Пример 1. Обход с одновременной фильтрацией с помощью continue
$numbers = [5, 12, 3, 8, 15, 7];
$filtered = [];
foreach ($numbers as $n) {
if ($n % 2 === 0) {
continue;
}
if ($n > 10) {
$filtered[] = $n . '-большой';
} else {
$filtered[] = $n . '-маленький';
}
}
print_r($filtered);Array ( [0] => 5-маленький [1] => 3-маленький [2] => 15-большой [3] => 7-маленький )
Пример 2. Использование foreach с ArrayIterator
$array = ['apple', 'banana', 'cherry'];
$iterator = new ArrayIterator($array);
foreach ($iterator as $index => $fruit) {
echo "$index: $fruit\n";
}0: apple 1: banana 2: cherry
ArrayIterator позволяет менять позицию итератора, поддерживать обратный проход и другие операции.
Пример 3. Обход генератора с помощью foreach
function generateSquares($limit) {
for ($i = 1; $i <= $limit; $i++) {
yield $i * $i;
}
}
foreach (generateSquares(5) as $square) {
echo $square . ' ';
}1 4 9 16 25
Генераторы возвращают объект Generator, который реализует Iterator, поэтому его можно использовать в foreach.
Пример 4. Рекурсивный обход многомерного массива
$tree = [
'name' => 'root',
'children' => [
['name' => 'child1', 'children' => []],
['name' => 'child2', 'children' => [
['name' => 'grandchild', 'children' => []]
]]
]
];
function traverse(array $node, $depth = 0) {
echo str_repeat('-', $depth) . $node['name'] . '\n';
foreach ($node['children'] as $child) {
traverse($child, $depth + 1);
}
}
traverse($tree);root -child1 -child2 --grandchild
Пример 5. Обход с использованием list() и вложенного foreach для CSV-подобных данных
$data = [
['John', 25, 'USA'],
['Anna', 30, 'UK'],
];
foreach ($data as list($name, $age, $country)) {
echo "$name ($age) from $country\n";
}John (25) from USA Anna (30) from UK
В PHP 7.1+ list() можно заменить на квадратные скобки [$name, $age, $country].
Пример 6. Обход с изменением ключей и значений через ссылку на ключ (только для объектов)
Для массивов нельзя изменить ключ напрямую, но можно создать новый массив:
$array = ['a' => 1, 'b' => 2, 'c' => 3];
$new = [];
foreach ($array as $k => $v) {
$new[strtoupper($k)] = $v * 10;
}
print_r($new);Array ( [A] => 10 [B] => 20 [C] => 30 )
Пример 7. Использование шаблона «foreach с последующей обработкой» (вызов callback)
$names = ['alice', 'bob', 'charlie'];
$result = [];
foreach ($names as &$name) {
$name = ucfirst($name);
}
unset($name);
array_walk($names, function($val) use (&$result) {
$result[] = "Hello, $val!";
});
print_r($result);Array ( [0] => Hello, Alice! [1] => Hello, Bob! [2] => Hello, Charlie! )
Пример 8. Обход объекта DirectoryIterator (файлы в папке)
$dir = new DirectoryIterator('.');
foreach ($dir as $fileInfo) {
if ($fileInfo->isDot()) continue;
echo $fileInfo->getFilename() . ' (' . $fileInfo->getSize() . ' bytes)\n';
}file1.txt (1024 bytes) image.png (20480 bytes) ...
DirectoryIterator реализует Iterator и может быть использован в foreach.