PHP foreach(): недопустимый аргумент - почему массив пустой и как это исправить
PHP-разработчики сталкиваются с этой ошибкой постоянно: цикл foreach не выполняется, в логах мелькает Warning: Invalid argument supplied for foreach(), а данные как будто исчезают. Статья поможет разобраться, откуда берётся проблема, быстро её диагностировать и раз и навсегда закрыть.
Что это такое
Конструкция foreach в PHP предназначена для перебора элементов массива или объекта, реализующего интерфейс Traversable. Если передать что-то другое - PHP выбросит предупреждение:
Warning: Invalid argument supplied for foreach() in /var/www/index.php on line 12В PHP 8+ формулировка изменилась:
Warning: foreach() argument must be of type array|object, null givenСам цикл при этом просто не выполнится - никаких итераций, никаких данных.
Почему возникает
Корень проблемы - в переменной, которую вы передаёте в foreach. Вот самые распространённые сценарии:
Функция вернула null, false или строку вместо массива
Переменная не была инициализирована перед использованием
Данные пришли из API или БД и оказались пустыми или в другом формате
JSON был декодирован в объект, а не в массив (json_decode без второго аргумента)
Переменная перезаписалась в другом месте кода
Запрос к БД вернул false или пустой результат, а не массив строк
Частые ошибки
1. Функция возвращает не массив
// Неправильно: getItems() может вернуть null
foreach (getItems() as $item) { ... }
// Правильно: проверяем перед обходом
$items = getItems();
if (is_array($items)) {
foreach ($items as $item) { ... }
}2. json_decode без второго аргумента
// Возвращает объект stdClass, не массив
$data = json_decode($jsonString);
// Возвращает ассоциативный массив
$data = json_decode($jsonString, true);3. Пустой результат запроса к БД
// $result может быть false или пустым объектом
$result = $db->query("SELECT * FROM users WHERE id = 999");
foreach ($result as $row) { ... }
// Безопасный вариант
$result = $db->query("...");
$rows = $result ? $result->fetch_all(MYSQLI_ASSOC) : [];
foreach ($rows as $row) { ... }4. Необъявленная переменная
// $items нигде не объявлена
foreach ($items as $item) { echo $item; }
// Инициализируем заранее
$items = [];
// ... логика заполнения ...
foreach ($items as $item) { echo $item; }Пошаговая диагностика и исправление
Шаг 1. Добавьте var_dump() прямо перед foreach - убедитесь, что переменная действительно массив:
var_dump($yourVar); // array(3) { ... } или NULL?
die();Шаг 2. Проверьте тип через is_array() перед итерацией:
if (!is_array($data)) {
$data = (array) $data; // конвертируем, если объект
}Шаг 3. Используйте оператор ?? для значения по умолчанию (PHP 7+):
$items = getItems() ?? [];
foreach ($items as $item) { ... }Шаг 4. Для объектов - проверьте Traversable:
if (is_array($data) || $data instanceof Traversable) {
foreach ($data as $item) { ... }
}Шаг 5. Включите строгие ошибки в разработке - ранняя диагностика экономит время:
error_reporting(E_ALL);
ini_set('display_errors', '1');Совет. Никогда не подавляйте ошибку через @foreach(...) - это маскирует баг, а не устраняет его. Всегда разбирайтесь с причиной.
Примеры из реальных сценариев
API-ответ с вложенным ключом
$response = json_decode(file_get_contents($url), true);
// Ошибка: 'items' может отсутствовать в ответе
foreach ($response['items'] as $item) { ... }
// Безопасно: проверяем наличие ключа и тип
$items = $response['items'] ?? [];
foreach ($items as $item) { ... }PDO fetchAll
$stmt = $pdo->prepare("SELECT * FROM products WHERE active = 1");
$stmt->execute();
$products = $stmt->fetchAll(PDO::FETCH_ASSOC); // всегда возвращает массив
foreach ($products as $product) {
echo $product['name'];
}Рекурсивный обход с проверкой
function processTree(array $nodes): void {
foreach ($nodes as $node) {
echo $node['title'];
if (!empty($node['children']) && is_array($node['children'])) {
processTree($node['children']);
}
}
}Часто задаваемые вопросы
Почему foreach молча ничего не делает? Если передать пустой массив [], foreach просто пропускает итерации без ошибок. Проверьте, не пустой ли массив: count($arr) или empty($arr).
Можно ли использовать foreach с объектом? Да, если объект реализует интерфейс Traversable - например, ArrayObject, SplFixedArray или результат PDO-запроса. Обычный stdClass тоже обходится, но только по публичным свойствам.
Как исправить "foreach argument must be of type array|object, null given" в PHP 8? Переменная равна null. Добавьте: $data = $data ?? []; перед foreach или убедитесь, что вызываемая функция объявляет возвращаемый тип array.
В чём разница между is_array() и empty()? is_array() проверяет тип переменной - массив это или нет. empty() проверяет, пуст ли массив (или равна ли переменная нулю, пустой строке, null и т.д.). Для защиты foreach нужен именно is_array().
Как безопасно обойти null-ответ от функции? Используйте оператор нулевого слияния: foreach (getItems() ?? [] as $item) - если функция вернёт null, подставится пустой массив.
Почему json_decode возвращает объект, а не массив? По умолчанию json_decode создаёт объекты stdClass. Передайте второй аргумент true: json_decode($json, true) - и получите ассоциативный массив.
Как избежать ошибки при работе с вложенными массивами? Проверяйте каждый уровень: if (!empty($data['users']) && is_array($data['users'])). Для проверки наличия ключа удобна функция array_key_exists().
Можно ли итерировать строку в foreach? Нет, строка вызовет предупреждение. Если нужно перебрать символы, используйте str_split($string) - это вернёт массив символов.
Лучшие практики
Всегда инициализируйте массивы перед использованием: $items = [];
Типизируйте возвращаемые значения функций: function getUsers(): array
Используйте ?? [] как запасное значение при получении данных извне
В PHP 8 включайте строгие типы: declare(strict_types=1);
Не полагайтесь на неявное приведение типов - будьте явными
Покрывайте функции unit-тестами, которые проверяют крайние случаи: null, пустой массив, некорректный тип
Итог
Ошибка foreach(): invalid argument всегда означает одно - переменная не является массивом или обходимым объектом. Решение: диагностируйте тип через var_dump или is_array, убедитесь, что источник данных (функция, API, БД) действительно возвращает массив, и добавьте защитную проверку перед итерацией. Паттерн getItems() ?? [] закрывает большинство случаев с нулевыми значениями.
Комментарии
Чтобы оставить комментарий, войдите в аккаунт.