Почему PDO создает исключение ErrorException, а не PDOException? - PullRequest
2 голосов
/ 12 марта 2019

У меня есть Sentry, отслеживающий неперехваченные исключения в моем приложении PHP, и я заметил странное неперехваченное исключение из PDO.Код выглядит следующим образом:

   /**
    * @return boolean TRUE if the connection to the database worked; FALSE otherwise. 
    */
   public function verifyDatabase() {
       try{ 
           $this->pdo->query('SELECT 1');
           return true;
       }
       catch (\PDOException $e) {
           echo 'Lost connection to database: ' . $e->getMessage() . PHP_EOL;
           return false;
       }
    }

Это должно отлавливать ошибки типа «сервер MySQL ушел», и он действительно работает на моей машине для разработки.Тем не менее, Sentry недавно записал эту ошибку:

ErrorException PDO :: query (): сервер MySQL пропал

По словам часового, это было выдано $this->pdo->query('SELECT 1'); утверждение выше.Подобные ошибки должны были быть обнаружены try/catch.Почему PDO выбрасывает ErrorException, а не PDOException?

Ответы [ 2 ]

1 голос
/ 12 марта 2019

Я не могу воспроизвести ErrorException.

$pdo = new PDO('mysql:host=127.0.0.1;dbname=test', ..., ...);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

sleep(20); // during this sleep, I stop my MySQL Server instance.

$result = $pdo->query("SELECT 1");

Выход:

Warning: PDO::query(): MySQL server has gone away

Warning: PDO::query(): Error reading result set's header

Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
Stack trace:
#0 /Users/bkarwin/Documents/SO/pdo.php(8): PDO->query('SELECT 1')

Это показывает, что при удалении сервера генерируется исключение PDOException, а не ErrorException.

Протестировано с MySQL 5.6.37 и PHP 7.1.23.

Интересно, является ли код, который вы показываете в своем вопросе, на самом деле кодом, который развернут и выдает исключение для Sentry. Возможно, у вас есть код вроде:

   catch (\PDOException $e) {
       throw ErrorException($e->getMessage());
   }

Либо в вашей функции verifyDatabase (), либо в коде, который вызывает verifyDatabase (). Другими словами, что делает ваше приложение, когда verifyDatabase () возвращает false ?

0 голосов
/ 13 марта 2019

Хорошо, думаю, я понял это. Похоже, что это связано с ошибкой, из-за которой драйвер MySQL PDO выдает предупреждения, даже если предполагается, что они отключены (см. Также: этот ответ ). Я полагаю, что Sentry воспринимает это как ошибки.

Я наконец смог воспроизвести и решить эту проблему, изменив тестовый сценарий Билла Карвина:

// Initialize the Sentry reporting client
$ravenClient = new \Raven_Client(SENTRY_KEY);
$ravenClient->install();

echo 'Connecting...' . PHP_EOL;

$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

echo 'Connected. Waiting...' . PHP_EOL;

sleep(20); // during this sleep, I stop my MySQL Server instance.

echo 'Querying...' . PHP_EOL;

try {
    $result = $pdo->query("SELECT 1");
}
catch(\PDOException $e) {
    echo 'Caught PDOException ' . $e->getMessage() . PHP_EOL;
}

Это напечатает следующее:

Connecting...
Connected. Waiting...
Querying...
PHP Warning:  PDO::query(): MySQL server has gone away in /home/xxx/src/test.php on line 37
PHP Stack trace:
PHP   1. {main}() /home/xxx/src/test.php:0
PHP   2. PDO->query() /home/xxx/src/test.php:37
Caught PDOException SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
Done.

И Sentry запишет ErrorException в строке query().

Мне удалось решить эту проблему, реализовав «решение», опубликованное jferrer в отчете об ошибках PHP .

// Convert NOTICE, WARNING, ... in Exceptions
$convertErrorToException = function ($level, $message, $file, $line){
    throw new ErrorException($message, 0, $level, $file, $line);
};

// The $previousErrorHandler will contain Sentry's handler
$previousErrorHandler = set_error_handler($convertErrorToException);

try {
    $result = $pdo->query("SELECT 1");
}
catch(\PDOException $e) {
    echo 'Caught PDOException ' . $e->getMessage() . PHP_EOL;
}
catch(\ErrorException $e) {
    echo 'Caught ErrorException ' . $e->getMessage() . PHP_EOL;
}

// Restore Sentry as the default handler
set_error_handler($previousErrorHandler);

В результате выдается и перехватывается только исключение ErrorException:

Connecting...
Connected. Waiting...
Querying...
Caught ErrorException PDO::query(): MySQL server has gone away
Done.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...