diff --git a/README.txt b/README.txt index 7ebb234..c556d6b 100644 --- a/README.txt +++ b/README.txt @@ -199,6 +199,11 @@ Default: private://feeds/cache Description: The location on the file system where results of HTTP requests are cached. +Name: feeds_sync_cache_feeds_http_interval +Default: 21600 +Description: How often the feeds cache directory should be checked for orphaned + cache files. + Name: feeds_use_mbstring Default: TRUE Description: The extension mbstring is used to convert encodings during parsing. diff --git a/feeds.install b/feeds.install index c79bac1..84e9764 100644 --- a/feeds.install +++ b/feeds.install @@ -70,6 +70,8 @@ function feeds_uninstall() { variable_del('feeds_reschedule'); variable_del('feeds_use_mbstring'); variable_del('feeds_http_file_cache_dir'); + variable_del('feeds_sync_cache_feeds_http_last_check'); + variable_del('feeds_sync_cache_feeds_http_interval'); } /** diff --git a/feeds.module b/feeds.module index 7cded50..e5b1905 100644 --- a/feeds.module +++ b/feeds.module @@ -66,6 +66,20 @@ function feeds_cron() { } feeds_reschedule(FALSE); + + // Sync the files in the cache directory with entries in the cache every now + // and then. By default: every six hours. + $last_check = variable_get('feeds_sync_cache_feeds_http_last_check'); + $interval = variable_get('feeds_sync_cache_feeds_http_interval', 21600); + if ($last_check < (REQUEST_TIME - $interval)) { + // Check first if the task isn't already queued. + $queue = DrupalQueue::get('feeds_sync_cache_feeds_http'); + if ($queue->numberOfItems() < 1) { + // Queue sync task. + FeedsHTTPCache::getInstance('cache_feeds_http')->startSync(); + } + variable_set('feeds_sync_cache_feeds_http_last_check', REQUEST_TIME); + } } /** @@ -108,6 +122,9 @@ function feeds_cron_queue_info() { $queues['feeds_push_unsubscribe'] = array( 'worker callback' => 'feeds_push_unsubscribe', ); + $queues['feeds_sync_cache_feeds_http'] = array( + 'worker callback' => 'feeds_sync_cache_feeds_http', + ); return $queues; } @@ -160,6 +177,14 @@ function _feeds_queue_worker_helper(array $job, $method) { } /** + * Scheduler callback for syncing the feeds cache directory with the entries in + * the cache. + */ +function feeds_sync_cache_feeds_http(array $job) { + FeedsHTTPCache::getInstance('cache_feeds_http')->sync($job['files']); +} + +/** * Scheduler callback for unsubscribing from PuSH hubs. */ function feeds_push_unsubscribe($job) { diff --git a/includes/FeedsHTTPCache.inc b/includes/FeedsHTTPCache.inc index ce00bc1..944a2d1 100644 --- a/includes/FeedsHTTPCache.inc +++ b/includes/FeedsHTTPCache.inc @@ -6,6 +6,26 @@ class FeedsHTTPCache extends DrupalDatabaseCache { /** + * Returns cache object. + * + * @param string $bin + * The cache bin. + * + * @return FeedsHTTPCache + * An instance of FeedsHTTPCache. + */ + public static function getInstance($bin) { + $cache_object = _cache_get_object($bin); + if (!($cache_object instanceof self)) { + // A different cache class could be used for the cache_feeds_http bin that + // does not extend FeedsHTTPCache. In this case, just instantiate the + // FeedsHTTPCache class. + $cache_object = new self($bin); + } + return $cache_object; + } + + /** * Returns cache dir. * * @return string @@ -24,6 +44,30 @@ class FeedsHTTPCache extends DrupalDatabaseCache { } /** + * Returns a list of file names in the cache directory. + * + * @return array + * A list of files. + */ + public function getFileList() { + $files = array(); + + $file_cache_dir = $this->getCacheDir(); + if (is_dir($file_cache_dir)) { + $dir = dir($file_cache_dir); + while (($entry = $dir->read()) !== FALSE) { + if ($entry == '.' || $entry == '..') { + continue; + } + $files[] = $entry; + } + $dir->close(); + } + + return $files; + } + + /** * Constructs a file path for a certain cache ID. * * @param string $cid @@ -220,4 +264,47 @@ class FeedsHTTPCache extends DrupalDatabaseCache { // No cache dir found. return TRUE; } + + /** + * Queues all files to be synced with the cache list. + */ + public function startSync() { + // Get all files currently in the cache directory. + $files = $this->getFileList(); + + $queue = DrupalQueue::get('feeds_sync_cache_feeds_http'); + $queue->createItem(array( + 'files' => $files, + )); + } + + /** + * Removes files for which an entry no longer appears in the cache. + * + * @param array $files + * The files to check. + */ + public function sync(array $files) { + if (count($files) > 50) { + // There are more than 50 files in the cache directory to check. Take the + // first 50 and put the rest on the queue. + $queue = DrupalQueue::get('feeds_sync_cache_feeds_http'); + $queue->createItem(array( + 'files' => array_slice($files, 50), + )); + + // Pick the first 50. + $files = array_slice($files, 0, 50); + } + + // Try to load cache entries for each file. Since the file names are passed + // by reference and the file names for which a cache entry is found are + // removed from the array, we end up with a list of file names for which NO + // cache entry is found. + cache_get_multiple($files, $this->bin); + + // The files for which no cache entry is found, can be removed. + $this->deleteMultipleFiles($files); + } + } diff --git a/includes/FeedsHTTPCacheItem.inc b/includes/FeedsHTTPCacheItem.inc index f4cdd5a..59ba61c 100644 --- a/includes/FeedsHTTPCacheItem.inc +++ b/includes/FeedsHTTPCacheItem.inc @@ -119,14 +119,7 @@ class FeedsHTTPCacheItem { * An instance of FeedsHTTPCache. */ public function getCacheObject() { - $cache_object = _cache_get_object(self::CACHE_BIN); - if (!($cache_object instanceof FeedsHTTPCache)) { - // A different cache class could be used for the cache_feeds_http bin that - // does not extend FeedsHTTPCache. In this case, just instantiate the - // FeedsHTTPCache class. - $cache_object = new FeedsHTTPCache(self::CACHE_BIN); - } - return $cache_object; + return FeedsHTTPCache::getInstance(self::CACHE_BIN); } /**