PHP try/catch and fatal error
Asked Answered
W

6

34

I'm using the following script to use a database using PHP:

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

Now, I want to use this database handle to do a request using this code:

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

Here is the problem:

  • When the connection to the DB is OK, everything works,
  • When the connection fails but I don't use the DB, I have the $GLOBALS['errors'][] array and the script is still running afterwards,
  • When the connection to the DB has failed, I get the following fatal error:

Notice: Undefined variable: db in C:\xampp\htdocs[...]\test.php on line 32

Fatal error: Call to a member function prepare() on a non-object in C:\xampp\htdocs[...]\test.php on line 32

Note: Line 32 is the $query = $db->prepare(...) instruction.

That is to say, the script crashes, and the try/catch seems to be useless. Do you know why this second try/catch don't works and how to solve it?

Thanks for the help!

EDIT: There are some really good replies. I've validated one which is not exactly what I wanted to do, but which is probably the best approach.

Wien answered 17/10, 2012 at 6:35 Comment(0)
S
41

try/catch blocks only work for thrown exceptions (throw Exception or a subclass of Exception must be called). You cannot catch fatal errors using try/catch.

If your DB connection cannot be established, I would consider it fatal since you probably need your DB to do anything meaningful on the page.

PDO will throw an exception if the connection cannot be established. Your specific problem is that $db is not defined when you try to call a method with it so you get a null pointer (sort of) which is fatal. Rather than jump through if ($db == null) hoops as others are suggesting, you should just fix your code to make sure that $db is either always defined when you need it or have a less fragile way of making sure a DB connection is available in the code that uses it.

If you really want to "catch" fatal errors, use set_error_handler, but this still stops script execution on fatal errors.

Sappy answered 17/10, 2012 at 6:55 Comment(4)
OK, thanks for this explanation. I will stop generating the requested page is the connection to the DB fails. It's easier and probably more understandable the the end user (instead of having errors stacking the the notification box).Wien
set_error_handler might not work for some errors - if so, you can try register_shutdown_function instead as discussed in another thread on SOAllhallows
can you pls help me > error > Fatal error: Cannot use try without catch or finally in C:\wamp\www\anusthana\andro.php on line 55 justpaste.it/74cqb @Explosion PillsAntipode
You can catch fatal errors using \Throwable instead of \Exception in catch blockAponte
D
49

In PHP7, we now can using try catch fatal error with simple work

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

But usualy, we should avoid using catch Error, because it involve to miss code which is belong to programmer's reponsibility :-)

Diabolic answered 4/8, 2018 at 17:29 Comment(1)
And if you'd like you can catch both together as in Java: try { ... } catch (Exception | Error $e) { ... }Snailfish
S
41

try/catch blocks only work for thrown exceptions (throw Exception or a subclass of Exception must be called). You cannot catch fatal errors using try/catch.

If your DB connection cannot be established, I would consider it fatal since you probably need your DB to do anything meaningful on the page.

PDO will throw an exception if the connection cannot be established. Your specific problem is that $db is not defined when you try to call a method with it so you get a null pointer (sort of) which is fatal. Rather than jump through if ($db == null) hoops as others are suggesting, you should just fix your code to make sure that $db is either always defined when you need it or have a less fragile way of making sure a DB connection is available in the code that uses it.

If you really want to "catch" fatal errors, use set_error_handler, but this still stops script execution on fatal errors.

Sappy answered 17/10, 2012 at 6:55 Comment(4)
OK, thanks for this explanation. I will stop generating the requested page is the connection to the DB fails. It's easier and probably more understandable the the end user (instead of having errors stacking the the notification box).Wien
set_error_handler might not work for some errors - if so, you can try register_shutdown_function instead as discussed in another thread on SOAllhallows
can you pls help me > error > Fatal error: Cannot use try without catch or finally in C:\wamp\www\anusthana\andro.php on line 55 justpaste.it/74cqb @Explosion PillsAntipode
You can catch fatal errors using \Throwable instead of \Exception in catch blockAponte
N
2

I will not report what has already been written about testing if $db is empty. Just add that a "clean" solution is to artificially create an exception if the connection to the database failed:

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

Insert the previous line in the try - catch as follow:

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;
}

Hope this will help others!

Needlefish answered 21/1, 2018 at 10:48 Comment(3)
can you pls help me > error > Fatal error: Cannot use try without catch or finally in C:\wamp\www\anusthana\andro.php on line 55 justpaste.it/74cqb @ImabotAntipode
The try keyword is found, but not the catch. If you added the catch keyword, check your curly brackets.Needlefish
solved . just add catch(Exception $e){ $GLOBALS['errors'][] = $e; }Antipode
A
2

Fot catching fatal errors like Type error you can use \Throwable in try-catch construction.

try {
    // some code
} catch (\Throwable $e) {
    // some code
}
Aponte answered 31/10, 2023 at 11:34 Comment(0)
L
0

If database connection fails, $db from your first try .. catch block will be null. That's why later you cannot use a member of non-object, in your case $db->prepare(...). Before using this add

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

This will ensure that you have db instance to work with it.

Labia answered 17/10, 2012 at 6:46 Comment(3)
Is there a way to avoid the if($db) { try{...} catch{...} } ? It's not really nice to have to do that for every single request...Wien
I don't know if your code is inside a function or not, but I suppose that in this test.php you first connect to database and then do other stuff, so after your first try .. catch block where you open connection you can add if(!$db) return; or if (!$db) die('Could not connect to database'). if code execution passes this, you are safe to use $db variable without constantly checking if it exists.Labia
@Labia this wouldn't work in the case there is a database that is --read-only but is trying to write(INSERT INTO). Which is my current caseBrought
T
-1

Try adding the following if statement :

if ($db) {
    $query = $db->prepare("INSERT INTO users (...) VALUES (...);");
    $query->execute(....);
}
else die('Connection lost');
Thistle answered 17/10, 2012 at 6:45 Comment(3)
I don't want to use the die() function because I still want to run the following parts of the script. All requests are done before anything is returned to the client, so if there is an error, I can display friendly errors to the final user.Wien
@Wien then you just omit die part and use else part to display user friendly error. What you need to do is basically detect if the connection is available. So checking for $db instance is the essential thing.Touslesmois
As a fact, almost all the answers are along the same lines utilizing on evaluating the $db instance.Touslesmois

© 2022 - 2025 — McMap. All rights reserved.