PHP foreach(): недопустимый аргумент - почему массив пустой и как это исправить

mr. Cooper 1 час назад Веб-разработка
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() ?? [] закрывает большинство случаев с нулевыми значениями.

Комментарии

Пока нет комментариев. Будьте первым, кто напишет.

Чтобы оставить комментарий, войдите в аккаунт.

Похожие статьи