diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index d8f5e2f..d5b411e 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -321,6 +321,7 @@ function install_begin_request(&$install_state) { drupal_language_initialize(); require_once DRUPAL_ROOT . '/core/includes/ajax.inc'; + require_once DRUPAL_ROOT . '/core/includes/cache.inc'; // Override the module list with a minimal set of modules. $module_list['system']['filename'] = 'core/modules/system/system.module'; $module_list['user']['filename'] = 'core/modules/user/user.module'; @@ -328,18 +329,6 @@ function install_begin_request(&$install_state) { drupal_load('module', 'system'); drupal_load('module', 'user'); - // Load the cache infrastructure using a "fake" cache implementation that - // does not attempt to write to the database. We need this during the initial - // part of the installer because the database is not available yet. We - // continue to use it even when the database does become available, in order - // to preserve consistency between interactive and command-line installations - // (the latter complete in one page request and therefore are forced to - // continue using the cache implementation they started with) and also - // because any data put in the cache during the installer is inherently - // suspect, due to the fact that Drupal is not fully set up yet. - require_once DRUPAL_ROOT . '/core/includes/cache.inc'; - $conf['cache_classes'] = array('cache' => 'Drupal\Core\Cache\InstallBackend'); - // The install process cannot use the database lock backend since the database // is not fully up, so we use a null backend implementation during the // installation process. This will also speed up the installation process. diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php index d4de8bf..db594d9 100644 --- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php +++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php @@ -41,6 +41,14 @@ class DatabaseBackend implements CacheBackendInterface { } /** + * Checks whether the database is available. + */ + protected function isDatabaseAvailable() { + global $install_state; + return class_exists('Drupal\Core\Database\Database', FALSE) && (empty($install_state) || !empty($install_state['database_tables_exist'])); + } + + /** * Implements Drupal\Core\Cache\CacheBackendInterface::get(). */ function get($cid) { @@ -53,7 +61,8 @@ class DatabaseBackend implements CacheBackendInterface { * Implements Drupal\Core\Cache\CacheBackendInterface::getMultiple(). */ function getMultiple(&$cids) { - try { + $cache = array(); + if ($this->isDatabaseAvailable()) { // When serving cached pages, the overhead of using ::select() was found // to add around 30% overhead to the request. Since $this->bin is a // variable, this means the call to ::query() here uses a concatenated @@ -62,7 +71,6 @@ class DatabaseBackend implements CacheBackendInterface { // otherwise. When serving an uncached page, the overhead of using // ::select() is a much smaller proportion of the request. $result = Database::getConnection()->query('SELECT cid, data, created, expire, serialized, tags, checksum FROM {' . Database::getConnection()->escapeTable($this->bin) . '} WHERE cid IN (:cids)', array(':cids' => $cids)); - $cache = array(); foreach ($result as $item) { $item = $this->prepareItem($item); if ($item) { @@ -70,13 +78,8 @@ class DatabaseBackend implements CacheBackendInterface { } } $cids = array_diff($cids, array_keys($cache)); - return $cache; - } - catch (Exception $e) { - // If the database is never going to be available, cache requests should - // return FALSE in order to allow exception handling to occur. - return array(); } + return $cache; } /** @@ -135,63 +138,70 @@ class DatabaseBackend implements CacheBackendInterface { $fields['serialized'] = 0; } - try { + if ($this->isDatabaseAvailable()) { Database::getConnection()->merge($this->bin) ->key(array('cid' => $cid)) ->fields($fields) ->execute(); } - catch (Exception $e) { - // The database may not be available, so we'll ignore cache_set requests. - } } /** * Implements Drupal\Core\Cache\CacheBackendInterface::delete(). */ function delete($cid) { - Database::getConnection()->delete($this->bin) - ->condition('cid', $cid) - ->execute(); + if ($this->isDatabaseAvailable()) { + Database::getConnection()->delete($this->bin) + ->condition('cid', $cid) + ->execute(); + } } /** * Implements Drupal\Core\Cache\CacheBackendInterface::deleteMultiple(). */ function deleteMultiple(array $cids) { - // Delete in chunks when a large array is passed. - do { - Database::getConnection()->delete($this->bin) - ->condition('cid', array_splice($cids, 0, 1000), 'IN') - ->execute(); + if ($this->isDatabaseAvailable()) { + // Delete in chunks when a large array is passed. + do { + Database::getConnection()->delete($this->bin) + ->condition('cid', array_splice($cids, 0, 1000), 'IN') + ->execute(); + } + while (count($cids)); } - while (count($cids)); } /** * Implements Drupal\Core\Cache\CacheBackendInterface::deletePrefix(). */ function deletePrefix($prefix) { - Database::getConnection()->delete($this->bin) - ->condition('cid', Database::getConnection()->escapeLike($prefix) . '%', 'LIKE') - ->execute(); + if ($this->isDatabaseAvailable()) { + Database::getConnection()->delete($this->bin) + ->condition('cid', Database::getConnection()->escapeLike($prefix) . '%', 'LIKE') + ->execute(); + } } /** * Implements Drupal\Core\Cache\CacheBackendInterface::flush(). */ function flush() { - Database::getConnection()->truncate($this->bin)->execute(); + if ($this->isDatabaseAvailable()) { + Database::getConnection()->truncate($this->bin)->execute(); + } } /** * Implements Drupal\Core\Cache\CacheBackendInterface::expire(). */ function expire() { - Database::getConnection()->delete($this->bin) - ->condition('expire', CacheBackendInterface::CACHE_PERMANENT, '<>') - ->condition('expire', REQUEST_TIME, '<') - ->execute(); + if ($this->isDatabaseAvailable()) { + Database::getConnection()->delete($this->bin) + ->condition('expire', CacheBackendInterface::CACHE_PERMANENT, '<>') + ->condition('expire', REQUEST_TIME, '<') + ->execute(); + } } /** @@ -249,13 +259,15 @@ class DatabaseBackend implements CacheBackendInterface { * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags(). */ public function invalidateTags(array $tags) { - foreach ($this->flattenTags($tags) as $tag) { - unset(self::$tagCache[$tag]); - Database::getConnection()->merge('cache_tags') - ->key(array('tag' => $tag)) - ->fields(array('invalidations' => 1)) - ->expression('invalidations', 'invalidations + 1') - ->execute(); + if ($this->isDatabaseAvailable()) { + foreach ($this->flattenTags($tags) as $tag) { + unset(self::$tagCache[$tag]); + Database::getConnection()->merge('cache_tags') + ->key(array('tag' => $tag)) + ->fields(array('invalidations' => 1)) + ->expression('invalidations', 'invalidations + 1') + ->execute(); + } } } @@ -281,15 +293,12 @@ class DatabaseBackend implements CacheBackendInterface { } } if ($query_tags) { - try { + if ($this->isDatabaseAvailable()) { if ($db_tags = Database::getConnection()->query('SELECT tag, invalidations FROM {cache_tags} WHERE tag IN (:tags)', array(':tags' => $query_tags))->fetchAllKeyed()) { self::$tagCache = array_merge(self::$tagCache, $db_tags); $checksum += array_sum($db_tags); } } - catch (Exception $e) { - // The database may not be available, so we'll ignore cache_set requests. - } } return $checksum; } @@ -298,12 +307,14 @@ class DatabaseBackend implements CacheBackendInterface { * Implements Drupal\Core\Cache\CacheBackendInterface::isEmpty(). */ function isEmpty() { - $this->garbageCollection(); - $query = Database::getConnection()->select($this->bin); - $query->addExpression('1'); - $result = $query->range(0, 1) - ->execute() - ->fetchField(); - return empty($result); + if ($this->isDatabaseAvailable()) { + $this->garbageCollection(); + $query = Database::getConnection()->select($this->bin); + $query->addExpression('1'); + $result = $query->range(0, 1) + ->execute() + ->fetchField(); + return empty($result); + } } } diff --git a/core/lib/Drupal/Core/Cache/InstallBackend.php b/core/lib/Drupal/Core/Cache/InstallBackend.php deleted file mode 100644 index b507684..0000000 --- a/core/lib/Drupal/Core/Cache/InstallBackend.php +++ /dev/null @@ -1,151 +0,0 @@ -prepareDatabasePrefix(); @@ -65,10 +65,20 @@ abstract class UnitTestBase extends TestBase { if (!$this->setupDatabasePrefix) { return FALSE; } + $install_state['database_tables_exist'] = FALSE; // Set user agent to be consistent with WebTestBase. $_SERVER['HTTP_USER_AGENT'] = $this->databasePrefix; $this->setup = TRUE; } + + /** + * Removes the "no database tables" global flag. + */ + function tearDown() { + global $install_state; + parent::tearDown(); + unset($install_state['database_tables_exist']); + } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/InstallTest.php b/core/modules/system/lib/Drupal/system/Tests/Cache/InstallTest.php deleted file mode 100644 index a2199c2..0000000 --- a/core/modules/system/lib/Drupal/system/Tests/Cache/InstallTest.php +++ /dev/null @@ -1,131 +0,0 @@ - 'Cache install test', - 'description' => 'Confirm that the cache backend used for installing Drupal works correctly.', - 'group' => 'Cache', - ); - } - - /** - * 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')); - - // 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."); - } - } -}