Add Stack driver to horde/Cache package
authorMichael M Slusarz <slusarz@curecanti.org>
Tue, 18 May 2010 19:43:38 +0000 (13:43 -0600)
committerMichael M Slusarz <slusarz@curecanti.org>
Tue, 18 May 2010 19:47:11 +0000 (13:47 -0600)
framework/Cache/lib/Horde/Cache/Sql.php
framework/Cache/lib/Horde/Cache/Stack.php [new file with mode: 0644]
framework/Cache/package.xml
framework/Core/lib/Horde/Core/Binder/Cache.php

index e540e05..abb8b31 100644 (file)
@@ -38,13 +38,6 @@ class Horde_Cache_Sql extends Horde_Cache_Base
     protected $_db;
 
     /**
-     * The memory cache object to use, if configured.
-     *
-     * @var Horde_Cache
-     */
-    protected $_mc = null;
-
-    /**
      * Constructor.
      *
      * @param array $params  Parameters:
@@ -52,8 +45,6 @@ class Horde_Cache_Sql extends Horde_Cache_Base
      * 'db' - (Horde_Db_Adapter_Base) [REQUIRED] The DB instance.
      * 'table' - (string) The name of the cache table.
      *           DEFAULT: 'horde_cache'
-     * 'use_memorycache' - (Horde_Cache) Use this memory caching object to
-     *                     cache the data (to avoid DB accesses).
      * </pre>
      *
      * @throws Horde_Cache_Exception
@@ -64,12 +55,7 @@ class Horde_Cache_Sql extends Horde_Cache_Base
             throw new Horde_Cache_Exception('Missing db parameter.');
         }
         $this->_db = $params['db'];
-
-        if (isset($params['use_memorycache'])) {
-            $this->_mc = $params['use_memorycache'];
-        }
-
-        unset($params['db'], $params['use_memorycache']);
+        unset($params['db']);
 
         $params = array_merge(array(
             'table' => 'horde_cache',
@@ -111,13 +97,6 @@ class Horde_Cache_Sql extends Horde_Cache_Base
         $okey = $key;
         $key = hash('md5', $key);
 
-        if ($this->_mc) {
-            $data = $this->_mc->get($key, $lifetime);
-            if ($data !== false) {
-                return $data;
-            }
-        }
-
         $timestamp = time();
         $maxage = $timestamp - $lifetime;
 
@@ -146,10 +125,6 @@ class Horde_Cache_Sql extends Horde_Cache_Base
             return false;
         }
 
-        if ($this->_mc) {
-            $this->_mc->set($key, $result);
-        }
-
         if ($this->_logger) {
             $this->_logger->log(sprintf('Cache hit: %s (Id %s newer than %d)', $okey, $key, $maxage), 'DEBUG');
         }
@@ -172,10 +147,6 @@ class Horde_Cache_Sql extends Horde_Cache_Base
         $okey = $key;
         $key = hash('md5', $key);
 
-        if ($this->_mc) {
-            $this->_mc->set($key, $data);
-        }
-
         $timestamp = time();
 
         // 0 lifetime indicates the object should not be GC'd.
@@ -224,10 +195,6 @@ class Horde_Cache_Sql extends Horde_Cache_Base
         $okey = $key;
         $key = hash('md5', $key);
 
-        if ($this->_mc && $this->_mc->exists($key, $lifetime)) {
-            return true;
-        }
-
         /* Build SQL query. */
         $query = 'SELECT 1 FROM ' . $this->_params['table'] .
                  ' WHERE cache_id = ?';
@@ -271,10 +238,6 @@ class Horde_Cache_Sql extends Horde_Cache_Base
     {
         $key = hash('md5', $key);
 
-        if ($this->_mc) {
-            $this->_mc->expire($key);
-        }
-
         $query = 'DELETE FROM ' . $this->_params['table'] .
                  ' WHERE cache_id = ?';
         $values = array($key);
diff --git a/framework/Cache/lib/Horde/Cache/Stack.php b/framework/Cache/lib/Horde/Cache/Stack.php
new file mode 100644 (file)
index 0000000..8cdc74b
--- /dev/null
@@ -0,0 +1,154 @@
+<?php
+/**
+ * Horde_Cache_Stack:: is a Cache implementation that will loop through a
+ * given list of Cache drivers to search for a cached value.  This driver
+ * allows for use of caching backends on top of persistent backends.
+ *
+ * Copyright 2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @package  Cache
+ */
+class Horde_Cache_Stack extends Horde_Cache_Base
+{
+    /**
+     * Stack of cache drivers.
+     *
+     * @var string
+     */
+    protected $_stack = array();
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Parameters:
+     * <pre>
+     * 'stack' - (array) [REQUIRED] A list of cache drivers to loop
+     *           through, in order of priority. The last entry is considered
+     *           the 'master' driver, for purposes of writes.
+     *           Each value should contain an array with two keys: 'driver', a
+     *           string value with the Cache driver to use, and 'params',
+     *           containing any parameters needed by this driver.
+     * </pre>
+     *
+     * @throws InvalidArgumentException
+     */
+    public function __construct(array $params = array())
+    {
+        if (!isset($params['stack'])) {
+            throw new InvalidArgumentException('Missing stack parameter.');
+        }
+
+        foreach ($params['stack'] as $val) {
+            $this->_stack[] = Horde_Cache::factory($val['driver'], $val['params']);
+        }
+
+        unset($params['stack']);
+
+        parent::__construct($params);
+    }
+
+    /**
+     * Attempts to retrieve a cached object and return it to the
+     * caller.
+     *
+     * @param string $key        Object ID to query.
+     * @param integer $lifetime  Lifetime of the object in seconds.
+     *
+     * @return mixed  Cached data, or false if none was found.
+     */
+    public function get($key, $lifetime = 1)
+    {
+        foreach ($this->_stack as $val) {
+            $result = $val->get($key, $lifetime);
+            if ($result !== false) {
+                break;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Attempts to store an object in the cache.
+     *
+     * @param string $key        Object ID used as the caching key.
+     * @param mixed $data        Data to store in the cache.
+     * @param integer $lifetime  Object lifetime - i.e. the time before the
+     *                           data becomes available for garbage
+     *                           collection.  If null use the default Horde GC
+     *                           time.  If 0 will not be GC'd.
+     *
+     * @return boolean  True on success, false on failure.
+     */
+    public function set($key, $data, $lifetime = null)
+    {
+        /* Do writes in *reverse* order - it is OK if a write to one of the
+         * non-master backends fails. */
+        $master = true;
+
+        foreach (array_reverse($this->_stack) as $val) {
+            $result = $val->set($key, $data, $lifetime);
+            if ($result === false) {
+                if ($master) {
+                    return false;
+                }
+
+                /* Attempt to invalidate cache if write failed. */
+                $val->expire($id);
+            }
+            $master = false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks if a given key exists in the cache, valid for the given
+     * lifetime.
+     *
+     * @param string $key        Cache key to check.
+     * @param integer $lifetime  Lifetime of the key in seconds.
+     *
+     * @return boolean  Existence.
+     */
+    public function exists($key, $lifetime = 1)
+    {
+        foreach ($this->_stack as $val) {
+            $result = $val->exists($key, $lifetime);
+            if ($result === true) {
+                break;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     * Expire any existing data for the given key.
+     *
+     * @param string $key  Cache key to expire.
+     *
+     * @return boolean  Success or failure.
+     */
+    public function expire($key)
+    {
+        /* Only report success from master. */
+        $master = $success = true;
+
+        foreach (array_reverse($this->_stack) as $val) {
+            $result = $val->expire($id);
+            if ($master && ($result === false)) {
+                $success = false;
+            }
+            $master = false;
+        }
+
+        return $success;
+    }
+
+}
index e6aa6f7..78e63a0 100644 (file)
@@ -53,6 +53,7 @@ Performance Suite&apos;s content cache), memcached, or an SQL table.
       <file name="Mock.php" role="php" />
       <file name="Null.php" role="php" />
       <file name="Sql.php" role="php" />
+      <file name="Stack.php" role="php" />
       <file name="Xcache.php" role="php" />
       <file name="Zps4.php" role="php" />
      </dir> <!-- /lib/Horde/Cache -->
@@ -87,15 +88,16 @@ Performance Suite&apos;s content cache), memcached, or an SQL table.
     <name>Log</name>
     <channel>pear.horde.org</channel>
    </package>
+   <package>
+    <name>Memcache</name>
+    <channel>pear.horde.org</channel>
+   </package>
    <extension>
     <name>apc</name>
    </extension>
    <extension>
     <name>eaccelerator</name>
    </extension>
-   <extension>
-    <name>memcache</name>
-   </extension>
   </optional>
  </dependencies>
  <phprelease>
@@ -109,6 +111,7 @@ Performance Suite&apos;s content cache), memcached, or an SQL table.
    <install name="lib/Horde/Cache/Mock.php" as="Horde/Cache/Mock.php" />
    <install name="lib/Horde/Cache/Null.php" as="Horde/Cache/Null.php" />
    <install name="lib/Horde/Cache/Sql.php" as="Horde/Cache/Sql.php" />
+   <install name="lib/Horde/Cache/Stack.php" as="Horde/Cache/Stack.php" />
    <install name="lib/Horde/Cache/Xcache.php" as="Horde/Cache/Xcache.php" />
    <install name="lib/Horde/Cache/Zps4.php" as="Horde/Cache/Zps4.php" />
    <install name="lib/Horde/Cache.php" as="Horde/Cache.php" />
index ce31ed1..ed6d1a8 100644 (file)
@@ -7,31 +7,49 @@ class Horde_Core_Binder_Cache implements Horde_Injector_Binder
 {
     public function create(Horde_Injector $injector)
     {
-        return $this->_getCacheInstance($GLOBALS['conf']['cache']['driver'], $injector);
-    }
-
-    protected function _getCacheInstance($driver, $injector)
-    {
-        if (empty($driver) || (strcasecmp($driver, 'None') === 0)) {
+        $driver = empty($GLOBALS['conf']['cache']['driver'])
+            ? 'Null'
+            : $GLOBALS['conf']['cache']['driver'];
+        if (strcasecmp($driver, 'None') === 0) {
             $driver = 'Null';
         }
 
         $params = Horde::getDriverConfig('cache', $driver);
+        if (isset($GLOBALS['conf']['cache']['default_lifetime'])) {
+            $params['lifetime'] = $GLOBALS['conf']['cache']['default_lifetime'];
+        }
+
+        $logger = $injector->getInstance('Horde_Log_Logger');
 
         if (strcasecmp($driver, 'Memcache') === 0) {
             $params['memcache'] = $injector->getInstance('Horde_Memcache');
-        } elseif (strcasecmp($driver, 'Sql') === 0) {
-            $params['db'] = $injector->getInstance('Horde_Db_Adapter_Base');
+        } else {
+            if (strcasecmp($driver, 'Sql') === 0) {
+                $params['db'] = $injector->getInstance('Horde_Db_Adapter_Base');
+            }
 
             if (!empty($params['use_memorycache'])) {
-                $params['use_memorycache'] = $this->_getCacheInstance($params['use_memorycache'], $injector);
+                $params = array(
+                    'stack' => array(
+                        array(
+                            'driver' => 'Memcache',
+                            'params' => array_merge($params, array(
+                                'logger' => $logger,
+                                'memcache' => $injector->getInstance('Horde_Memcache')
+                            ))
+                        ),
+                        array(
+                            'driver' => $driver,
+                            'params' => array_merge($params, array(
+                                'logger' => $logger
+                             ))
+                         )
+                    )
+                );
+                $driver = 'Stack';
             }
         }
 
-        if (isset($GLOBALS['conf']['cache']['default_lifetime'])) {
-            $params['lifetime'] = $GLOBALS['conf']['cache']['default_lifetime'];
-        }
-
         $params['logger'] = $injector->getInstance('Horde_Log_Logger');
 
         return Horde_Cache::factory($driver, $params);