Проблемы подключения 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 3306tcp6 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.