From abb86f982853436e96e3fc21848e1199e62cc901 Mon Sep 17 00:00:00 2001 From: catch <catch@35733.no-reply.drupal.org> Date: Mon, 21 May 2012 11:13:13 +0900 Subject: [PATCH] Issue #1394648 by David_Rothstein: Fixed The installer's cache backend no longer overrides all cache-clearing methods, which can lead to fatal errors. --- core/lib/Drupal/Core/Cache/InstallBackend.php | 49 +++++- core/modules/system/tests/cache.test | 143 ++++++++++++++++++ .../tests/modules/cache_test/cache_test.info | 6 + .../modules/cache_test/cache_test.install | 15 ++ .../modules/cache_test/cache_test.module | 6 + 5 files changed, 211 insertions(+), 8 deletions(-) create mode 100644 core/modules/system/tests/modules/cache_test/cache_test.info create mode 100644 core/modules/system/tests/modules/cache_test/cache_test.install create mode 100644 core/modules/system/tests/modules/cache_test/cache_test.module diff --git a/core/lib/Drupal/Core/Cache/InstallBackend.php b/core/lib/Drupal/Core/Cache/InstallBackend.php index 086109004b70..b527d56c32cb 100644 --- a/core/lib/Drupal/Core/Cache/InstallBackend.php +++ b/core/lib/Drupal/Core/Cache/InstallBackend.php @@ -34,26 +34,26 @@ class InstallBackend extends DatabaseBackend { /** - * Overrides Drupal\Core\Cache\CacheBackendInterface::get(). + * Overrides Drupal\Core\Cache\DatabaseBackend::get(). */ function get($cid) { return FALSE; } /** - * Overrides Drupal\Core\Cache\CacheBackendInterface::getMultiple(). + * Overrides Drupal\Core\Cache\DatabaseBackend::getMultiple(). */ function getMultiple(&$cids) { return array(); } /** - * Overrides Drupal\Core\Cache\CacheBackendInterface::set(). + * Overrides Drupal\Core\Cache\DatabaseBackend::set(). */ function set($cid, $data, $expire = CACHE_PERMANENT, array $tags = array()) {} /** - * Implements Drupal\Core\Cache\CacheBackendInterface::delete(). + * Overrides Drupal\Core\Cache\DatabaseBackend::delete(). */ function delete($cid) { try { @@ -65,7 +65,7 @@ function delete($cid) { } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::deleteMultiple(). + * Overrides Drupal\Core\Cache\DatabaseBackend::deleteMultiple(). */ function deleteMultiple(array $cids) { try { @@ -77,7 +77,7 @@ function deleteMultiple(array $cids) { } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::deletePrefix(). + * Overrides Drupal\Core\Cache\DatabaseBackend::deletePrefix(). */ function deletePrefix($prefix) { try { @@ -88,6 +88,9 @@ function deletePrefix($prefix) { catch (Exception $e) {} } + /** + * Overrides Drupal\Core\Cache\DatabaseBackend::invalidateTags(). + */ function invalidateTags(array $tags) { try { if (class_exists('Drupal\Core\Database\Database')) { @@ -98,7 +101,7 @@ function invalidateTags(array $tags) { } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::flush(). + * Overrides Drupal\Core\Cache\DatabaseBackend::flush(). */ function flush() { try { @@ -110,9 +113,39 @@ function flush() { } /** - * Overrides Drupal\Core\Cache\CacheBackendInterface::isEmpty(). + * Overrides Drupal\Core\Cache\DatabaseBackend::expire(). + */ + function expire() { + try { + if (class_exists('Drupal\Core\Database\Database')) { + parent::expire(); + } + } + catch (Exception $e) {} + } + + /** + * Overrides Drupal\Core\Cache\DatabaseBackend::garbageCollection(). + */ + function garbageCollection() { + try { + if (class_exists('Drupal\Core\Database\Database')) { + parent::garbageCollection(); + } + } + catch (Exception $e) {} + } + + /** + * Overrides Drupal\Core\Cache\DatabaseBackend::isEmpty(). */ function isEmpty() { + try { + if (class_exists('Drupal\Core\Database\Database')) { + return parent::isEmpty(); + } + } + catch (Exception $e) {} return TRUE; } } diff --git a/core/modules/system/tests/cache.test b/core/modules/system/tests/cache.test index b1d45cfeee50..8a5d9ff1263e 100644 --- a/core/modules/system/tests/cache.test +++ b/core/modules/system/tests/cache.test @@ -1,5 +1,7 @@ <?php +use Drupal\Core\Cache\DatabaseBackend; +use Drupal\Core\Cache\InstallBackend; use Drupal\simpletest\WebTestBase; class CacheTestCase extends WebTestBase { @@ -457,3 +459,144 @@ class CacheIsEmptyCase extends CacheTestCase { $this->assertTrue($cache->isEmpty(), t('The cache bin is empty')); } } + +/** + * Tests the behavior of the cache backend used for installing Drupal. + */ +class CacheInstallTestCase extends CacheTestCase { + protected $profile = 'testing'; + + public static function getInfo() { + return array( + 'name' => 'Cache install test', + 'description' => 'Confirm that the cache backend used for installing Drupal works correctly.', + 'group' => 'Cache', + ); + } + + function setUp() { + parent::setUp(array('cache_test')); + } + + /** + * Tests the behavior of the cache backend used for installing Drupal. + * + * While Drupal is being installed, the cache system must deal with the fact + * that the database is not initially available, and, after it is available, + * the fact that other requests that take place while Drupal is being + * installed (for example, Ajax requests triggered via the installer's user + * interface) may cache data in the database, which needs to be cleared when + * the installer makes changes that would result in it becoming stale. + * + * We cannot test this process directly, so instead we test it by switching + * between the normal database cache (Drupal\Core\Cache\DatabaseBackend) and + * the installer cache (Drupal\Core\Cache\InstallBackend) while setting and + * clearing various items in the cache. + */ + function testCacheInstall() { + $database_cache = new DatabaseBackend('test'); + $install_cache = new InstallBackend('test'); + + // Store an item in the database cache, and confirm that the installer's + // cache backend recognizes that the cache is not empty. + $database_cache->set('cache_one', 'One'); + $this->assertFalse($install_cache->isEmpty()); + $database_cache->delete('cache_one'); + $this->assertTrue($install_cache->isEmpty()); + + // Store an item in the database cache, then use the installer's cache + // backend to delete it. Afterwards, confirm that it is no longer in the + // database cache. + $database_cache->set('cache_one', 'One'); + $this->assertEqual($database_cache->get('cache_one')->data, 'One'); + $install_cache->delete('cache_one'); + $this->assertFalse($database_cache->get('cache_one')); + + // Store multiple items in the database cache, then use the installer's + // cache backend to delete them. Afterwards, confirm that they are no + // longer in the database cache. + $database_cache->set('cache_one', 'One'); + $database_cache->set('cache_two', 'Two'); + $this->assertEqual($database_cache->get('cache_one')->data, 'One'); + $this->assertEqual($database_cache->get('cache_two')->data, 'Two'); + $install_cache->deleteMultiple(array('cache_one', 'cache_two')); + $this->assertFalse($database_cache->get('cache_one')); + $this->assertFalse($database_cache->get('cache_two')); + + // Store multiple items in the database cache, then use the installer's + // cache backend to delete them via a wildcard prefix. Afterwards, confirm + // that they are no longer in the database cache. + $database_cache->set('cache_one', 'One'); + $database_cache->set('cache_two', 'Two'); + $this->assertEqual($database_cache->get('cache_one')->data, 'One'); + $this->assertEqual($database_cache->get('cache_two')->data, 'Two'); + $install_cache->deletePrefix('cache_'); + $this->assertFalse($database_cache->get('cache_one')); + $this->assertFalse($database_cache->get('cache_two')); + + // Store multiple items in the database cache, then use the installer's + // cache backend to flush the cache. Afterwards, confirm that they are no + // longer in the database cache. + $database_cache->set('cache_one', 'One'); + $database_cache->set('cache_two', 'Two'); + $this->assertEqual($database_cache->get('cache_one')->data, 'One'); + $this->assertEqual($database_cache->get('cache_two')->data, 'Two'); + $install_cache->flush(); + $this->assertFalse($database_cache->get('cache_one')); + $this->assertFalse($database_cache->get('cache_two')); + + // Store multiple items in the database cache with a temporary expiration, + // then use the installer's cache backend to expire outdated items. + // Afterwards, confirm that they are no longer in the database cache. + $database_cache->set('cache_one', 'One', CACHE_TEMPORARY); + $database_cache->set('cache_two', 'Two', CACHE_TEMPORARY); + $this->assertEqual($database_cache->get('cache_one')->data, 'One'); + $this->assertEqual($database_cache->get('cache_two')->data, 'Two'); + $install_cache->expire(); + $this->assertFalse($database_cache->get('cache_one')); + $this->assertFalse($database_cache->get('cache_two')); + + // Store multiple items in the database cache with a temporary expiration, + // then use the installer's cache backend to perform garbage collection. + // Afterwards, confirm that they are no longer in the database cache. + $database_cache->set('cache_one', 'One', CACHE_TEMPORARY); + $database_cache->set('cache_two', 'Two', CACHE_TEMPORARY); + $this->assertEqual($database_cache->get('cache_one')->data, 'One'); + $this->assertEqual($database_cache->get('cache_two')->data, 'Two'); + // After we've checked that the items are in the cache, we need to enable + // garbage collection before running the garbage collector. + config('system.performance')->set('cache_lifetime', 1)->save(); + variable_set('cache_flush_cache_test', 1); + $install_cache->garbageCollection(); + $this->assertFalse($database_cache->get('cache_one')); + $this->assertFalse($database_cache->get('cache_two')); + + // Invalidate a tag using the installer cache, then check that the + // invalidation was recorded correctly in the database. + $install_cache->invalidateTags(array('tag')); + $invalidations = db_query("SELECT invalidations FROM {cache_tags} WHERE tag = 'tag'")->fetchField(); + $this->assertEqual($invalidations, 1); + + // For each cache clearing event that we tried above, try it again after + // dropping the {cache_test} table. This simulates the early stages of the + // installer (when the database cache tables won't be available yet) and + // thereby confirms that the installer's cache backend does not produce + // errors if the installer ever calls any code early on that tries to clear + // items from the cache. + db_drop_table('cache_test'); + try { + $install_cache->isEmpty(); + $install_cache->delete('cache_one'); + $install_cache->deleteMultiple(array('cache_one', 'cache_two')); + $install_cache->deletePrefix('cache_'); + $install_cache->flush(); + $install_cache->expire(); + $install_cache->garbageCollection(); + $install_cache->invalidateTags(array('tag')); + $this->pass("The installer's cache backend can be used even when the cache database tables are unavailable."); + } + catch (Exception $e) { + $this->fail("The installer's cache backend can be used even when the cache database tables are unavailable."); + } + } +} diff --git a/core/modules/system/tests/modules/cache_test/cache_test.info b/core/modules/system/tests/modules/cache_test/cache_test.info new file mode 100644 index 000000000000..095b6fd3e39a --- /dev/null +++ b/core/modules/system/tests/modules/cache_test/cache_test.info @@ -0,0 +1,6 @@ +name = "Cache test" +description = "Support module for cache system testing." +package = Testing +version = VERSION +core = 8.x +hidden = TRUE diff --git a/core/modules/system/tests/modules/cache_test/cache_test.install b/core/modules/system/tests/modules/cache_test/cache_test.install new file mode 100644 index 000000000000..5b50e11cb487 --- /dev/null +++ b/core/modules/system/tests/modules/cache_test/cache_test.install @@ -0,0 +1,15 @@ +<?php + +/** + * @file + * Install, update and uninstall functions for the cache_test module. + */ + +/** + * Implements hook_schema(). + */ +function cache_test_schema() { + $schema['cache_test'] = drupal_get_schema_unprocessed('system', 'cache'); + $schema['cache_test']['description'] = 'Cache table for testing the cache system.'; + return $schema; +} diff --git a/core/modules/system/tests/modules/cache_test/cache_test.module b/core/modules/system/tests/modules/cache_test/cache_test.module new file mode 100644 index 000000000000..3d2e68d683db --- /dev/null +++ b/core/modules/system/tests/modules/cache_test/cache_test.module @@ -0,0 +1,6 @@ +<?php + +/** + * @file + * Support module for testing the cache system. + */ -- GitLab