2

I wrote a database wrapper class to help with queries and I'm trying to figure the best way to implement try-catch blocks.

In my database class, I have a helper function;

public function fetchAll($sql, $bind = array()) {

        $stmt = $this->query($sql, $bind);
        $result = $stmt->fetchAll($this->_fetch_mode);

        return $result;
    }

that I can call outside of the class:

$this->db->fetchAll($sql,[$var1,$var2]);

My question is where to put the try-catch blocks. It would be "easier" if I just put them inside of my database class:

    public function fetchAll($sql, $bind = array()) {

            $stmt = $this->query($sql, $bind);
            try {
                  $result = $stmt->fetchAll($this->_fetch_mode);
                } catch (PDOException $e) {
                   //log the error...give some message
                }

            return $result;
        }

instead of wrapping each call to the database class:

try {
     $this->db->fetchAll($sql,[$var1,$var2]);
    } (catch PDOException $e) {
     //log the error...give some message
    }

However, my feeling (without being able to come up with a concrete example...) is that even though it would be more work to wrap each call to the database class, if I do hard code the try-catch block in my database class, it won't be as flexible in terms of handling errors.

Or, would it be better to have a "catch-all" in my index.php (I use MVC) to catch general exceptions (for example, PDO related), and then if I need more specific ones in my application (for example, those thrown when connecting to 3rd party services), then add additional try-block catches for them:

In my index.php, which all requests pass through via my .htaccess file:

try {
    $db = new Db();
    $db->connect(['host'=>DB_HOST,'dbname'=>DB_NAME,'username'=>DB_USERNAME,'password'=>DB_PASSWORD]);

    $bootstrap = new Bootstrap();

    $bootstrap->setControllerPath('application/controllers');
    $bootstrap->setModelPath('application/models');

    $bootstrap->init(); //load the controller/model/classes
} (catch Exception $e) {
    //log error
    //if ajax request return json_encoded message other redirect to error page
}
  • 1
    In what circumstances will the PDOException be thrown? – Winston Ewert Jul 3 '15 at 15:06
  • Database unable to connect, transaction timing out; re your comment below, I would at least like to give the user a "friendly" message if there is such an error and I plan to log the error details. The message would typically appear after an ajax request (for example, a user tries to submit something via ajax but can't because of a database connection issue). – Eric Jul 4 '15 at 5:53
  • you don't need to add special handling for your PDOException in order to do that. All you need is to have a catch-all error handler that will catch the error, log the relevant information, and then send an error response to the client. In the case of an AJAX request, it should return some sort of error code in the http response, and the client side code show show a generic error. – Winston Ewert Jul 4 '15 at 15:38
  • Please see my edited code above. Is this how the placement of the "catch all" would work? If so, that would be great as it would minimize the number of try-catches that I would need. In this scenario, I'd have one "catch all" my index.php. Then, I would just need to add a few more for third party issues; For example, I use Stripe within my application and would catch Stripe specific issues as well such as "credit card declined", etc. directly in my app. – Eric Jul 5 '15 at 6:22
  • Yes, that's exactly how it should work. – Winston Ewert Jul 5 '15 at 13:55
2

You shouldn't catch the PDOException at all.

Now, you should have some sort of last resort exception handler that catches all exceptions and logs them. PDOExceptions should be caught there. But you shouldn't catch them anywhere else.

In my understanding, PDOException indicates either that something has gone very wrong with the connection to the database, or you wrote bad SQL. Its only useful to attempt to handle the exception if you can do something useful as an alternative. But if you can't get the information from the database, your application is probably useless, and there is little point in trying to recover.

  • 1
    I think it's important to add this little warning from php.net concerning PDOException handling. "Warning If your application does not catch the exception thrown from the PDO constructor, the default action taken by the zend engine is to terminate the script and display a back trace. This back trace will likely reveal the full database connection details, including the username and password. It is your responsibility to catch this exception, either explicitly (via a catch statement) or implicitly via set_exception_handler()." from: php.net/manual/en/pdo.connections.php – CreationTribe Jan 3 at 6:24
2

I would argue it's impossible to handle all exceptions the same way. I think you should let it propagate back to the calling code as only that ultimately knows how to handle the exceptional situation.

As an example imagine you have an blog. Every hour you query the database for new articles to populate a cache. But luck isn't with you and the database is temporarily unavailable. You could let the exception go unhandled and the whole site is down. You could also handle the exception inside the database abstraction layer and return a default result (effectively hiding the exception). But returning a default value would overwrite the cache with nothing. The site is up, but doesn't work properly. You could also log the error, rethrow the exception to let the calling code decide what to do. The calling code could reuse the already cached query results and extend the lifetime of the cache another hour hoping for the database to come back up. This way your users would experience minimal disruption of your site and may never even notice you had problems.

But it isn't always easy to determine what to do in exceptional cases and sometimes it would preferable to just let the exception bubble up until it hits a generic exception handler, which displays a generic error page. You can in addition implement some general procedures if a query fails, such as error logging in this generic exception handler. This would be preferable as this can be considered boilerplate code that doesn't change anything. And having unhandled exceptions emailed to you (as an example) lets you resolve the issue faster than if you were to discover the issue yourself.

You also get an additional bonus by grouping generic-logging code in a single place. You can handle specific logging alongside relevant code (where details are available) and if your generic logging procedure should change you only need to change it once.

Using your fetchAll() method the structure could look like:

public function fetchAll($sql, $bind = array()) {

    // Do something awesome before try-catch block.

    try {

        // Execute query...

    }catch(PDOException $exception) {

        $this->logger->log('SQL query failed: ' . $exception->getMessage());

        /*
         * Re-throw the exception to let the calling code
         * handle the situation.
         */
        throw $exception

    }

    // Continue as everything executed as expected.

}
  • I never thought about the possibility of re-throwing the exception as a means to handle the error in a general sense (every PDO exception will be logged), and then in a specific sense regarding my app's logic. If I understand you correctly, would this then mean that every call outside of the database class would have another try-catch block to implement specifics, case by case? – Eric Jul 3 '15 at 15:02
  • @Eric - Yes... That is if the application can recover from the exception. If not you could let it propagate up until it hits the general exception handler (which you can also customize) and show a generic error page. – AnotherGuy Jul 5 '15 at 10:45
1

The answer depends on the responsibilities of your wrapping class. If all it does is encapsulate your connection settings for convenience, then simply letting PDOException pass through is preferable. If your wrapper has more responsibilities such as encapsulating certain queries or connection pooling, you may want to develop an exception or two for this wrapper, which in turn wrap PDOException to help you be sure about where the error originated.

A good rule of thumb to help answer the question, "where should exceptions be caught," would be to catch them as soon as the application can move on from it.

Suppose a method in class A needs an integer value and calls a method in class B which in turn expects an array from a method in the db wrapper. If returning an empty value from the db wrapper (like array()) allows the application to continue sensibly with degraded results, then catching in B is OK. But if the empty array is an expected value, letting the exception bubble up the call stack to A is reasonable. Be sure to stop as soon as you reach a frame where you can move on from the failure. In a well-designed application, this shouldn't be more than 2 frames.

Catching exceptions at the soonest possible point yields problems communicating the failure to the caller, when the exception can do exactly that. For example, if a method returns data from a nullable integer column from a database, you have no reasonable return value to flag an error. But the exception will always clearly indicate the failure.

Or, would it be better to have a "catch-all" in my index.php

In general, you should have one of those to ensure that you have control of otherwise unhandled exceptions and to ensure that you do things like logging and properly releasing resources you've acquired. Avoid them elsewhere in your application to ensure you aren't swallowing information vital to explaining how your application is behaving.

1

My question is where to put the try-catch blocks.

Simple question; simple answer.

You put catch blocks where your code can do something useful about an Exception.

Ideally, that "something useful" would be to recover from the exceptional circumstance, thereby allowing the program to carry on from that point as if nothing had happened.

IMHO, that's the most important part of Structured Exception Handling.

If code can't do anything "useful" about an Exception, it shouldn't even acknowledge it, let alone catch it, allowing the Exception to propagate up and out to some other code that can handle it, even if that's only the "Oops; something went wrong!", catch-all exception handler, be it provided by the run-time or coded at the top-level of your PHP pages.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.