Avatar ·

Ошибка «Invalid argument supplied for foreach()»: причины и как исправить

Всем привет! Я новичок в PHP и пишу простой скрипт для вывода списка пользователей. У меня есть функция, которая получает данные из базы данных и возвращает их в виде массива. Когда я пытаюсь перебрать результат через foreach, получаю ошибку:
Warning: Invalid argument supplied for foreach() in /var/www/test.php on line 15
Вот мой код:
function getUsers() {
    $result = mysql_query("SELECT * FROM users");
    return $result; // Я думал, что это массив
}

$users = getUsers();
foreach ($users as $user) {
    echo $user['name'] . '
'; }
Я ожидал, что $users будет содержать массив строк из таблицы, но вместо этого получаю ошибку. Подскажите, что я делаю не так? Как правильно организовать перебор результата запроса? Заранее спасибо! <<>>

Почему возникает ошибка

Ошибка «Invalid argument supplied for foreach()» возникает, когда вы пытаетесь передать в конструкцию foreach переменную, которая не является массивом или объектом, реализующим интерфейс Traversable. В вашем случае проблема в том, что mysql_query() возвращает ресурс (resource), а не массив. Ресурс — это специальный тип данных в PHP, который ссылается на внешний ресурс (в данном случае — результат SQL-запроса). foreach не умеет работать с ресурсами напрямую.

Как правильно решить проблему

Вам нужно извлечь данные из ресурса с помощью функций вроде mysql_fetch_assoc() (или mysqli_fetch_assoc(), если вы используете современное расширение). Вот исправленный пример с использованием устаревшего расширения mysql_* (для образовательных целей):

function getUsers() {
    $result = mysql_query("SELECT * FROM users");
    $users = array(); // Создаём пустой массив
    while ($row = mysql_fetch_assoc($result)) {
        $users[] = $row; // Добавляем каждую строку в массив
    }
    return $users; // Теперь возвращаем массив
}

$users = getUsers();
foreach ($users as $user) {
    echo $user['name'] . '
'; }

Однако настоятельно рекомендую перейти на современные расширения: mysqli или PDO. Расширение mysql_* удалено из PHP 7.0 и выше. Вот пример с mysqli (объектно-ориентированный стиль):

function getUsers() {
    $mysqli = new mysqli("localhost", "user", "password", "database");
    if ($mysqli->connect_error) {
        die("Connection failed: " . $mysqli->connect_error);
    }
    $result = $mysqli->query("SELECT * FROM users");
    $users = $result->fetch_all(MYSQLI_ASSOC); // Сразу получаем массив
    $result->free();
    $mysqli->close();
    return $users;
}

Другие типичные причины ошибки

  • Передача null или false: если функция возвращает null или false, а вы пытаетесь её перебрать. Всегда проверяйте тип перед foreach:
if (is_array($data) || $data instanceof Traversable) {
    foreach ($data as $item) { ... }
} else {
    // Обработка ошибки
}
  • Скалярные значения: передача строки, числа или булева значения в foreach.
  • Объекты без Traversable: обычные объекты (например, stdClass) не перебираются через foreach, если не реализуют интерфейс Iterator или IteratorAggregate.

Проверка типа с помощью var_dump

Чтобы отладить, используйте var_dump($users); перед циклом — вы увидите, что на самом деле хранится в переменной. Это сэкономит часы поиска ошибок.

В итоге: всегда убеждайтесь, что аргумент foreach — это массив или объект, который можно итерировать. И откажитесь от устаревших функций mysql_* — они небезопасны и не поддерживаются.

<<>>

Альтернативный подход: использование PDO

PDO (PHP Data Objects) — это современный и безопасный способ работы с базами данных. Он решает проблему «Invalid argument supplied for foreach()» автоматически, так как метод fetchAll() всегда возвращает массив (или пустой массив, если строк нет).

function getUsers() {
    try {
        $pdo = new PDO("mysql:host=localhost;dbname=database", "user", "password");
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $stmt = $pdo->query("SELECT * FROM users");
        return $stmt->fetchAll(PDO::FETCH_ASSOC); // Гарантированно массив
    } catch (PDOException $e) {
        error_log("Database error: " . $e->getMessage());
        return array(); // Возвращаем пустой массив при ошибке
    }
}

$users = getUsers();
if (!empty($users)) {
    foreach ($users as $user) {
        echo htmlspecialchars($user['name']) . '
'; } } else { echo "Нет пользователей или ошибка БД."; }

Преимущество этого подхода: вы никогда не получите ошибку foreach, так как fetchAll() всегда возвращает массив. Кроме того, PDO защищает от SQL-инъекций при использовании подготовленных запросов. Рекомендую всегда использовать try-catch для обработки ошибок и возвращать пустой массив как запасной вариант.