Проблемы подключения PHP к СУБД: диагностика и устранение

Раздел: Базы данных -> Ошибки баз данных

Диагностика и решение проблемы подключения PHP к базе данных

Наиболее эффективным способом обработки ошибок соединения в PHP является использование расширения PDO с включённым режимом исключений. Такой подход позволяет перехватывать любые сбои подключения и реагировать на них единообразно вне зависимости от используемой СУБД.

Пример базового подключения с PDO:

<?php
$dsn = 'mysql:host=localhost;dbname=testdb;charset=utf8';
$user = 'user';
$pass = 'password';
$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
];
try {
    $pdo = new PDO($dsn, $user, $pass, $options);
    echo 'Подключение успешно';
} catch (PDOException $e) {
    echo 'Ошибка подключения: ' . $e->getMessage();
}
?>

Пояснение: В блоке try выполняется создание объекта PDO. Если подключение невозможно, генерируется исключение PDOException. В блоке catch выводится сообщение об ошибке. Использование константы PDO::ERRMODE_EXCEPTION гарантирует, что любые проблемы с БД будут вызывать исключение.

Типичные проблемы: неверные учётные данные (логин/пароль, имя базы данных), недоступный хост, несоответствие драйвера (не установлено расширение pdo_mysql или pdo_pgsql). Решение - проверить наличие расширения через phpinfo() и правильность DSN.

Как вывести детали ошибки при использовании mysqli?

При работе с расширением mysqli можно использовать функции mysqli_connect_errno() и mysqli_connect_error() для получения кода и текста ошибки.

<?php
$link = @mysqli_connect('localhost', 'user', 'pass', 'db');
if (!$link) {
    echo 'Код ошибки: ' . mysqli_connect_errno() . ' - ' . mysqli_connect_error();
    exit;
}
echo 'Подключение выполнено';
mysqli_close($link);
?>

Проблема: символ @ подавляет вывод ошибок, что может затруднить отладку. Лучше использовать mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT) для автоматического выброса исключений.

Как настроить таймаут соединения в PDO?

Параметр PDO::ATTR_TIMEOUT позволяет задать время ожидания подключения (в секундах). Это полезно при медленных сетевых подключениях.

<?php
$options = [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_TIMEOUT => 5, // 5 секунд
];
$pdo = new PDO('mysql:host=remote.example.com;dbname=test', 'user', 'pass', $options);
?>

Замечание: не все драйверы поддерживают этот атрибут. Например, для MySQL драйвера значение таймаута может игнорироваться, если не настроено в самом MySQL. Альтернатива - установка таймаута через DSN: mysql:host=...;connect_timeout=5.

Что делать, если возникает ошибка 'Connection refused'?

Ошибка указывает, что сервер БД не принимает соединения. Причины: сервер не запущен, порт занят, файрвол блокирует порт. Проверка статуса MySQL:

$ sudo systemctl status mysql
$ sudo netstat -tlnp | grep 3306
tcp6       0      0 :::3306                 :::*                    LISTEN      1234/mysqld

Если порт не слушается, запустите сервер: sudo systemctl start mysql.

Проблема: может быть неверно указан хост или порт в DSN. Убедитесь, что host соответствует адресу сервера (например, 127.0.0.1 для локального, а не localhost).

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

Ошибка 'Access denied for user' - пользователь не имеет прав на подключение с данного хоста или к указанной базе. Проверка прав через MySQL:

mysql> SELECT user, host FROM mysql.user WHERE user='myuser';
mysql> SHOW GRANTS FOR 'myuser'@'localhost';

Решение: предоставить необходимые привилегии: GRANT ALL PRIVILEGES ON db.* TO 'myuser'@'localhost' IDENTIFIED BY 'pass'; FLUSH PRIVILEGES;. Или изменить хост: 'myuser'@'%' для всех хостов.

Как использовать исключения в mysqli для перехвата ошибок?

Включение режима строгих отчётов позволяет mysqli выбрасывать исключения mysqli_sql_exception.

<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
try {
    $mysqli = new mysqli('localhost', 'user', 'pass', 'db');
    echo 'Подключение успешно';
} catch (mysqli_sql_exception $e) {
    echo 'Ошибка: ' . $e->getMessage();
}
?>

Внимание: старые версии PHP (ниже 5.4) не поддерживают класс mysqli_sql_exception. Рекомендуется обновить PHP до актуальной версии.

Расширенные примеры и сценарии работы с ошибками соединения

Пример 1: Обработка разных типов ошибок PDO

В одном блоке можно различать ошибки подключения и ошибки выполнения запроса:

Пример
<?php
try {
    $pdo = new PDO('mysql:host=localhost;dbname=test', 'invalid', 'wrong');
} catch (PDOException $e) {
    echo 'Ошибка подключения: ' . $e->getMessage() . PHP_EOL;
    // Логирование ошибки
    error_log('PDO connection error: ' . $e->getMessage());
    exit(1);
}
try {
    $stmt = $pdo->query('SELECT * FROM non_existent_table');
} catch (PDOException $e) {
    echo 'Ошибка запроса: ' . $e->getMessage();
}
?>
Ошибка подключения: SQLSTATE[HY000] [1045] Access denied for user 'invalid'@'localhost' (using password: YES)

Пример 2: Подключение к PostgreSQL через PDO

Для PostgreSQL DSN выглядит иначе:

Пример
<?php
$dsn = 'pgsql:host=localhost;port=5432;dbname=testdb;user=postgres;password=secret';
$pdo = new PDO($dsn);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$result = $pdo->query('SELECT version()');
foreach ($result as $row) echo $row['version'];
?>
PostgreSQL 15.0 on x86_64-linux-gnu

Пример 3: Автоматический повтор подключения при временной ошибке

Полезно при кратковременном отключении сети:

Пример
<?php
$maxAttempts = 3;
$attempt = 0;
while ($attempt < $maxAttempts) {
    try {
        $pdo = new PDO('mysql:host=192.168.1.10;dbname=test', 'user', 'pass', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_TIMEOUT => 2
        ]);
        echo 'Подключение установлено';
        break;
    } catch (PDOException $e) {
        $attempt++;
        if ($attempt === $maxAttempts) {
            throw $e;
        }
        sleep(1); // пауза 1 секунда
    }
}
?>

Пример 4: Чтение конфигурации из файла и проверка драйверов

Пример
<?php
$config = parse_ini_file('config.ini');
$dsn = sprintf('%s:host=%s;dbname=%s', $config['driver'], $config['host'], $config['dbname']);
// Проверка наличия драйвера
$availableDrivers = PDO::getAvailableDrivers();
if (!in_array($config['driver'], $availableDrivers)) {
    echo 'Драйвер ' . $config['driver'] . ' не установлен. Доступны: ' . implode(', ', $availableDrivers);
    exit;
}
$pdo = new PDO($dsn, $config['user'], $config['password']);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// далее работа с БД
?>
Драйвер mysql не установлен. Доступны: pgsql, sqlite

Пример 5: Код ошибки 2002 (Connection refused) с альтернативой через сокет

При использовании MySQL через Unix-сокет вместо TCP:

Пример
<?php
// Использование сокета (обычно /var/run/mysqld/mysqld.sock)
$dsn = 'mysql:unix_socket=/var/run/mysqld/mysqld.sock;dbname=test';
$pdo = new PDO($dsn, 'user', 'pass', [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
echo 'Подключение через сокет успешно';
?>

Возможная ошибка: сокет может отсутствовать, если MySQL не настроен на сокет или используется другой путь. Проверка через phpinfo() - раздел pdo_mysql - строка 'Client API version' или команда mysql_config --socket.

Ошибка соединения PHP - comments

En
Connect error php (php)