From 9c88047ff7384bd31173afffdef466cc57cae826 Mon Sep 17 00:00:00 2001
From: Bob Vincent <bobvin@pillars.net>
Date: Sat, 1 Oct 2011 23:35:57 -0400
Subject: [PATCH] Issue #1015946 by Damien Tournoud, catch, pillarsdotnet:
 Eliminate $user->cache and {session}.cache in favor of
 $_SESSION['cache_expiration'][$bin]

---
 includes/bootstrap.inc        |    1 -
 includes/cache.inc            |   47 +++++++++++++++++++++++++++-------------
 includes/session.inc          |    1 -
 modules/system/system.install |   13 ++++++-----
 4 files changed, 39 insertions(+), 23 deletions(-)

diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc
index 794f908798c37b6ca0e6a6ab8c281a098543bfc6..a48c40b13d304ab2bf58b83fd1cd851827d7b21a 100644
--- a/includes/bootstrap.inc
+++ b/includes/bootstrap.inc
@@ -2052,7 +2052,6 @@ function drupal_anonymous_user() {
   $user->hostname = ip_address();
   $user->roles = array();
   $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
-  $user->cache = 0;
   return $user;
 }
 
diff --git a/includes/cache.inc b/includes/cache.inc
index caa5c032db70d59e68c42e327b4d00f111e3352c..d5611fde734d66d40167a3daeceefc402e20ac34 100644
--- a/includes/cache.inc
+++ b/includes/cache.inc
@@ -462,17 +462,15 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
     if (!isset($cache->data)) {
       return FALSE;
     }
-    // If enforcing a minimum cache lifetime, validate that the data is
-    // currently valid for this user before we return it by making sure the cache
-    // entry was created before the timestamp in the current session's cache
-    // timer. The cache variable is loaded into the $user object by _drupal_session_read()
-    // in session.inc. If the data is permanent or we're not enforcing a minimum
-    // cache lifetime always return the cached data.
-    if ($cache->expire != CACHE_PERMANENT && variable_get('cache_lifetime', 0) && $user->cache > $cache->created) {
-      // This cache data is too old and thus not valid for us, ignore it.
+    // If the cached data is temporary and subject to a per-user minimum
+    // lifetime, compare the cache entry timestamp with the user session
+    // cache_expiration timestamp. If the cache entry is too old, ignore it.
+    if ($cache->expire != CACHE_PERMANENT && variable_get('cache_lifetime', 0) && isset($_SESSION['cache_expiration'][$this->bin]) && $_SESSION['cache_expiration'][$this->bin] > $cache->created) {
+      // This cached data is too old; ignore it.
       return FALSE;
     }
-
+    // If the data is permanent or not subject to a minimum cache lifetime,
+    // unserialize and return the cached data.
     if ($cache->serialized) {
       $cache->data = unserialize($cache->data);
     }
@@ -534,11 +532,10 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
 
   function expire() {
     if (variable_get('cache_lifetime', 0)) {
-      // We store the time in the current user's $user->cache variable which
-      // will be saved into the sessions bin by _drupal_session_write(). We then
-      // simulate that the cache was flushed for this user by not returning
-      // cached data that was cached before the timestamp.
-      $GLOBALS['user']->cache = REQUEST_TIME;
+      // We store the time in the current user's session. We then simulate
+      // that the cache was flushed for this user by not returning cached
+      // data that was cached before the timestamp.
+      $_SESSION['cache_expiration'][$this->bin] = REQUEST_TIME;
 
       $cache_flush = variable_get('cache_flush_' . $this->bin, 0);
       if ($cache_flush == 0) {
@@ -565,8 +562,28 @@ class DrupalDatabaseCache implements DrupalCacheInterface {
   }
 
   function garbageCollection() {
-    global $user;
+    $cache_lifetime = variable_get('cache_lifetime', 0);
 
+    // Clean-up the per-user cache flush session data, so that the session
+    // handler can properly clean-up the session for anonymous users.
+    if (isset($_SESSION['cache_flush'])) {
+      $expire = REQUEST_TIME - $cache_lifetime;
+      foreach ($_SESSION['cache_expiration'] as $bin => $timestamp) {
+        if ($timestamp < $expire) {
+          unset($_SESSION['cache_expiration'][$bin]);
+        }
+      }
+      if (!$_SESSION['cache_expiration']) {
+        unset($_SESSION['cache_expiration']);
+      }
+    }
+
+    // Garbage collection of temporary items is only necessary when enforcing
+    // a minimum cache lifetime.
+    $cache_lifetime = variable_get('cache_lifetime', 0);
+    if (!$cache_lifetime) {
+      return;
+    }
     // When cache lifetime is in force, avoid running garbage collection too
     // often since this will remove temporary cache items indiscriminately.
     $cache_flush = variable_get('cache_flush_' . $this->bin, 0);
diff --git a/includes/session.inc b/includes/session.inc
index fd04de8753b45e8c83cc0acd43a1fc013735cbf2..6af4643d695f408da4f3569e933c7cee8b48942f 100644
--- a/includes/session.inc
+++ b/includes/session.inc
@@ -176,7 +176,6 @@ function _drupal_session_write($sid, $value) {
       // Either ssid or sid or both will be added from $key below.
       $fields = array(
         'uid' => $user->uid,
-        'cache' => isset($user->cache) ? $user->cache : 0,
         'hostname' => ip_address(),
         'session' => $value,
         'timestamp' => REQUEST_TIME,
diff --git a/modules/system/system.install b/modules/system/system.install
index 24933e2d9b698fdbbfd330feb256bf11279e698a..da40c2b1a679278677d4bec765c4556153811069 100644
--- a/modules/system/system.install
+++ b/modules/system/system.install
@@ -1470,12 +1470,6 @@ function system_schema() {
         'not null' => TRUE,
         'default' => 0,
       ),
-      'cache' => array(
-        'description' => "The time of this user's last post. This is used when the site has specified a minimum_cache_lifetime. See cache_get().",
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0,
-      ),
       'session' => array(
         'description' => 'The serialized contents of $_SESSION, an array of name/value pairs that persists across page requests by this session ID. Drupal loads $_SESSION from here at the start of each request and saves it at the end.',
         'type' => 'blob',
@@ -1642,6 +1636,13 @@ function system_update_8001() {
 }
 
 /**
+ * Remove the obsolete {session}.cache column.
+ */
+function system_update_8002() {
+  db_drop_field('session', 'cache');
+}
+
+/**
  * @} End of "defgroup updates-7.x-to-8.x"
  * The next series of updates should start at 9000.
  */
-- 
1.7.5.4

