From abb86f982853436e96e3fc21848e1199e62cc901 Mon Sep 17 00:00:00 2001 From: catch 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 086109004b7..b527d56c32c 100644 --- a/core/lib/Drupal/Core/Cache/InstallBackend.php +++ b/core/lib/Drupal/Core/Cache/InstallBackend.php @@ -34,26 +34,26 @@ use Exception; 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 @@ class InstallBackend extends DatabaseBackend { } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::deleteMultiple(). + * Overrides Drupal\Core\Cache\DatabaseBackend::deleteMultiple(). */ function deleteMultiple(array $cids) { try { @@ -77,7 +77,7 @@ class InstallBackend extends DatabaseBackend { } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::deletePrefix(). + * Overrides Drupal\Core\Cache\DatabaseBackend::deletePrefix(). */ function deletePrefix($prefix) { try { @@ -88,6 +88,9 @@ class InstallBackend extends DatabaseBackend { 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 @@ class InstallBackend extends DatabaseBackend { } /** - * Implements Drupal\Core\Cache\CacheBackendInterface::flush(). + * Overrides Drupal\Core\Cache\DatabaseBackend::flush(). */ function flush() { try { @@ -110,9 +113,39 @@ class InstallBackend extends DatabaseBackend { } /** - * 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 b1d45cfeee5..8a5d9ff1263 100644 --- a/core/modules/system/tests/cache.test +++ b/core/modules/system/tests/cache.test @@ -1,5 +1,7 @@ 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 00000000000..095b6fd3e39 --- /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 00000000000..5b50e11cb48 --- /dev/null +++ b/core/modules/system/tests/modules/cache_test/cache_test.install @@ -0,0 +1,15 @@ +