PHP try/catch и фатальная ошибка

Я использую следующий скрипт для использования базы данных с помощью PHP:

try{
    $db = new PDO('mysql:host='.$host.';port='.$port.';dbname='.$db, $user, $pass, $options);
}
catch(Exception $e){
    $GLOBALS['errors'][] = $e;
}

Теперь я хочу использовать этот дескриптор базы данных для выполнения запроса с помощью этого кода:

try{
    $query = $db->prepare("INSERT INTO users (...) VALUES (...);");
    $query->execute(array(
        '...' => $...,
        '...' => $...
    ));
}
catch(Exception $e){
    $GLOBALS['errors'][] = $e;
}

Вот проблема:

  • Когда соединение с БД в порядке, все работает,
  • Когда соединение терпит неудачу, но я не использую БД, у меня есть массив $GLOBALS['errors'][] и скрипт все еще работает после этого,
  • Когда соединение с БД завершилось неудачно, я получаю следующую фатальную ошибку:

Примечание. Неопределенная переменная: db в C:\xampp\htdocs [...]\test.php в строке 32

Неустранимая ошибка: вызовите функцию-член prepare() для не-объекта в C:\xampp\htdocs [...]\test.php в строке 32

Примечание. Строка 32 - это команда $query = $db->prepare(...).

То есть, сценарий падает, а try/catch кажется бесполезным. Вы знаете, почему эта вторая попытка/уловка не работает и как ее решить?

Спасибо за помощь!

EDIT: Есть действительно хорошие ответы. Я подтвердил то, что не совсем то, что я хотел сделать, но это, вероятно, лучший подход.

Ответ 1

try/catch блоки работают только для сброшенных исключений (throw Exception или подкласс Exception). Вы не можете поймать фатальные ошибки, используя try/catch.

Если ваше соединение с БД не может быть установлено, я считаю его фатальным, так как вам, вероятно, нужна ваша БД, чтобы сделать что-либо значимое на странице.

PDO выдает исключение, если соединение не может быть установлено. Ваша конкретная проблема заключается в том, что $db не определяется при попытке вызвать метод с ним, чтобы вы получили нулевой указатель (сорт), который является фатальным. Вместо того, чтобы перепрыгивать, if ($db == null) обручи, как предлагают другие, вы должны просто исправить свой код, чтобы убедиться, что $db либо всегда определяется, когда вам это нужно, либо имеет менее хрупкий способ убедиться, что соединение с БД доступен в коде, который его использует.

Если вы действительно хотите "уловить" фатальные ошибки, используйте set_error_handler, но это все еще останавливает выполнение сценария при фатальных ошибках.

Ответ 2

В PHP7, теперь мы можем использовать try catch fatal error с простой работой

try {
   do some thing evil
} catch (Error $e) {
   echo 'Now you can catch me!';
}

Но, как правило, мы должны избегать использования catch Error, потому что она подразумевает пропуск кода, который принадлежит программисту :-)

Ответ 3

Если соединение с базой данных завершается неудачно, $db с первой try.. catch блокирует try.. catch. Вот почему позже вы не можете использовать член не-объекта, в вашем случае $db->prepare(...). Перед использованием этого добавить

if ($db) {
    // other try catch statement
}

Это гарантирует, что у вас есть экземпляр db для работы с ним.

Ответ 4

Почему вы используете try... catch заявления для объявления этого. Замените это:

try{
    $db = new PDO('mysql:host='.$host.';port='.$port.';dbname='.$db, $user, $pass, $options);
}

С:

$db = new PDO('mysql:host='.$host.';port='.$port.';dbname='.$db, $user, $pass, $options) or die("Cannot Create PDO!");

Или на вашем пути:

$db = new PDO('mysql:host='.$host.';port='.$port.';dbname='.$db, $user, $pass, $options) or ($GLOBALS['errors'][] = "Cannot Create PDO!");

Ответ 5

Попробуйте добавить следующую инструкцию if:

if ($db) {
    $query = $db->prepare("INSERT INTO users (...) VALUES (...);");
    $query->execute(....);
}
else die('Connection lost');

Ответ 6

try{
if(!is_null($db))
{
    $query = $db->prepare("INSERT INTO users (...) VALUES (...);");
    $query->execute(array(
        '...' => $...,
        '...' => $...
    ));
}
}
catch(Exception $e){
    $GLOBALS['errors'][] = $e;
}

Ответ 7

Я не буду сообщать, что уже было написано о тестировании, если $db пуст. Просто добавьте, что "чистым" решением является искусственное создание исключения, если соединение с базой данных не удалось:

if ($db == NULL) throw new Exception('Connection failed.');

Вставьте предыдущую строку в try - catch следующим образом:

try{

    // This line create an exception if $db is empty
    if ($db == NULL) throw new Exception('Connection failed.');


    $query = $db->prepare("INSERT INTO users (...) VALUES (...);");
    $query->execute(array(
        '...' => $...,
        '...' => $...
    ));
}
catch(Exception $e){
    $GLOBALS['errors'][] = $e;
}

Надеюсь, это поможет другим!