From 335d1759ce33ca4aa827529e5c9fbdbcc763500b Mon Sep 17 00:00:00 2001 From: twistor <twistor@473738.no-reply.drupal.org> Date: Sun, 12 Jul 2015 17:50:58 -0700 Subject: [PATCH] Issue #2531858 by twistor: Add a FeedsSource::pushImport() method --- includes/FeedsConfigurable.inc | 4 +- includes/FeedsSource.inc | 65 +++++++++++++++++++++++++++++++-- plugins/FeedsPlugin.inc | 2 +- tests/feeds_processor_node.test | 18 +++++++-- 4 files changed, 80 insertions(+), 9 deletions(-) diff --git a/includes/FeedsConfigurable.inc b/includes/FeedsConfigurable.inc index d0d8ce16..d809c20f 100644 --- a/includes/FeedsConfigurable.inc +++ b/includes/FeedsConfigurable.inc @@ -55,7 +55,9 @@ abstract class FeedsConfigurable { if (!strlen($id)) { throw new InvalidArgumentException(t('Empty configuration identifier.')); } - static $instances = array(); + + $instances = &drupal_static(__METHOD__, array()); + if (!isset($instances[$class][$id])) { $instances[$class][$id] = new $class($id); } diff --git a/includes/FeedsSource.inc b/includes/FeedsSource.inc index 1fc5209e..f5b0de34 100644 --- a/includes/FeedsSource.inc +++ b/includes/FeedsSource.inc @@ -197,7 +197,9 @@ class FeedsSource extends FeedsConfigurable { */ public static function instance($importer_id, $feed_nid) { $class = variable_get('feeds_source_class', 'FeedsSource'); - static $instances = array(); + + $instances = &drupal_static(__METHOD__, array()); + if (!isset($instances[$class][$importer_id][$feed_nid])) { $instances[$class][$importer_id][$feed_nid] = new $class($importer_id, $feed_nid); } @@ -406,23 +408,80 @@ class FeedsSource extends FeedsConfigurable { // occurred in hook_feeds_after_import(). $this->exception = $e; } - $this->releaseLock(); // Clean up. $result = $this->progressImporting(); if ($result == FEEDS_BATCH_COMPLETE || isset($e)) { $this->imported = time(); - $this->log('import', 'Imported in !s s', array('!s' => $this->imported - $this->state[FEEDS_START]), WATCHDOG_INFO); + $this->log('import', 'Imported in @s seconds.', array('@s' => $this->imported - $this->state[FEEDS_START]), WATCHDOG_INFO); module_invoke_all('feeds_after_import', $this); unset($this->fetcher_result, $this->state); } $this->save(); + + $this->releaseLock(); + if (isset($e)) { throw $e; } + return $result; } + /** + * Imports a fetcher result all at once in memory. + * + * @param FeedsFetcherResult $fetcher_result + * The fetcher result to process. + * + * @throws Exception + * Thrown if an error occurs when importing. + */ + public function pushImport(FeedsFetcherResult $fetcher_result) { + // Since locks only work during a request, check if an import is active. + if (!empty($this->fetcher_result) || !empty($this->state)) { + throw new RuntimeException('The feed is currently importing.'); + } + + $this->acquireLock(); + $start = time(); + + try { + module_invoke_all('feeds_before_import', $this); + + // Parse. + do { + $parser_result = $this->importer->parser->parse($this, $fetcher_result); + module_invoke_all('feeds_after_parse', $this, $parser_result); + + // Process. + $this->importer->processor->process($this, $parser_result); + + } while ($this->progressParsing() !== FEEDS_BATCH_COMPLETE); + } + catch (Exception $e) { + // $e is stored and re-thrown once we've had a chance to log our progress. + // Set the exception so that other modules can check if an exception + // occurred in hook_feeds_after_import(). + $this->exception = $e; + } + + module_invoke_all('feeds_after_import', $this); + + $this->imported = time(); + $this->log('import', 'Imported in @s seconds.', array('@s' => $this->imported - $start), WATCHDOG_INFO); + + unset($this->fetcher_result, $this->state); + + $this->save(); + + $this->releaseLock(); + + if (isset($e)) { + throw $e; + } + } + /** * Remove all items from a feed. * diff --git a/plugins/FeedsPlugin.inc b/plugins/FeedsPlugin.inc index 2629fb9e..65115f85 100644 --- a/plugins/FeedsPlugin.inc +++ b/plugins/FeedsPlugin.inc @@ -53,7 +53,7 @@ abstract class FeedsPlugin extends FeedsConfigurable implements FeedsSourceInter throw new InvalidArgumentException(t('Empty configuration identifier.')); } - static $instances = array(); + $instances = &drupal_static(__METHOD__, array()); if (!isset($instances[$class][$id])) { $instance = new $class($id); diff --git a/tests/feeds_processor_node.test b/tests/feeds_processor_node.test index e9a2bc91..c5cbd056 100644 --- a/tests/feeds_processor_node.test +++ b/tests/feeds_processor_node.test @@ -12,8 +12,8 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { public static function getInfo() { return array( - 'name' => 'RSS import to nodes', - 'description' => 'Tests a feed configuration that is attached to a content type, uses HTTP fetcher, common syndication parser and a node processor. Repeats the same test for an importer configuration that is not attached to a content type and for a configuration that is attached to a content type and uses the file fetcher.', + 'name' => 'Processor: Node', + 'description' => 'Tests for the node processor.', 'group' => 'Feeds', ); } @@ -577,8 +577,6 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { // The feed should still be scheduled because it is being processed. // @see https://drupal.org/node/2275893 - feeds_source('syndication', 0)->scheduleImport(); - $this->cronRun(); $this->assertEqual(86, db_query("SELECT COUNT(*) FROM {node}")->fetchField()); } @@ -658,4 +656,16 @@ class FeedsRSStoNodesTest extends FeedsWebTestCase { $this->assertNoText('Updated 2 nodes.'); } + /** + * Tests the FeedsSource::pushImport() method. + */ + public function testPushImport() { + // Attach to standalone importer. + $this->setSettings('syndication', NULL, array('content_type' => '')); + + $raw = file_get_contents(dirname(__FILE__) . '/feeds/developmentseed.rss2'); + feeds_source('syndication', 0)->pushImport(new FeedsFetcherResult($raw)); + $this->assertEqual(10, db_query("SELECT COUNT(*) FROM {node}")->fetchField()); + } + } -- GitLab