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