Exceptions were sneaked into core without agreeing on their use. What's worse, there was no research on when they are necessary and what problems they bring. We currently have two problems: if an exception gets thrown too early then Drupal dies and there is an issue which would solve by adding even more code to bootstrap (the error/exception handling). The bigger problem is if an exception gets thrown while handling an exception (say, you make a typo in a query in catch which resets some state) then you get a very helpful Fatal error: Exception thrown without a stack frame in Unknown on line 0 error message.

Instead, we should collect errors in a static and provide a getter. Very easy with drupal_static. This pattern is very common in PHP and Drupal and the consequences are well understood. We should throw an exception only if there is no hope of continuing.

CommentFileSizeAuthor
#17 exceptions.patch2.83 KBchx
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Dries’s picture

The fact that we get Fatal error: Exception thrown without a stack frame in Unknown on line 0 error message is that a bug in PHP or Drupal?

chx’s picture

It's a PHP feature! :) Edit: http://bugs.php.net/bug.php?id=36826

casey’s picture

Throwing exceptions in a exception handler doesn't seem the best thing to do to me.

sun’s picture

Although I'm not very familiar with the problem and exceptions in general, the outlined problem analysis + proposed solution sounds sane. So, a +1 from me.

casey’s picture

-1 from me. Use of exceptions is a very powerful method of error handling. Pushing errors in an array doesn't give you very much room to fix/handle those at runtime.

Throwing exceptions in catch blocks work fine, just not in a exception handler. A exception handler is a ultimum remedium; it catches exceptions which are uncaught by PHP script. You shouldn't throw exceptions in there.

try {
  try {
    throw new Exception("Waaaaah");
  }
  catch (Exception $ex) {
    throw new Exception("Woooh");
  }
}
catch (Exception $ex) {

}
yched’s picture

Agreed that a static array of errors doesn't provide the catching features of exceptions.
Field API form validation, for instance, entirely relies on raising / catching exceptions.

jbrown’s picture

I disagree chx. Exceptions are extremely powerful when used correctly.

There should be an exception handler, but it should never be used deliberately. It is a bug if it is called.

Functions should document any exceptions they may throw.

Functions that call such a function should catch these exceptions, or document that they may throw them.

jbrown’s picture

Exceptions fall through the call stack until caught:

function a() {
  throw new Exception("Woooh");
}

function b() {
  a();
}

try {
  b();
}
catch (Exception $e) {
  print "Exception caught";
}

It is only if they reach the bottom that the handler is called in.

Andrew Udvare’s picture

This snippet will cause a fatal exception most of the time if the record already exists. Bad if you want to keep refreshing the page for testing.

  if (file_unmanaged_move($src, $dest, FILE_EXISTS_REPLACE) != FALSE) {
    // Create a Drupal file object
    $file = new stdClass();
    $file->fid = NULL;
    $file->uri = $dest;
    $file->filename = $filename;
    $file->filemime = file_get_mimetype($file->uri);
    $file->uid = $user->uid;
    $file->status |= FILE_STATUS_PERMANENT;
    file_save($file);

Also, forgetting to call db_set_active() after switching databases will often do the same.

chx’s picture

@jbrown did you even read my writeup? Noone cares what the intentional purposes of exceptions are. I am worried about the known and unknown side effects.

Crell’s picture

@Tatsh: Both of what you describe are bugs in their own right that should be fixed. (The latter is a bug in the caller's code. You'll get an explosion if you try to run Drupal queries against a non-Drupal DB because the tables are missing. That's to be expected. If you really want to be safe, use Database::getConnection() to get your 3rd party connection and use it directly, without ever switching the active DB.)

jbrown’s picture

chx: provided my rules in #7 are followed, there are no side effects.

catch’s picture

I recently got the stack frame on line 0 error when dbtng was querying for a non-existent database column. In D6 you'd get a nice error showing you the query, so this is still a critical regression.

jbrown’s picture

jbrown’s picture

Priority: Critical » Normal

Demoting this to normal to bring down the critical issue count as #742246: Handle uncaught exceptions has been fixed.

andypost’s picture

Suppose _drupal_log_error() should care about switching DB to 'default' database other than current implementation of dblog_watchdog() which allows only storing messages in default DB

chx’s picture

Title: Exceptions are thrown instead of error collection » Database bugs became fatals
Priority: Normal » Critical
Status: Active » Needs review
FileSize
2.83 KB

In Drupal 6 database errors were not fatal. Nor they should be. Note that once this is in we can happily throw out drupal_register_shutdown_function and whatever else kludges we added because of this regression.

Status: Needs review » Needs work

The last submitted patch, exceptions.patch, failed testing.

matt2000’s picture

<silliness>
<?php
try {
  flag_issue($this);
} catch (Exception $e) {
  echo 'subscribe';
}
?>
</silliness>
matt2000’s picture

Confirming that install fails:

Fatal error: Call to a member function fetchField() on a non-object in /var/www/drupal/includes/bootstrap.inc on line 678

Damien Tournoud’s picture

Strongly disagree. Database errors should be fatal. We already had trouble with that before, it's way better to fail quick on this.

chx’s picture

Damien, this causes uncounted problems. As I mentioned, so far we have broken installs and session dying with uncaught on line 0 and then who knows what else.

Crell’s picture

Priority: Critical » Normal

Absolutely not. Exceptions are the main way that we trigger transaction rollbacks. Requiring that we explicitly ask for a transaction every time we think we may possibly need one renders the entire error handling system useless.

Silently failing (which is what returning NULL means) is a terrible idea. It means every time you get a result object back you need to check it's type before using it to confirm that it's not NULL or you get a fatal anyway. (See comment #20, for instance.) Exceptions give you a chance to recover gracefully if you plan for it. If we're not planning for it properly, the solution isn't to throw out exceptions (pun intended) but to handle them properly.

chx’s picture

Status: Needs work » Closed (won't fix)

As I said already: I will point everyone to this issue when they come to IRC and post everywhere how Drupal 7 sucks because of it. Your call. This is a very bad decision but I cam not fight both of you. I really like how theories and transactions (which are absolutely unnecessary for the typical webapp and only throws hurdles in your way when you try to scale) stop making Drupal better. Oh well.

jbrown’s picture

Exceptions are only fatal if they are not caught.

If you want to handle a db error in code instead of getting the error page, just wrap your code in a try{} catch{} .

I never noticed that throw_exception option before - its really awful and should be got rid of. Use try{} catch{} instead when necessary.

matt2000’s picture

If database errors are (intentionally) fatal, is it still possible for someone to write a module that would respond to database failures by serving a page from a static cache (such as provided by Boost module or a proxy) ?

joshk’s picture

Status: Closed (won't fix) » Needs work

Coming in six months late and with my Pragmatic hat on I think this is a must-fix.

The Drupal developer experience is 100% broken if database errors (or who knows what from contib) cause fails like this:

Fatal error: Exception thrown without a stack frame in Unknown on line 0

#742246: Handle uncaught exceptions may have helped but I have hit this enough times in my first attempt to do an actual upgrade. Personally I would consider this as a critical issue. Nobody cares whether we use exceptions "correctly" or not, but they will walk away from the project if debugging becomes a complete guessing game.

joshk’s picture

So as it stands, it looks like we have some cases from #742246: Handle uncaught exceptions where error_displayable() fails, which causes this problem. Investigating...

joshk’s picture

Status: Needs work » Closed (won't fix)

Ok, this isn't about the database in particular. I'm going to re-close and open a new issue.