Issue #1394648 by David_Rothstein: Fixed The installer's cache backend no longer overrides all cache-clearing methods, which can lead to fatal errors.

8.0.x
catch 2012-05-21 11:13:13 +09:00
parent b08c8b478e
commit abb86f9828
5 changed files with 211 additions and 8 deletions

View File

@ -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;
}
}

View File

@ -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.");
}
}
}

View File

@ -0,0 +1,6 @@
name = "Cache test"
description = "Support module for cache system testing."
package = Testing
version = VERSION
core = 8.x
hidden = TRUE

View File

@ -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;
}

View File

@ -0,0 +1,6 @@
<?php
/**
* @file
* Support module for testing the cache system.
*/