Robert DeBoer
Robert DeBoer

Reputation: 1695

Catching a Thrown Exception from a Static Method

I am attempting to implement exception handling into my project and have hit a bit of a head scratcher.

Code flow: A request hits the index.php page, which brings in a bootstrap file. That bootstrap file defines constants, brings in other files, registers functions, etc, to "start" the system. The bootstrap does two main big things: defines/registers my autoload function, then does the same for exception handling by requiring an error.php file with the exception handling code in it. After the bootstrap file loads, routing happens and away the system goes.

My issue comes from the following. I have a folder of class files in the system. Just classes with static methods for various uses - database, sessions, user, etc. All these classes throw various exceptions (standard and SPL) which my registered exception handler catches. However, the exception handler isn't catching thrown exceptions for the Session class.

The session class is a class (regular, not abstract/static) with static methods that "extend"/"replace" the default PHP session functions with custom ones in order to store session data in a database. In the start() function I register the appropiate functions with the php session "handles" via the session_set_save_handler function. I also call register_shutdown_function as suggested by php.net to ensure session data is saved if the app must abruptly stop.

Here is some code of my session class that was throwing a PDOExceptoin because my SQL was wrong - DB is just a wrapper for PDO:

    public static function write($id, $data) {
    try {
        $expires = time() + self::$lifetime;
        $sql = "INSERT INTO sessions (session_id, session_data, expires) VALUES (?,?,?) ON DUPLICATE KEY UPDATE SET session_data = ? WHERE session_id = ? ";
        if (DB::query($sql, array($id, $data, $expires, $data, $id))->rowCount() > 0) {
            return true;
        } else {
            return false;
        }
    } catch (Exception $e) { 
        return $e->getMessage();
    }
}
  1. If I delete the catch/try, I get a fatal "uncaught PDOException" error.
  2. If I try to rethrow a new Exception in the catch block with the PDOExceptoin message, then PHP spits out a fatal error message at the bottom of the page. It also spits out the exception message and full exception trace.
  3. If I echo the exception message in the catch block, it will print out fine and no fatal uncaught exception errors.

Here is my exception handling error.php file


// Error handler function
function log_error($num, $str, $file, $line, $context = null) {
    log_exception(new ErrorException($str, 0, $num, $file, $line));
}
// Exception handler function
function log_exception($e) {
    if (DEBUG) {
        $type = get_class($e);
        $trace = $e->getTrace();
        $error = /* Code that creates an HTML table for pretty exception printing */;
        echo "";
        echo $error;
        echo "";
    } else {
        $message = "Type: " . get_class($e) . "; Message: {$e->getMessage()}; File: {$e->getFile()}; Line: {$e ->getLine()};";
        file_put_contents(SYSPATH . "/private/log/exceptions.log", $message . PHP_EOL, FILE_APPEND);
        header("Location: 500.html");
    }
}
// Checks for a fatal error, work around for set_error_handler not working on fatal errors.
function check_for_fatal() {
    $error = error_get_last();
    if ($error["type"] == E_ERROR)
        log_error($error["type"], $error["message"], $error["file"],$error["line"]);
}
set_error_handler("log_error");
set_exception_handler("log_exception");
register_shutdown_function("check_for_fatal");

I do realize that my SQL statement is wrong and that is causing the PDOException, but I'm leaving it at the moment to figure out my exception bug.

I believe it has something to do with the fact that I'm calling register_shutdown_function twice in two different spots even though PHP says that it legal and the functions will be called in the order they are registered, so maybe my order is backwards or maybe PHP doesn't really call them in order, just the last function registered?

Edit: I have tried the following to confirm a few things.

  1. The error.php file is being loaded. If I define a constant in the file and print it out later on in the index.php file, it prints out.
  2. After my bootstrap include in the index page, I can throw an exception in the index.php and the exception performs as expected
  3. I can print out the error message from the static DB function that is giving the exception, so the try/catch handling is working inside the static function

Upvotes: 1

Views: 5957

Answers (3)

leedch
leedch

Reputation: 151

Not sure if this helps: If you're using namespaces (which you should), then make sure you are accessing the correct Exception class.

Either put in your header

use Exception;

or add the backslash to your catch statement

} catch (\Exception $e) {

Otherwise PHP will be searching for an exception class inside your namespace.

Upvotes: 4

Your Common Sense
Your Common Sense

Reputation: 157839

} catch (Exception $e) { 
    return $e->getMessage();
}

-- this is why your handler doesn't catch anything.
Because you already caught it manually.

If I delete the catch/try, I get a fatal "uncaught PDOException" error.

Check if your handler is ready by the time exception is thrown. Then check it again.

Upvotes: 0

Orangepill
Orangepill

Reputation: 24645

In your set_exception_handler you have $e ->getLine() ... remove the space... I think what might be happening is this is causing an exception (ErrorException) to be raised from within your exception handler which is what you are seeing, because there is nothing to catch that exception.

you are probably better off doing something like this:

$error = "<div style='text-align: center;'>";
$error .= "<h2 style='color: rgb(190, 50, 50);'>".get_class($e)." Occured:</h2>";
$error .= "<table style='width: 800px; display: inline-block;'>";
$error .= "<tr style='background-color:rgb(240,240,240);'><th>Message</th>";
$error .= "<td>".$e->getMessage()."</td></tr>";
$error .= "<tr style='background-color:rgb(230,230,230);'><th>File</th>";
$error .= "<td>".$e->getFile()."</td></tr>";
$error .= "<tr style='background-color:rgb(240,240,240);'><th>Line</th>";
$error .= "<td>".$e->getLine()."</td></tr>";
$error .= "</table></div>";
echo $error;

Upvotes: 0

Related Questions