? 955236-3_error_handling.patch
? 955236-4_error_handling.patch
Index: feeds.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/feeds/feeds.module,v
retrieving revision 1.74.2.11
diff -u -p -r1.74.2.11 feeds.module
--- feeds.module	27 Oct 2010 19:53:21 -0000	1.74.2.11
+++ feeds.module	1 Nov 2010 18:56:45 -0000
@@ -738,6 +738,39 @@ function feeds_log($importer_id, $feed_n
 }
 
 /**
+ * Gets the last caller from a backtrace.
+ *
+ * Modeled after _drupal_get_last_caller().
+ *
+ * @param $backtrace
+ *   A standard PHP backtrace.
+ * @return
+ *   An associative array with keys 'file', 'line' and 'function'.
+ */
+function feeds_get_last_caller($backtrace) {
+  // Shift off one to not report Feeds error handler methods.
+  array_shift($backtrace);
+
+  // The first trace is the call itself.
+  // It gives us the line and the file of the last call.
+  $call = $backtrace[0];
+
+  // The second call give us the function where the call originated.
+  if (isset($backtrace[1])) {
+    if (isset($backtrace[1]['class'])) {
+      $call['function'] = $backtrace[1]['class'] . $backtrace[1]['type'] . $backtrace[1]['function'] . '()';
+    }
+    else {
+      $call['function'] = $backtrace[1]['function'] . '()';
+    }
+  }
+  else {
+    $call['function'] = 'main()';
+  }
+  return $call;
+}
+
+/**
  * Loads an item info object.
  *
  * Example usage:
Index: includes/FeedsSource.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/feeds/includes/FeedsSource.inc,v
retrieving revision 1.26.2.8
diff -u -p -r1.26.2.8 FeedsSource.inc
--- includes/FeedsSource.inc	26 Oct 2010 03:05:58 -0000	1.26.2.8
+++ includes/FeedsSource.inc	1 Nov 2010 18:56:45 -0000
@@ -328,6 +328,8 @@ class FeedsSource extends FeedsConfigura
    */
   public function import() {
     $this->acquireLock();
+    set_error_handler(array($this, 'importError'));
+
     try {
       // Fetch.
       if (empty($this->fetcher_result) || FEEDS_BATCH_COMPLETE == $this->progressParsing()) {
@@ -354,6 +356,8 @@ class FeedsSource extends FeedsConfigura
       unset($this->fetcher_result, $this->state);
     }
     $this->save();
+    restore_error_handler();
+
     if (isset($e)) {
       throw $e;
     }
@@ -376,6 +380,8 @@ class FeedsSource extends FeedsConfigura
    */
   public function clear() {
     $this->acquireLock();
+    set_error_handler(array($this, 'clearError'));
+
     try {
       $this->importer->fetcher->clear($this);
       $this->importer->parser->clear($this);
@@ -391,6 +397,8 @@ class FeedsSource extends FeedsConfigura
       unset($this->state);
     }
     $this->save();
+    restore_error_handler();
+
     if (isset($e)) {
       throw $e;
     }
@@ -398,6 +406,57 @@ class FeedsSource extends FeedsConfigura
   }
 
   /**
+   * Handles errors on import.
+   */
+  public function importError($error_level, $message, $filename, $line, $context) {
+    if ($error_level & error_reporting()) {
+      // If this is NOT a recoverable error, attempt to clean up.
+      // @todo: does this make sense or do we know that if an error was not
+      // recoverable we can't clean up?
+      // @todo: it may be safer to delete the state every time when loading it.
+      if ($this->handleError($error_level, $message, $filename, $line, $context)) {
+        unset($this->state);
+        $this->save();
+      }
+      _drupal_error_handler($error_level, $message, $filename, $line, $context);
+    }
+  }
+
+  /**
+   * Handles errors on clear.
+   */
+  public function clearError($error_level, $message, $filename, $line, $context) {
+    if ($error_level & error_reporting()) {
+      if ($this->handleError($error_level, $message, $filename, $line, $context)) {
+        unset($this->state);
+        $this->save();
+      }
+      _drupal_error_handler($error_level, $message, $filename, $line, $context);
+    }
+  }
+
+  /**
+   * Logs error and determines whether error is fatal
+   *
+   * @return
+   *   TRUE if error is fatal, FALSE otherwise.
+   */
+  protected function handleError($error_level, $message, $filename, $line, $context) {
+    require_once DRUPAL_ROOT . '/includes/errors.inc';
+    $levels = drupal_error_levels();
+    list ($severity_message, $severity_level) = $levels[$error_level];
+    $caller = feeds_get_last_caller(debug_backtrace());
+    $variables = array(
+      '@message' => $message,
+      '%function' => $caller['function'],
+      '%file' => isset($caller['file']) ? $caller['file'] : '',
+      '%line' => isset($caller['line']) ? $caller['line'] : '',
+    );
+    $this->log('import', "@message<br />%function<br />%file<br />%line", $variables, $severity_level);
+    return $error_level & array(E_ERROR | E_COMPILE_ERROR | E_COMPILE_ERROR | E_USER_ERROR);
+  }
+
+  /**
    * Report progress as float between 0 and 1. 1 = FEEDS_BATCH_COMPLETE.
    */
   public function progressParsing() {
