From 00f1c9f177d0aefca3662c6a3e44cae74b01e3c2 Mon Sep 17 00:00:00 2001
From: Bob Vincent <bobvin@pillars.net>
Date: Fri, 27 May 2011 22:09:56 -0400
Subject: [PATCH] Issue #512026 by Jody Lynn, justinrandell, sun, Damien Tournoud, effulgentsia, marcingy, Eric_A, pillarsdotnet: Rename cache_form table to form_state to help developers and administrators avoid wiping unrecoverable data.

---
 includes/cache.inc                 |   29 ++++++++++++++---------------
 includes/common.inc                |    3 ++-
 includes/form.inc                  |   12 ++++++------
 includes/install.core.inc          |    1 +
 modules/system/system.install      |    9 +++++----
 modules/system/system.module       |    2 +-
 sites/default/default.settings.php |   16 +++++++++++++++-
 7 files changed, 44 insertions(+), 28 deletions(-)

diff --git a/includes/cache.inc b/includes/cache.inc
index 8666874ac6250efd71ec4ab873e6f0b39ada09b0..b0c8ff4e13fdc01d7d9bf0032c3dd0ab9d24053c 100644
--- a/includes/cache.inc
+++ b/includes/cache.inc
@@ -37,10 +37,8 @@ function _cache_get_object($bin) {
  * @param $cid
  *   The cache ID of the data to retrieve.
  * @param $bin
- *   The cache bin to store the data in. Valid core values are 'cache_block',
- *   'cache_bootstrap', 'cache_field', 'cache_filter', 'cache_form',
- *   'cache_menu', 'cache_page', 'cache_path', 'cache_update' or 'cache' for
- *   the default cache.
+ *   The cache bin to store the data in. See cache_set() for the list and
+ *   description of core bins.
  *
  * @return
  *   The cache or FALSE on failure.
@@ -73,9 +71,11 @@ function cache_get_multiple(array &$cids, $bin = 'cache') {
  * structures that get flushed together. While it is not a problem for most
  * cache bins if the entries in them are flushed before their expire time, some
  * might break functionality or are extremely expensive to recalculate. These
- * will be marked with a (*). The other bins expired automatically by core.
- * Contributed modules can add additional bins and get them expired
- * automatically by implementing hook_flush_caches().
+ * are marked with a (*). The other bins are fully flushed during
+ * drupal_flush_all_caches() and all bins are pruned of expired entries during
+ * system_cron(). Contributed modules can add additional bins and implement
+ * hook_flush_caches() to have them flushed during drupal_flush_all_caches() and
+ * pruned during system_cron().
  *
  *  - cache: Generic cache storage bin (used for variables, theme registry,
  *  locale date, list of simpletest tests etc).
@@ -86,10 +86,6 @@ function cache_get_multiple(array &$cids, $bin = 'cache') {
  *
  *  - cache_filter: Stores filtered pieces of content.
  *
- *  - cache_form(*): Stores multistep forms. Flushing this bin means that some
- *  forms displayed to users lose their state and the data already submitted
- *  to them.
- *
  *  - cache_menu: Stores the structure of visible navigation menus per page.
  *
  *  - cache_page: Stores generated pages for anonymous users. It is flushed
@@ -104,6 +100,11 @@ function cache_get_multiple(array &$cids, $bin = 'cache') {
  *  installed on the current site. As this is different for (almost) every
  *  site, it's very expensive to recalculate for the update server.
  *
+ *  - form_state(*): Stores multistep forms. This bin is named without the
+ *  'cache_' prefix, because it stores data that cannot be regenerated: flushing
+ *  this bin means that some forms displayed to users lose their state and the
+ *  data already submitted to them.
+ *
  * The reasons for having several bins are as follows:
  *
  * - smaller bins mean smaller database tables and allow for faster selects and
@@ -120,10 +121,8 @@ function cache_get_multiple(array &$cids, $bin = 'cache') {
  *   serialized before insertion.
  *   Strings will be stored as plain text and not serialized.
  * @param $bin
- *   The cache bin to store the data in. Valid core values are 'cache_block',
- *   'cache_bootstrap', 'cache_field', 'cache_filter', 'cache_form',
- *   'cache_menu', 'cache_page', 'cache_update' or 'cache' for the default
- *   cache.
+ *   The cache bin to store the data in. See above for the list and description
+ *   of core bins.
  * @param $expire
  *   One of the following values:
  *   - CACHE_PERMANENT: Indicates that the item should never be removed unless
diff --git a/includes/common.inc b/includes/common.inc
index 95e03e8cc93e6d41ee5780e9019bd9e50607f547..ce07166d3cdb1d9fba65cc164104ecaf7d5a5dfe 100644
--- a/includes/common.inc
+++ b/includes/common.inc
@@ -7126,7 +7126,8 @@ function drupal_flush_all_caches() {
   // Synchronize to catch any actions that were added or removed.
   actions_synchronize();
 
-  // Don't clear cache_form - in-progress form submissions may break.
+  // The 'form_state' cache bin must not be flushed here. See the description
+  // of the cache bins in the cache_set() documentation for details.
   // Ordered so clearing the page cache will always be the last action.
   $core = array('cache', 'cache_filter', 'cache_bootstrap', 'cache_page');
   $cache_tables = array_merge(module_invoke_all('flush_caches'), $core);
diff --git a/includes/form.inc b/includes/form.inc
index c0e2ec7c1556098585935ecb579fd0dc203d6d73..491bfc5f8f746f471de0a60578a7fd43b8ed0d3c 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -469,12 +469,12 @@ function drupal_rebuild_form($form_id, &$form_state, $old_form = NULL) {
  * Fetch a form from cache.
  */
 function form_get_cache($form_build_id, &$form_state) {
-  if ($cached = cache_get('form_' . $form_build_id, 'cache_form')) {
+  if ($cached = cache_get('form_' . $form_build_id, 'form_state')) {
     $form = $cached->data;
 
     global $user;
     if ((isset($form['#cache_token']) && drupal_valid_token($form['#cache_token'])) || (!isset($form['#cache_token']) && !$user->uid)) {
-      if ($cached = cache_get('form_state_' . $form_build_id, 'cache_form')) {
+      if ($cached = cache_get('form_state_' . $form_build_id, 'form_state')) {
         // Re-populate $form_state for subsequent rebuilds.
         $form_state = $cached->data + $form_state;
 
@@ -508,12 +508,12 @@ function form_set_cache($form_build_id, $form, $form_state) {
     if ($GLOBALS['user']->uid) {
       $form['#cache_token'] = drupal_get_token();
     }
-    cache_set('form_' . $form_build_id, $form, 'cache_form', REQUEST_TIME + $expire);
+    cache_set('form_' . $form_build_id, $form, 'form_state', REQUEST_TIME + $expire);
   }
 
   // Cache form state.
   if ($data = array_diff_key($form_state, array_flip(form_state_keys_no_cache()))) {
-    cache_set('form_state_' . $form_build_id, $data, 'cache_form', REQUEST_TIME + $expire);
+    cache_set('form_state_' . $form_build_id, $data, 'form_state', REQUEST_TIME + $expire);
   }
 }
 
@@ -832,8 +832,8 @@ function drupal_process_form($form_id, &$form, &$form_state) {
       // here, as we've finished with them. The in-memory copies are still
       // here, though.
       if (!variable_get('cache', 0) && !empty($form_state['values']['form_build_id'])) {
-        cache_clear_all('form_' . $form_state['values']['form_build_id'], 'cache_form');
-        cache_clear_all('form_state_' . $form_state['values']['form_build_id'], 'cache_form');
+        cache_clear_all('form_' . $form_state['values']['form_build_id'], 'form_state');
+        cache_clear_all('form_state_' . $form_state['values']['form_build_id'], 'form_state');
       }
 
       // If batches were set in the submit handlers, we process them now,
diff --git a/includes/install.core.inc b/includes/install.core.inc
index a74dfdf0f37e5776b6f9187493d288a69194b93b..9c4935e6006c47d6abe4917bc46a0f52d18dd3c4 100644
--- a/includes/install.core.inc
+++ b/includes/install.core.inc
@@ -273,6 +273,7 @@ function install_begin_request(&$install_state) {
   require_once DRUPAL_ROOT . '/includes/cache.inc';
   require_once DRUPAL_ROOT . '/includes/cache-install.inc';
   $conf['cache_default_class'] = 'DrupalFakeCache';
+  $conf['cache_class_form_state'] = 'DrupalFakeCache';
 
   // Prepare for themed output. We need to run this at the beginning of the
   // page request to avoid a different theme accidentally getting set. (We also
diff --git a/modules/system/system.install b/modules/system/system.install
index 6d2fc80975af0363ae3ffde7164262ab378d2211..4be575dcafa291875af91836f868ab790bf6ac7c 100644
--- a/modules/system/system.install
+++ b/modules/system/system.install
@@ -690,14 +690,14 @@ function system_schema() {
   );
   $schema['cache_bootstrap'] = $schema['cache'];
   $schema['cache_bootstrap']['description'] = 'Cache table for data required to bootstrap Drupal, may be routed to a shared memory cache.';
-  $schema['cache_form'] = $schema['cache'];
-  $schema['cache_form']['description'] = 'Cache table for the form system to store recently built forms and their storage data, to be used in subsequent page requests.';
   $schema['cache_page'] = $schema['cache'];
   $schema['cache_page']['description'] = 'Cache table used to store compressed pages for anonymous users, if page caching is enabled.';
   $schema['cache_menu'] = $schema['cache'];
   $schema['cache_menu']['description'] = 'Cache table for the menu system to store router information as well as generated link trees for various menu/page/user combinations.';
   $schema['cache_path'] = $schema['cache'];
   $schema['cache_path']['description'] = 'Cache table for path alias lookup.';
+  $schema['form_state'] = $schema['cache'];
+  $schema['form_state']['description'] = 'Non-volatile cache table for the form system to store recently built forms and their storage data, to be used in subsequent page requests.';
 
   $schema['date_format_type'] = array(
     'description' => 'Stores configured date format types.',
@@ -1632,10 +1632,11 @@ function system_update_last_removed() {
  */
 
 /**
- * Placeholder update to set the schema version to 8000.
+ * Rename {cache_form} to {form_state}.
+ * This table should not be cleared when flushing caches.
  */
 function system_update_8000() {
-  // Fill in the first update to Drupal 8 when needed.
+  db_rename_table('cache_form', 'form_state');
 }
 
 /**
diff --git a/modules/system/system.module b/modules/system/system.module
index 3ebc65706702b56310fc9b9916f96b8dad9decae..3f48d7ec8e966cffaa13ae3ab4fdcd44d0b1fbf5 100644
--- a/modules/system/system.module
+++ b/modules/system/system.module
@@ -2996,7 +2996,7 @@ function system_cron() {
     }
   }
 
-  $core = array('cache', 'cache_filter', 'cache_page', 'cache_form', 'cache_menu');
+  $core = array('cache', 'cache_filter', 'cache_page', 'form_state', 'cache_menu');
   $cache_tables = array_merge(module_invoke_all('flush_caches'), $core);
   foreach ($cache_tables as $table) {
     cache_clear_all(NULL, $table);
diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php
index 0472f02b1c3bdf1344cf9ddad90e302d799083c4..4e11e520f9bc13d436bd7356321190173cef5f41 100644
--- a/sites/default/default.settings.php
+++ b/sites/default/default.settings.php
@@ -405,7 +405,6 @@ ini_set('session.cookie_lifetime', 2000000);
 # );
 
 /**
- *
  * IP blocking:
  *
  * To bypass database queries for denied IP addresses, use this setting.
@@ -442,3 +441,18 @@ ini_set('session.cookie_lifetime', 2000000);
  * Remove the leading hash signs to disable.
  */
 # $conf['allow_authorize_operations'] = FALSE;
+
+/**
+ * Cache configuration.
+ *
+ * Persistent caches are split up into several cache bins. Each cache bin can
+ * use a different backend cache implementation. Modify the configuration
+ * below to use a different backend implementation. Core provides two default
+ * implementations:
+ * - DrupalDatabaseCache: The standard database-backed cache.
+ * - DrupalFakeCache: A black-hole cache implementation, which does nothing.
+ */
+$conf['cache_default_class'] = 'DrupalDatabaseCache';
+// Unlike other cache bins, 'form_state' data cannot be regenerated; already
+// submitted user input would be lost. Use non-volatile implementations only.
+$conf['cache_class_form_state'] = 'DrupalDatabaseCache';
-- 
1.7.4.1

