Типичные ошибки PDO и методы их предотвращения

Раздел: PHP -> Работа с базами данных в PHP

Ошибки PDO в PHP: методы обработки

PDO (PHP Data Objects) предоставляет единый интерфейс для работы с базами данных. Обработка ошибок имеет решающее значение для стабильности приложения. Рассмотрим основные подходы, начиная с рекомендуемого.

Как обеспечить надёжную обработку ошибок PDO?

Самый эффективный способ - установить режим исключений (ERRMODE_EXCEPTION) и обрабатывать исключения в блоке try/catch. Это позволяет централизованно управлять ошибками и избегать неожиданного завершения скрипта.

try {
    $pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $stmt = $pdo->query('SELECT * FROM non_existent_table');
} catch (PDOException $e) {
    echo 'Ошибка: ' . $e->getMessage();
}

Php pdo sqlsrv (pdo для sql server)

Пояснение: При возникновении ошибки (например, таблица не существует) PDO выбрасывает исключение PDOException. Блок catch перехватывает его, и можно вывести сообщение или записать в лог.

Типичные проблемы: Необработанное исключение (если не используется try/catch) приводит к фатальной ошибке. Также важно правильно указывать DSN и проверять наличие драйвера.

Как подавить вывод ошибок PDO и проверять их вручную?

Режим ERRMODE_SILENT не выводит и не выбрасывает исключения. Ошибки можно проверить методами errorCode() и errorInfo().

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT);
$stmt = $pdo->query('SELECT * FROM missing_table');
if ($stmt === false) {
    $error = $pdo->errorInfo();
    echo 'Код: ' . $error[0] . ', сообщение: ' . $error[2];
}

Php artisan db (команда artisan db в laravel)

Проблемы: Легко забыть проверить результат запроса, и ошибка останется незамеченной. Ручная проверка каждого запроса увеличивает код.

Как получать предупреждения PDO без остановки скрипта?

Режим ERRMODE_WARNING выводит предупреждения (warning), но выполнение продолжается. Полезно для отладки.

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
$stmt = $pdo->query('SELECT * FROM fake_table');
echo 'Скрипт продолжает работу';

Php pdo pgsql (pdo для postgresql)

Проблемы: Предупреждения могут быть скрыты настройками error_reporting, и разработчик их не увидит. Не подходит для продакшена.

Как получить детальную информацию об ошибке PDO после выполнения запроса?

Метод errorInfo() возвращает массив из трёх элементов: SQLSTATE код, код драйвера и сообщение. Можно использовать с любым режимом ошибок.

$stmt = $pdo->prepare('INSERT INTO users (name) VALUES (:name)');
$stmt->execute([':name' => 'test']);
if ($stmt->errorCode() !== '00000') {
    $info = $stmt->errorInfo();
    echo 'SQLSTATE: ' . $info[0] . ', Driver code: ' . $info[1] . ', Message: ' . $info[2];
}

Create table php (создание таблицы в php)

Проблемы: Необходимо проверять errorCode() после каждого execute. Можно пропустить ошибку, если не вызывать errorInfo().

Как обработать неудачное соединение с БД через PDO?

Ошибка подключения - это исключение PDOException, даже если режим ошибок установлен в silent. Рекомендуется оборачивать создание объекта PDO в try/catch.

try {
    $pdo = new PDO('mysql:host=invalid_host;dbname=test', 'user', 'pass');
} catch (PDOException $e) {
    echo 'Не удалось подключиться: ' . $e->getMessage();
}

Select from users php (sql запрос select from users в php)

Проблемы: Неверные учётные данные, недоступный хост, не установлен драйвер PDO. Важно проверять сообщение исключения и не выводить его в продакшене без фильтрации.

Как обрабатывать ошибки PDO внутри транзакций?

При работе с транзакциями ошибки могут нарушить целостность данных. Лучший подход - использовать исключения и откатывать транзакцию в блоке catch.

$pdo->beginTransaction();
try {
    $pdo->exec('UPDATE accounts SET balance = balance - 100 WHERE id = 1');
    $pdo->exec('UPDATE accounts SET balance = balance + 100 WHERE id = 2');
    $pdo->commit();
} catch (PDOException $e) {
    $pdo->rollBack();
    echo 'Транзакция откачена: ' . $e->getMessage();
}

Проблемы: Если забыть rollBack, база может остаться в заблокированном состоянии. Также нужно учитывать, что некоторые ошибки (например, deadlock) требуют повторной попытки.

Общие типичные ошибки PDO:

  • could not find driver - отсутствует расширение PDO для нужной БД (php_pdo_mysql и т.д.).
  • invalid data source name - неправильный формат DSN (например, пропущен двоеточие).
  • SQLSTATE[23000] - нарушение уникальности (дубликат ключа).
  • SQLSTATE[HY000] - общая ошибка драйвера (например, превышение соединений).

Решение: проверять настройки php.ini, правильность DSN, использовать подготовленные запросы для избегания ошибок синтаксиса.

- Php db query (выполнение запроса к базе данных в php)
- Php pdo query (выполнение запросов pdo)
- Php mysqli fetch (функция mysqli_fetch в php)

Расширенные примеры обработки ошибок PDO

Логирование ошибок PDO в файл

В продакшен-среде ошибки не должны показываться пользователю. Удобно записывать их в лог с деталями.

Пример
function handlePdoError(PDOException $e, PDO $pdo = null) {
    $log = date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL;
    if ($pdo) {
        $log .= 'Last query: ' . ($pdo->lastInsertId() ?? 'N/A') . PHP_EOL;
    }
    file_put_contents('/var/log/pdo_errors.log', $log, FILE_APPEND);
}

try {
    $pdo = new PDO('...');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $pdo->exec('INSERT INTO log (message) VALUES ("test")');
} catch (PDOException $e) {
    handlePdoError($e, $pdo ?? null);
    echo 'Произошла внутренняя ошибка.';
}
(Лог-файл будет содержать запись с временем и сообщением об ошибке)

Обработка ошибок при массовой вставке с транзакцией

При вставке нескольких записей в транзакции можно перехватить конкретный SQLSTATE и продолжить.

Пример
$pdo->beginTransaction();
try {
    foreach ($users as $user) {
        $stmt = $pdo->prepare('INSERT INTO users (email) VALUES (:email)');
        $stmt->execute([':email' => $user['email']]);
    }
    $pdo->commit();
} catch (PDOException $e) {
    $pdo->rollBack();
    if ($e->getCode() == 23000) {
        echo 'Обнаружен дубликат email. Проверьте данные.';
    } else {
        echo 'Ошибка базы данных: ' . $e->getMessage();
    }
}
При дубликате: "Обнаружен дубликат email. Проверьте данные."

Использование пользовательского класса исключений

Можно создать собственное исключение для более гибкой обработки.

Пример
class DatabaseException extends \Exception {}

try {
    $pdo = new PDO('...');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $pdo->query('SELECT 1');
} catch (PDOException $e) {
    throw new DatabaseException('Ошибка БД: ' . $e->getMessage(), (int)$e->getCode(), $e);
}
Выбрасывается исключение DatabaseException, которое можно перехватить выше.

Сравнение поведения разных режимов ошибок

Наглядный тест для понимания различий.

Пример
$modes = [
    PDO::ERRMODE_SILENT,
    PDO::ERRMODE_WARNING,
    PDO::ERRMODE_EXCEPTION,
];

foreach ($modes as $mode) {
    $pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
    $pdo->setAttribute(PDO::ATTR_ERRMODE, $mode);
    echo "Режим: $mode\n";
    $stmt = $pdo->query('SELECT * FROM fake');
    if ($stmt === false) echo 'Запрос вернул false' . PHP_EOL;
    echo '---' . PHP_EOL;
}
Режим: 0 (silent) - вывод "Запрос вернул false"
Режим: 1 (warning) - предупреждение и "Запрос вернул false"
Режим: 2 (exception) - фатальная ошибка, скрипт прерывается

Обработка ошибок с учётом SQLSTATE кода

Разные ошибки требуют разной реакции.

Пример
try {
    $stmt = $pdo->prepare('INSERT INTO users (login) VALUES (:login)');
    $stmt->execute([':login' => $login]);
} catch (PDOException $e) {
    switch ($e->getCode()) {
        case '23000':
            echo 'Логин уже занят.';
            break;
        case '22001':
            echo 'Слишком длинное значение.';
            break;
        default:
            echo 'Неизвестная ошибка: ' . $e->getMessage();
    }
}
При дубликате: "Логин уже занят."

Ошибки PDO - comments

En
Php pdo error (php)