Отладка ошибок MySQL в PHP классе Engine: практические советы

Раздел: Отладка PHP приложений -> Обработка ошибок MySQL

Отладка ошибок MySQL в классе Engine

Как надёжно обрабатывать ошибки MySQL в классе Engine с использованием PDO?

Наиболее эффективным решением является перевод PDO в режим исключений и перехват их в блоках try / catch. Это позволяет унифицировать обработку всех ошибок базы данных, включая ошибки соединения, синтаксиса запросов и нарушения ограничений.

class Engine {
    private $pdo;

    public function __construct($dsn, $user, $pass) {
        try {
            $this->pdo = new PDO($dsn, $user, $pass);
            $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        } catch (PDOException $e) {
            // Логирование и корректный выход
            error_log('Engine connection error: ' . $e->getMessage());
            throw new RuntimeException('Database connection failed');
        }
    }

    public function query($sql) {
        try {
            return $this->pdo->query($sql);
        } catch (PDOException $e) {
            error_log('Engine query error: ' . $e->getMessage());
            throw new RuntimeException('Query execution error');
        }
    }
}

Engine classes mysql php at line (ошибка в классе engine при работе с mysql в php)

При таком подходе все ошибки MySQL преобразуются в исключения, которые можно обрабатывать централизованно. Это упрощает отладку и предотвращает неожиданное завершение скрипта.

Возможные проблемы:

  • Исключение может быть перехвачено слишком рано и скрыть реальную причину сбоя. Рекомендуется логировать полное сообщение, а пользователю показывать общую информацию.
  • Если в коде используется @ для подавления ошибок, исключения PDO могут не сработать. Следует избегать оператора подавления в методах работы с БД.
  • При использовании транзакций исключение может оставить транзакцию открытой. Необходимо в catch выполнять откат.

Как использовать mysqli с проверкой ошибок в классе Engine?

Альтернативой PDO является расширение mysqli, которое предоставляет методы error и errno для получения информации об ошибке. Пример реализации:

class Engine {
    private $mysqli;

    public function __construct($host, $user, $pass, $db) {
        $this->mysqli = new mysqli($host, $user, $pass, $db);
        if ($this->mysqli->connect_errno) {
            throw new RuntimeException('Connect error: ' . $this->mysqli->connect_error);
        }
    }

    public function query($sql) {
        $result = $this->mysqli->query($sql);
        if (!$result) {
            error_log('MySQL error: ' . $this->mysqli->error);
            throw new RuntimeException('Query error [' . $this->mysqli->errno . ']');
        }
        return $result;
    }
}

Php mysql error (ошибка php mysql)

Этот вариант подходит, когда проект уже использует mysqli и нет необходимости переходить на PDO. Однако он менее гибкий в плане подготовки запросов и работы с разными СУБД.

Типичные ошибки:

  • Забывают проверять connect_errno после создания соединения. Это приводит к неопределённому поведению.
  • Использование error без проверки наличия ошибки – при успешном запросе свойство может содержать пустую строку.

Как получить полный контекст ошибки с помощью debug_backtrace?

Иногда требуется узнать не только текст ошибки, но и стек вызовов, приведший к ней. Это полезно при сложной архитектуре класса Engine с наследованием или цепочками вызовов.

class Engine {
    public function query($sql) {
        try {
            // ... выполнение запроса
        } catch (PDOException $e) {
            $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
            $context = [
                'message' => $e->getMessage(),
                'sql' => $sql,
                'trace' => $trace
            ];
            error_log(json_encode($context));
            throw $e; // или собственное исключение
        }
    }
}

Такой подход позволяет сохранить в логе полную картину сбоя. Однако debug_backtrace потребляет ресурсы, поэтому его стоит применять только в отладочном режиме или при логировании критических ошибок.

Проблемы:

  • Большой объём данных может забить лог. Следует ограничивать глубину или использовать флаг DEBUG_BACKTRACE_IGNORE_ARGS.
  • В production-среде вывод полного стека может раскрыть внутреннюю структуру приложения. Не показывайте его пользователю.

Расширенные примеры отладки ошибок MySQL в классе Engine

Ниже приведены подробные примеры, демонстрирующие различные сценарии возникновения ошибок и способы их обработки.

Пример 1: Обработка ошибки дублирования ключа в PDO

Пример
class Engine {
    private $pdo;

    public function __construct() {
        $this->pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
        $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }

    public function insertUser($email) {
        try {
            $stmt = $this->pdo->prepare('INSERT INTO users (email) VALUES (:email)');
            $stmt->execute([':email' => $email]);
        } catch (PDOException $e) {
            if ($e->getCode() == 23000) { // Нарушение уникальности
                // Специфичная обработка
                error_log('Duplicate email: ' . $email);
                throw new InvalidArgumentException('Email already exists');
            }
            throw $e; // Прочие ошибки пробрасываем дальше
        }
    }
}

$engine = new Engine();
try {
    $engine->insertUser('user@example.com');
} catch (InvalidArgumentException $e) {
    echo 'Пользователь с таким email уже существует.';
}
Результат: при повторной вставке того же email будет выведено сообщение 'Пользователь с таким email уже существует.', а в лог попадёт запись о дубликате.

Пример 2: Логирование всех SQL запросов с временными метками

Пример
class Engine {
    private $pdo;
    private $logFile;

    public function __construct($logFile = '/tmp/sql.log') {
        $this->pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
        ]);
        $this->logFile = $logFile;
    }

    public function query($sql) {
        $start = microtime(true);
        try {
            $result = $this->pdo->query($sql);
        } catch (PDOException $e) {
            file_put_contents($this->logFile, date('Y-m-d H:i:s') . " ERROR: " . $e->getMessage() . "\nSQL: $sql\n", FILE_APPEND);
            throw $e;
        }
        $elapsed = microtime(true) - $start;
        file_put_contents($this->logFile, date('Y-m-d H:i:s') . " OK: время $elapsed сек.\nSQL: $sql\n", FILE_APPEND);
        return $result;
    }
}

$engine = new Engine();
try {
    $engine->query('SELECT 1');
} catch (Exception $e) {
    // Обработка
}
Результат: в файл /tmp/sql.log будут записываться все выполненные запросы с отметкой успеха или ошибки.

Пример 3: Использование кодов ошибок MySQL для дифференцированной реакции

Пример
class Engine {
    private $mysqli;

    public function __construct() {
        $this->mysqli = new mysqli('localhost', 'user', 'pass', 'test');
        if ($this->mysqli->connect_errno) {
            throw new RuntimeException('Connect failed: ' . $this->mysqli->connect_error);
        }
    }

    public function execute($sql) {
        if (!$this->mysqli->query($sql)) {
            $errno = $this->mysqli->errno;
            $error = $this->mysqli->error;
            switch ($errno) {
                case 1062: // Duplicate entry
                    error_log("Duplicate entry: $error");
                    return ['error' => 'duplicate', 'message' => 'Запись уже существует'];
                case 1146: // Table not found
                    error_log("Table missing: $error");
                    return ['error' => 'missing_table', 'message' => 'Таблица не найдена'];
                default:
                    error_log("MySQL error ($errno): $error");
                    throw new RuntimeException('Database error');
            }
        }
        return true;
    }
}

$engine = new Engine();
$result = $engine->execute('INSERT INTO users (id) VALUES (1)');
if (is_array($result)) {
    echo $result['message']; // например, 'Запись уже существует'
}
Результат: при ошибке дублирования возвращается массив с кодом 'duplicate', при отсутствии таблицы – 'missing_table'. Исключение выбрасывается только для нераспознанных ошибок.

Ошибка в классе engine при работе с MySQL в PHP - comments

En
Engine classes mysql php at line (php)