Abstracted storage code in Horde_Cache
authorMichael M Slusarz <slusarz@curecanti.org>
Tue, 16 Nov 2010 02:30:50 +0000 (19:30 -0700)
committerMichael M Slusarz <slusarz@curecanti.org>
Tue, 16 Nov 2010 07:01:23 +0000 (00:01 -0700)
24 files changed:
framework/Cache/lib/Horde/Cache.php
framework/Cache/lib/Horde/Cache/Apc.php [deleted file]
framework/Cache/lib/Horde/Cache/Eaccelerator.php [deleted file]
framework/Cache/lib/Horde/Cache/File.php [deleted file]
framework/Cache/lib/Horde/Cache/Memcache.php [deleted file]
framework/Cache/lib/Horde/Cache/Mock.php [deleted file]
framework/Cache/lib/Horde/Cache/Null.php [deleted file]
framework/Cache/lib/Horde/Cache/Session.php [deleted file]
framework/Cache/lib/Horde/Cache/Sql.php [deleted file]
framework/Cache/lib/Horde/Cache/Stack.php [deleted file]
framework/Cache/lib/Horde/Cache/Storage.php [new file with mode: 0644]
framework/Cache/lib/Horde/Cache/Storage/Apc.php [new file with mode: 0644]
framework/Cache/lib/Horde/Cache/Storage/Eaccelerator.php [new file with mode: 0644]
framework/Cache/lib/Horde/Cache/Storage/File.php [new file with mode: 0644]
framework/Cache/lib/Horde/Cache/Storage/Memcache.php [new file with mode: 0644]
framework/Cache/lib/Horde/Cache/Storage/Mock.php [new file with mode: 0644]
framework/Cache/lib/Horde/Cache/Storage/Null.php [new file with mode: 0644]
framework/Cache/lib/Horde/Cache/Storage/Session.php [new file with mode: 0644]
framework/Cache/lib/Horde/Cache/Storage/Sql.php [new file with mode: 0644]
framework/Cache/lib/Horde/Cache/Storage/Stack.php [new file with mode: 0644]
framework/Cache/lib/Horde/Cache/Storage/Xcache.php [new file with mode: 0644]
framework/Cache/lib/Horde/Cache/Xcache.php [deleted file]
framework/Cache/package.xml
framework/Core/lib/Horde/Core/Factory/Cache.php

index b443fed..7c9e07e 100644 (file)
@@ -1,7 +1,6 @@
 <?php
 /**
- * The Horde_Cache:: class provides the abstract class definition for
- * Horde_Cache drivers.
+ * This class provides the API interface to the cache storage drivers.
  *
  * Copyright 1999-2010 The Horde Project (http://www.horde.org/)
  *
  * @author   Chuck Hagenbuch <chuck@horde.org>
  * @author   Michael Slusarz <slusarz@horde.org>
  * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
  * @package  Cache
  */
-abstract class Horde_Cache
+class Horde_Cache
 {
     /**
      * Cache parameters.
@@ -23,8 +23,7 @@ abstract class Horde_Cache
      */
     protected $_params = array(
         'compress' => false,
-        'lifetime' => 86400,
-        'prefix' => '',
+        'lifetime' => 86400
     );
 
     /**
@@ -35,9 +34,17 @@ abstract class Horde_Cache
     protected $_logger;
 
     /**
-     * Construct a new Horde_Cache object.
+     * Storage object.
      *
-     * @param array $params  Parameter array:
+     * @var Horde_Cache_Storage
+     */
+    protected $_storage;
+
+    /**
+     * Constructor.
+     *
+     * @param Horde_Cache_Storage $storage  The storage object.
+     * @param array $params                 Parameter array:
      * <pre>
      * 'compress' - (boolean) Compress data? Requires the 'lzf' PECL
      *              extension.
@@ -47,11 +54,14 @@ abstract class Horde_Cache
      * 'logger' - (Horde_Log_Logger) Log object to use for log/debug messages.
      * </pre>
      */
-    public function __construct($params = array())
+    public function __construct(Horde_Cache_Storage $storage,
+                                array $params = array())
     {
         if (isset($params['logger'])) {
             $this->_logger = $params['logger'];
             unset($params['logger']);
+
+            $storage->setLogger($this->_logger);
         }
 
         if (!empty($params['compress']) && !extension_loaded('lzf')) {
@@ -59,6 +69,7 @@ abstract class Horde_Cache
         }
 
         $this->_params = array_merge($this->_params, $params);
+        $this->_storage = $storage;
     }
 
     /**
@@ -90,7 +101,7 @@ abstract class Horde_Cache
      */
     public function get($key, $lifetime = 1)
     {
-        $res = $this->_get($key, $lifetime);
+        $res = $this->_storage->get($key, $lifetime);
 
         return ($this->_params['compress'] && ($res !== false))
             // lzf_decompress() returns false on error
@@ -99,41 +110,28 @@ abstract class Horde_Cache
     }
 
     /**
-     * @see get()
-     */
-    abstract protected function _get($key, $lifetime);
-
-    /**
      * 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 string $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.
-     *
-     * @throws Horde_Cache_Exception
      */
     public function set($key, $data, $lifetime = null)
     {
-        if (!is_string($data)) {
-            throw new Horde_Cache_Exception('Cache data must be a string.');
-        }
-
         if ($this->_params['compress']) {
             $data = lzf_compress($data);
         }
+        $lifetime = is_null($lifetime)
+            ? $this->_params['lifetime']
+            : $lifetime;
 
-        $res = $this->_set($key, $data, $lifetime);
+        $this->_storage->set($key, $data, $lifetime);
     }
 
     /**
-     * @see set()
-     */
-    abstract protected function _set($key, $data, $lifetime);
-
-    /**
      * Checks if a given key exists in the cache, valid for the given
      * lifetime.
      *
@@ -142,7 +140,10 @@ abstract class Horde_Cache
      *
      * @return boolean  Existence.
      */
-    abstract public function exists($key, $lifetime = 1);
+    public function exists($key, $lifetime = 1)
+    {
+        return $this->_storage->exists($key, $lifetime);
+    }
 
     /**
      * Expire any existing data for the given key.
@@ -151,18 +152,9 @@ abstract class Horde_Cache
      *
      * @return boolean  Success or failure.
      */
-    abstract public function expire($key);
-
-    /**
-     * Determine the default lifetime for data.
-     *
-     * @param mixed $lifetime  The lifetime to use or null for default.
-     *
-     * @return integer  The lifetime, in seconds.
-     */
-    protected function _getLifetime($lifetime)
+    public function expire($key)
     {
-        return is_null($lifetime) ? $this->_params['lifetime'] : $lifetime;
+        return $this->_storage->expire($key);
     }
 
 }
diff --git a/framework/Cache/lib/Horde/Cache/Apc.php b/framework/Cache/lib/Horde/Cache/Apc.php
deleted file mode 100644 (file)
index 324ceb4..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-<?php
-/**
- * The Horde_Cache_Apc:: class provides an Alternative PHP Cache
- * implementation of the Horde caching system.
- *
- * Copyright 2006-2007 Duck <duck@obala.net>
- *
- * 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   Duck <duck@obala.net>
- * @category Horde
- * @package  Cache
- */
-class Horde_Cache_Apc extends Horde_Cache
-{
-    /**
-     */
-    protected function _get($key, $lifetime)
-    {
-        $key = $this->_params['prefix'] . $key;
-        $this->_setExpire($key, $lifetime);
-        return apc_fetch($key);
-    }
-
-    /**
-     */
-    protected function _set($key, $data, $lifetime)
-    {
-        $key = $this->_params['prefix'] . $key;
-        $lifetime = $this->_getLifetime($lifetime);
-        if (apc_store($key . '_expire', time(), $lifetime)) {
-            apc_store($key, $data, $lifetime);
-        }
-    }
-
-    /**
-     * 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)
-    {
-        $key = $this->_params['prefix'] . $key;
-        $this->_setExpire($key, $lifetime);
-        return (apc_fetch($key) !== false);
-    }
-
-    /**
-     * Expire any existing data for the given key.
-     *
-     * @param string $key  Cache key to expire.
-     *
-     * @return boolean  Success or failure.
-     */
-    public function expire($key)
-    {
-        $key = $this->_params['prefix'] . $key;
-        apc_delete($key . '_expire');
-        return apc_delete($key);
-    }
-
-    /**
-     * Set expire time on each call since APC sets it on cache creation.
-     *
-     * @param string $key        Cache key to expire.
-     * @param integer $lifetime  Lifetime of the data in seconds.
-     */
-    protected function _setExpire($key, $lifetime)
-    {
-        $key = $this->_params['prefix'] . $key;
-        if ($lifetime == 0) {
-            // Don't expire.
-            return;
-        }
-
-        $expire = apc_fetch($key . '_expire');
-
-        // Set prune period.
-        if ($expire + $lifetime < time()) {
-            // Expired
-            apc_delete($key);
-            apc_delete($key . '_expire');
-        }
-    }
-
-}
diff --git a/framework/Cache/lib/Horde/Cache/Eaccelerator.php b/framework/Cache/lib/Horde/Cache/Eaccelerator.php
deleted file mode 100644 (file)
index 2a911f8..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-<?php
-/**
- * The Horde_Cache_Eaccelerator:: class provides a eAccelerator content cache
- * (version 0.9.5+) implementation of the Horde caching system.
- *
- * Copyright 2006-2007 Duck <duck@obala.net>
- *
- * 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   Duck <duck@obala.net>
- * @category Horde
- * @package  Cache
- */
-class Horde_Cache_Eaccelerator extends Horde_Cache
-{
-    /**
-     * Construct a new Horde_Cache object.
-     *
-     * @param array $params  Parameter array.
-     *
-     * @throws Horde_Cache_Exception
-     */
-    public function __construct($params = array())
-    {
-        if (!function_exists('eaccelerator_gc')) {
-            throw new Horde_Cache_Exception('eAccelerator must be compiled with support for shared memory to use as caching backend.');
-        }
-
-        parent::__construct($params);
-    }
-
-    /**
-     */
-    protected function _get($key, $lifetime)
-    {
-        $key = $this->_params['prefix'] . $key;
-        $this->_setExpire($key, $lifetime);
-        return eaccelerator_get($key);
-    }
-
-    /**
-     */
-    protected function _set($key, $data, $lifetime)
-    {
-        $key = $this->_params['prefix'] . $key;
-        $lifetime = $this->_getLifetime($lifetime);
-        if (eaccelerator_put($key . '_expire', time(), $lifetime)) {
-            eaccelerator_put($key, $data, $lifetime);
-        }
-    }
-
-    /**
-     * 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)
-    {
-        $key = $this->_params['prefix'] . $key;
-        $this->_setExpire($key, $lifetime);
-        return eaccelerator_get($key) !== false;
-    }
-
-    /**
-     * Expire any existing data for the given key.
-     *
-     * @param string $key  Cache key to expire.
-     *
-     * @return boolean  Success or failure.
-     */
-    public function expire($key)
-    {
-        $key = $this->_params['prefix'] . $key;
-        eaccelerator_rm($key . '_expire');
-        return eaccelerator_rm($key);
-    }
-
-    /**
-     * Set expire time on each call since eAccelerator sets it on
-     * cache creation.
-     *
-     * @param string $key        Cache key to expire.
-     * @param integer $lifetime  Lifetime of the data in seconds.
-     */
-    protected function _setExpire($key, $lifetime)
-    {
-        if ($lifetime == 0) {
-            // Don't expire.
-            return;
-        }
-
-        $key = $this->_params['prefix'] . $key;
-        $expire = eaccelerator_get($key . '_expire');
-
-        // Set prune period.
-        if ($expire + $lifetime < time()) {
-            // Expired
-            eaccelerator_rm($key);
-            eaccelerator_rm($key . '_expire');
-        }
-    }
-
-}
diff --git a/framework/Cache/lib/Horde/Cache/File.php b/framework/Cache/lib/Horde/Cache/File.php
deleted file mode 100644 (file)
index d8fae09..0000000
+++ /dev/null
@@ -1,249 +0,0 @@
-<?php
-/**
- * This class provides a filesystem implementation of the Horde caching
- * system.
- *
- * Copyright 1999-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   Anil Madhavapeddy <anil@recoil.org>
- * @author   Chuck Hagenbuch <chuck@horde.org>
- * @category Horde
- * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
- * @package  Cache
- */
-class Horde_Cache_File extends Horde_Cache
-{
-    /* Location of the garbage collection data file. */
-    const GC_FILE = 'horde_cache_gc';
-
-    /**
-     * The location of the temp directory.
-     *
-     * @var string
-     */
-    protected $_dir;
-
-    /**
-     * List of key to filename mappings.
-     *
-     * @var array
-     */
-    protected $_file = array();
-
-    /**
-     * Constructor.
-     *
-     * @param array $params  Optional parameters:
-     * <pre>
-     * 'dir' - (string) The base directory to store the cache files in.
-     *         DEFAULT: System default
-     * 'prefix' - (string) The filename prefix to use for the cache files.
-     *            DEFAULT: 'cache_'
-     * 'sub' - (integer) If non-zero, the number of subdirectories to create
-     *         to store the file (i.e. PHP's session.save_path).
-     *         DEFAULT: 0
-     * </pre>
-     */
-    public function __construct(array $params = array())
-    {
-        $params = array_merge(array(
-            'prefix' => 'cache_',
-            'sub' => 0
-        ), $params);
-
-        $this->_dir = (isset($params['dir']) && @is_dir($params['dir']))
-            ? $params['dir']
-            : Horde_Util::getTempDir();
-
-        parent::__construct($params);
-    }
-
-    /**
-     * Destructor.
-     */
-    public function __destruct()
-    {
-        /* Only do garbage collection 0.1% of the time we create an object. */
-        if (rand(0, 999) != 0) {
-            return;
-        }
-
-        $filename = $this->_dir . '/' . self::GC_FILE;
-        $excepts = array();
-
-        if (file_exists($filename)) {
-            $gc_file = file($filename, FILE_IGNORE_NEW_LINES);
-            reset($gc_file);
-            next($gc_file);
-            while (list(,$data) = each($gc_file)) {
-                $parts = explode("\t", $data, 2);
-                $excepts[$parts[0]] = $parts[1];
-            }
-        }
-
-        try {
-            $it = empty($this->_params['sub'])
-                ? new DirectoryIterator($this->_dir)
-                : new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->_dir), RecursiveIteratorIterator::CHILD_FIRST);
-        } catch (UnexpectedValueException $e) {
-            return;
-        }
-
-        $c_time = time();
-
-        foreach ($it as $key => $val) {
-            if (!$val->isDir() &&
-                (strpos($val->getFilename(), $this->_params['prefix']) === 0)) {
-                $d_time = isset($excepts[$key])
-                    ? $excepts[$key]
-                    : $this->_params['lifetime'];
-
-                if (!empty($d_time) &&
-                    (($c_time - $d_time) > filemtime($key))) {
-                    @unlink($key);
-                    unset($excepts[$key]);
-                }
-            }
-        }
-
-        $fp = fopen($filename, 'w');
-        foreach ($excepts as $key => $val) {
-            fwrite($fp, $key . "\t" . $val . "\n");
-        }
-        fclose($fp);
-    }
-
-    /**
-     */
-    protected function _get($key, $lifetime)
-    {
-        if (!$this->exists($key, $lifetime)) {
-            /* Nothing cached, return failure. */
-            return false;
-        }
-
-        $filename = $this->_keyToFile($key);
-        $size = filesize($filename);
-
-        return $size
-            ? @file_get_contents($filename)
-            : '';
-    }
-
-    /**
-     */
-    protected function _set($key, $data, $lifetime)
-    {
-        $filename = $this->_keyToFile($key, true);
-        $tmp_file = Horde_Util::getTempFile('HordeCache', true, $this->_dir);
-        if (isset($this->_params['umask'])) {
-            chmod($tmp_file, 0666 & ~$this->_params['umask']);
-        }
-
-        if (file_put_contents($tmp_file, $data) === false) {
-            throw new Horde_Cache_Exception('Cannot write to cache directory ' . $this->_dir);
-        }
-
-        @rename($tmp_file, $filename);
-
-        $lifetime = $this->_getLifetime($lifetime);
-        if (($lifetime != $this->_params['lifetime']) &&
-            ($fp = @fopen($this->_dir . '/horde_cache_gc', 'a'))) {
-            // This may result in duplicate entries in horde_cache_gc, but we
-            // will take care of these whenever we do GC and this is quicker
-            // than having to check every time we access the file.
-            fwrite($fp, $filename . "\t" . (empty($lifetime) ? 0 : time() + $lifetime) . "\n");
-            fclose($fp);
-        }
-    }
-
-    /**
-     */
-    public function exists($key, $lifetime = 1)
-    {
-        $filename = $this->_keyToFile($key);
-
-        /* Key exists in the cache */
-        if (file_exists($filename)) {
-            /* 0 means no expire.
-             * Also, If the file was been created after the supplied value,
-             * the data is valid (fresh). */
-            if (($lifetime == 0) ||
-                (time() - $lifetime <= filemtime($filename))) {
-                return true;
-            }
-
-            @unlink($filename);
-        }
-
-        return false;
-    }
-
-    /**
-     */
-    public function expire($key)
-    {
-        $filename = $this->_keyToFile($key);
-        return @unlink($filename);
-    }
-
-    /**
-     */
-    public function output($key, $lifetime = 1)
-    {
-        if (!$this->exists($key, $lifetime)) {
-            return false;
-        }
-
-        $filename = $this->_keyToFile($key);
-        return @readfile($filename);
-    }
-
-    /**
-     * Map a cache key to a unique filename.
-     *
-     * @param string $key     Cache key.
-     * @param string $create  Create path if it doesn't exist?
-     *
-     * @return string  Fully qualified filename.
-     */
-    protected function _keyToFile($key, $create = false)
-    {
-        if ($create || !isset($this->_file[$key])) {
-            $dir = $this->_dir . '/';
-            $md5 = hash('md5', $key);
-            $sub = '';
-
-            if (!empty($this->_params['sub'])) {
-                $max = min($this->_params['sub'], strlen($md5));
-                for ($i = 0; $i < $max; $i++) {
-                    $sub .= $md5[$i];
-                    if ($create && !is_dir($dir . $sub)) {
-                        if (!mkdir($dir . $sub)) {
-                            $sub = '';
-                            break;
-                        }
-                    }
-                    $sub .= '/';
-                }
-            }
-
-            $this->_file[$key] = $dir . $sub . $this->_params['prefix'] . $md5;
-        }
-
-        return $this->_file[$key];
-    }
-
-    /**
-     * TODO
-     *
-     * @throws Horde_Cache_Exception
-     */
-    protected function _gcDir($dir, &$excepts)
-    {
-    }
-
-}
diff --git a/framework/Cache/lib/Horde/Cache/Memcache.php b/framework/Cache/lib/Horde/Cache/Memcache.php
deleted file mode 100644 (file)
index bb90bc8..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-<?php
-/**
- * The Horde_Cache_Memcache:: class provides a memcached implementation of the
- * Horde caching system.
- *
- * Copyright 2006-2007 Duck <duck@obala.net>
- * Copyright 2007-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   Duck <duck@obala.net>
- * @author   Michael Slusarz <slusarz@horde.org>
- * @category Horde
- * @package  Cache
- */
-class Horde_Cache_Memcache extends Horde_Cache
-{
-    /**
-     * Memcache object.
-     *
-     * @var Horde_Memcache
-     */
-    protected $_memcache;
-
-    /**
-     * Cache results of expire() calls (since we will get the entire object
-     * on an expire() call anyway).
-     */
-    protected $_expirecache = array();
-
-    /**
-     * Construct a new Horde_Cache_Memcache object.
-     *
-     * @param array $params  Parameter array:
-     * <pre>
-     * 'memcache' - (Horde_Memcache) A Horde_Memcache object.
-     * </pre>
-     *
-     * @throws InvalidArgumentException
-     */
-    public function __construct($params = array())
-    {
-        if (!isset($params['memcache'])) {
-            throw new InvalidArgumentException('Missing memcache object');
-        }
-
-        $this->_memcache = $params['memcache'];
-
-        parent::__construct($params);
-    }
-
-    /**
-     * Do cleanup prior to serialization and provide a list of variables
-     * to serialize.
-     */
-    public function __sleep()
-    {
-        return array('_memcache');
-    }
-
-    /**
-     */
-    protected function _get($key, $lifetime)
-    {
-        $key = $this->_params['prefix'] . $key;
-        if (isset($this->_expirecache[$key])) {
-            return $this->_expirecache[$key];
-        }
-
-        $key_list = array($key);
-        if (!empty($lifetime)) {
-            $key_list[] = $key . '_e';
-        }
-
-        $res = $this->_memcache->get($key_list);
-
-        if ($res === false) {
-            unset($this->_expirecache[$key]);
-        } else {
-            // If we can't find the expire time, assume we have exceeded it.
-            if (empty($lifetime) ||
-                (($res[$key . '_e'] !== false) && ($res[$key . '_e'] + $lifetime > time()))) {
-                $this->_expirecache[$key] = $res[$key];
-            } else {
-                $res[$key] = false;
-                $this->expire($key);
-            }
-        }
-
-        return $res[$key];
-    }
-
-    /**
-     */
-    protected function _set($key, $data, $lifetime)
-    {
-        $key = $this->_params['prefix'] . $key;
-        $lifetime = $this->_getLifetime($lifetime);
-
-        if ($this->_memcache->set($key . '_e', time(), $lifetime) !== false) {
-            $this->_memcache->set($key, $data, $lifetime);
-        }
-    }
-
-    /**
-     * Checks if a given key exists in the cache.
-     *
-     * @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)
-    {
-        $key = $this->_params['prefix'] . $key;
-
-        return ($this->get($key, $lifetime) !== false);
-    }
-
-    /**
-     * Expire any existing data for the given key.
-     *
-     * @param string $key  Cache key to expire.
-     *
-     * @return boolean  Success or failure.
-     */
-    public function expire($key)
-    {
-        $key = $this->_params['prefix'] . $key;
-        unset($this->_expirecache[$key]);
-        $this->_memcache->delete($key . '_e');
-
-        return $this->_memcache->delete($key);
-    }
-
-}
diff --git a/framework/Cache/lib/Horde/Cache/Mock.php b/framework/Cache/lib/Horde/Cache/Mock.php
deleted file mode 100644 (file)
index 234997d..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-<?php
-/**
- * The Horde_Cache_Mock:: class provides a memory based implementation of the
- * Horde caching system. It persists only during a script run and ignores the
- * object lifetime because of that.
- *
- * 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   Gunnar Wrobel <wrobel@pardus.de>
- * @category Horde
- * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
- * @link     http://pear.horde.org/index.php?package=Cache
- * @package  Cache
- */
-class Horde_Cache_Mock extends Horde_Cache
-{
-    /**
-     * The storage location for this cache.
-     *
-     * @var array
-     */
-    private $_cache = array();
-
-    /**
-     * Construct a new Horde_Cache_Mock object.
-     *
-     * @param array $params  Configuration parameters:
-     */
-    public function __construct($params = array())
-    {
-        parent::__construct($params);
-    }
-
-    /**
-     */
-    protected function _get($key, $lifetime)
-    {
-        return isset($this->_cache[$key])
-            ? $this->_cache[$key]
-            : false;
-    }
-
-    /**
-     */
-    protected function _set($key, $data, $lifetime)
-    {
-        $this->_cache[$key] = $data;
-    }
-
-    /**
-     * 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)
-    {
-        return isset($this->_cache[$key]);
-    }
-
-    /**
-     * Expire any existing data for the given key.
-     *
-     * @param string $key  Cache key to expire.
-     *
-     * @return boolean  Success or failure.
-     */
-    public function expire($key)
-    {
-        unset($this->_cache[$key]);
-    }
-
-}
diff --git a/framework/Cache/lib/Horde/Cache/Null.php b/framework/Cache/lib/Horde/Cache/Null.php
deleted file mode 100644 (file)
index a332f85..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-<?php
-/**
- * The Horde_Cache_Null:: class provides a null implementation of the Horde
- * caching system.
- *
- * Copyright 2006-2007 Duck <duck@obala.net>
- *
- * 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   Duck <duck@obala.net>
- * @category Horde
- * @package  Cache
- */
-class Horde_Cache_Null extends Horde_Cache
-{
-    /**
-     */
-    protected function _get($key, $lifetime)
-    {
-        return false;
-    }
-
-    /**
-     */
-    protected function _set($key, $data, $lifetime)
-    {
-    }
-
-    /**
-     * 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)
-    {
-        return false;
-    }
-
-    /**
-     * Expire any existing data for the given key.
-     *
-     * @param string $key  Cache key to expire.
-     *
-     * @return boolean  Success or failure.
-     */
-    public function expire($key)
-    {
-        return false;
-    }
-
-}
diff --git a/framework/Cache/lib/Horde/Cache/Session.php b/framework/Cache/lib/Horde/Cache/Session.php
deleted file mode 100644 (file)
index 6aeb6b5..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-<?php
-/**
- * This class provides a session storage implementation of the Horde caching
- * system.
- *
- * 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@curecanti.org>
- * @category Horde
- * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
- * @package  Cache
- */
-class Horde_Cache_Session extends Horde_Cache
-{
-    /**
-     * Pointer to the session entry.
-     *
-     * @var array
-     */
-    protected $_sess;
-
-    /**
-     * Constructor.
-     *
-     * @param array $params  Optional parameters:
-     * <pre>
-     * 'session' - (string) Store session data in this entry.
-     *             DEFAULT: 'horde_cache_session'
-     * </pre>
-     */
-    public function __construct(array $params = array())
-    {
-        $params = array_merge(array(
-            'sess_name' => 'horde_cache_session'
-        ), $params);
-
-        parent::__construct($params);
-
-        if (!isset($_SESSION[$this->_params['sess_name']])) {
-            $_SESSION[$this->_params['sess_name']] = array();
-        }
-        $this->_sess = &$_SESSION[$this->_params['sess_name']];
-    }
-
-    /**
-     */
-    protected function _get($key, $lifetime)
-    {
-        return $this->exists($key, $lifetime)
-            ? $this->_sess[$key]['d']
-            : false;
-    }
-
-    /**
-     */
-    protected function _set($key, $data, $lifetime)
-    {
-        $this->_sess[$key] = array(
-            'd' => $data,
-            'l' => $this->_getLifetime($lifetime)
-        );
-    }
-
-    /**
-     * 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)
-    {
-        if (isset($this->_sess[$key])) {
-            /* 0 means no expire. */
-            if (($lifetime == 0) ||
-                ((time() - $lifetime) <= $this->_sess[$key]['l'])) {
-                return true;
-            }
-
-            unset($this->_sess[$key]);
-        }
-
-        return false;
-    }
-
-    /**
-     * Expire any existing data for the given key.
-     *
-     * @param string $key  Cache key to expire.
-     *
-     * @return boolean  Success or failure.
-     */
-    public function expire($key)
-    {
-        if (isset($this->_sess[$key])) {
-            unset($this->_sess[$key]);
-            return true;
-        }
-
-        return false;
-    }
-
-}
diff --git a/framework/Cache/lib/Horde/Cache/Sql.php b/framework/Cache/lib/Horde/Cache/Sql.php
deleted file mode 100644 (file)
index ead301c..0000000
+++ /dev/null
@@ -1,237 +0,0 @@
-<?php
-/**
- * The Horde_Cache_Sql:: class provides a SQL implementation of the Horde
- * Caching system.
- *
- * The table structure for the cache is as follows:
- * <pre>
- * CREATE TABLE horde_cache (
- *     cache_id          VARCHAR(32) NOT NULL,
- *     cache_timestamp   BIGINT NOT NULL,
- *     cache_data        LONGBLOB,
- *     (Or on PostgreSQL:)
- *     cache_data        TEXT,
- *     (Or on some other DBMS systems:)
- *     cache_data        IMAGE,
- *
- *     PRIMARY KEY (cache_id)
- * );
- * </pre>
- *
- * Copyright 2007-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>
- * @author   Ben Klang <ben@alkaloid.net>
- * @category Horde
- * @package  Cache
- */
-class Horde_Cache_Sql extends Horde_Cache
-{
-    /**
-     * Handle for the current database connection.
-     *
-     * @var Horde_Db_Adapter
-     */
-    protected $_db;
-
-    /**
-     * Constructor.
-     *
-     * @param array $params  Parameters:
-     * <pre>
-     * 'db' - (Horde_Db_Adapter) [REQUIRED] The DB instance.
-     * 'table' - (string) The name of the cache table.
-     *           DEFAULT: 'horde_cache'
-     * </pre>
-     *
-     * @throws Horde_Cache_Exception
-     */
-    public function __construct($params = array())
-    {
-        if (!isset($params['db'])) {
-            throw new Horde_Cache_Exception('Missing db parameter.');
-        }
-        $this->_db = $params['db'];
-        unset($params['db']);
-
-        $params = array_merge(array(
-            'table' => 'horde_cache',
-        ), $params);
-
-        parent::__construct($params);
-    }
-
-    /**
-     * Destructor.
-     */
-    public function __destruct()
-    {
-        /* Only do garbage collection 0.1% of the time we create an object. */
-        if (rand(0, 999) != 0) {
-            return;
-        }
-
-        $query = 'DELETE FROM ' . $this->_params['table'] .
-                 ' WHERE cache_expiration < ? AND cache_expiration <> 0';
-        $values = array(time());
-
-        try {
-            $this->_db->delete($query, $values);
-        } catch (Horde_Db_Exception $e) {}
-    }
-
-    /**
-     */
-    protected function _get($key, $lifetime)
-    {
-        $okey = $key;
-        $key = hash('md5', $key);
-
-        $timestamp = time();
-        $maxage = $timestamp - $lifetime;
-
-        /* Build SQL query. */
-        $query = 'SELECT cache_data FROM ' . $this->_params['table'] .
-                 ' WHERE cache_id = ?';
-        $values = array($key);
-
-        // 0 lifetime checks for objects which have no expiration
-        if ($lifetime != 0) {
-            $query .= ' AND cache_timestamp >= ?';
-            $values[] = $maxage;
-        }
-
-        try {
-            $result = $this->_db->selectValue($query, $values);
-        } catch (Horde_Db_Exception $e) {
-            return false;
-        }
-
-        if (is_null($result)) {
-            /* No rows were found - cache miss */
-            if ($this->_logger) {
-                $this->_logger->log(sprintf('Cache miss: %s (Id %s newer than %d)', $okey, $key, $maxage), 'DEBUG');
-            }
-            return false;
-        }
-
-        if ($this->_logger) {
-            $this->_logger->log(sprintf('Cache hit: %s (Id %s newer than %d)', $okey, $key, $maxage), 'DEBUG');
-        }
-
-        return $result;
-    }
-
-    /**
-     */
-    protected function _set($key, $data, $lifetime)
-    {
-        $okey = $key;
-        $key = hash('md5', $key);
-
-        $timestamp = time();
-
-        // 0 lifetime indicates the object should not be GC'd.
-        $expiration = ($lifetime === 0)
-            ? 0
-            : $this->_getLifetime($lifetime) + $timestamp;
-
-        if ($this->_logger) {
-            $this->_logger->log(sprintf('Cache set: %s (Id %s set at %d expires at %d)', $okey, $key, $timestamp, $expiration), 'DEBUG');
-        }
-
-        // Remove any old cache data and prevent duplicate keys
-        $query = 'DELETE FROM ' . $this->_params['table'] . ' WHERE cache_id=?';
-        $values = array($key);
-        try {
-            $this->_db->delete($query, $values);
-        } catch (Horde_Db_Exception $e) {}
-
-        /* Build SQL query. */
-        $query = 'INSERT INTO ' . $this->_params['table'] .
-                 ' (cache_id, cache_timestamp, cache_expiration, cache_data)' .
-                 ' VALUES (?, ?, ?, ?)';
-        $values = array($key, $timestamp, $expiration, $data);
-
-        try {
-            $this->_db->insert($query, $values);
-        } catch (Horde_Db_Exception $e) {
-            throw new Horde_Cache_Exception($e);
-        }
-    }
-
-    /**
-     * Checks if a given key exists in the cache, valid for the given
-     * lifetime.
-     *
-     * @param string $key        Cache key to check.
-     * @param integer $lifetime  Maximum age of the key in seconds or 0 for
-     *                           any object.
-     *
-     * @return boolean  Existence.
-     */
-    public function exists($key, $lifetime = 1)
-    {
-        $okey = $key;
-        $key = hash('md5', $key);
-
-        /* Build SQL query. */
-        $query = 'SELECT 1 FROM ' . $this->_params['table'] .
-                 ' WHERE cache_id = ?';
-        $values = array($key);
-
-        // 0 lifetime checks for objects which have no expiration
-        if ($lifetime != 0) {
-            $query .= ' AND cache_timestamp >= ?';
-            $values[] = time() - $lifetime;
-        }
-
-        try {
-            $result = $this->_db->selectValue($query, $values);
-        } catch (Horde_Db_Exception $e) {
-            return false;
-        }
-
-        $timestamp = time();
-        if (empty($result)) {
-            if ($this->_logger) {
-                $this->_logger->log(sprintf('Cache exists() miss: %s (Id %s newer than %d)', $okey, $key, $timestamp), 'DEBUG');
-            }
-            return false;
-        }
-
-        if ($this->_logger) {
-            $this->_logger->log(sprintf('Cache exists() hit: %s (Id %s newer than %d)', $okey, $key, $timestamp), 'DEBUG');
-        }
-
-        return true;
-    }
-
-    /**
-     * Expire any existing data for the given key.
-     *
-     * @param string $key  Cache key to expire.
-     *
-     * @return boolean  Success or failure.
-     */
-    public function expire($key)
-    {
-        $key = hash('md5', $key);
-
-        $query = 'DELETE FROM ' . $this->_params['table'] .
-                 ' WHERE cache_id = ?';
-        $values = array($key);
-
-        try {
-            $this->_db->delete($query, $values);
-        } catch (Horde_Db_Exception $e) {
-            return false;
-        }
-
-        return true;
-    }
-
-}
diff --git a/framework/Cache/lib/Horde/Cache/Stack.php b/framework/Cache/lib/Horde/Cache/Stack.php
deleted file mode 100644 (file)
index 55bc6ba..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-<?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
-{
-    /**
-     * Stack of cache drivers.
-     *
-     * @var string
-     */
-    protected $_stack = array();
-
-    /**
-     * Constructor.
-     *
-     * @param array $params  Parameters:
-     * <pre>
-     * 'stack' - (array) [REQUIRED] An array of Cache instances to loop
-     *           through, in order of priority. The last entry is considered
-     *           the 'master' driver, for purposes of writes.
-     * </pre>
-     *
-     * @throws InvalidArgumentException
-     */
-    public function __construct(array $params = array())
-    {
-        if (!isset($params['stack'])) {
-            throw new InvalidArgumentException('Missing stack parameter.');
-        }
-        $this->_stack[] = $params['stack'];
-
-        unset($params['stack']);
-        parent::__construct($params);
-    }
-
-    /**
-     */
-    protected function _get($key, $lifetime)
-    {
-        foreach ($this->_stack as $val) {
-            $result = $val->get($key, $lifetime);
-            if ($result !== false) {
-                return $result;
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     */
-    protected function _set($key, $data, $lifetime)
-    {
-        /* 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;
-                }
-
-                /* Attempt to invalidate cache if write failed. */
-                $val->expire($id);
-            }
-            $master = false;
-        }
-    }
-
-    /**
-     * 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;
-    }
-
-}
diff --git a/framework/Cache/lib/Horde/Cache/Storage.php b/framework/Cache/lib/Horde/Cache/Storage.php
new file mode 100644 (file)
index 0000000..6db05d2
--- /dev/null
@@ -0,0 +1,93 @@
+<?php
+/**
+ * This class provides the abstract implementation of the cache storage
+ * driver.
+ *
+ * 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
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Cache
+ */
+abstract class Horde_Cache_Storage
+{
+    /**
+     * Logger.
+     *
+     * @var Horde_Log_Logger
+     */
+    protected $_logger;
+
+    /**
+     * Parameters.
+     *
+     * @var array
+     */
+    protected $_params = array();
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Configuration parameters.
+     */
+    public function __construct(array $params = array())
+    {
+        $this->_params = array_merge($this->_params, $params);
+    }
+
+    /**
+     * Set the logging object.
+     *
+     * @param Horde_Log_Logger $logger  Log object.
+     */
+    public function setLogger($logger)
+    {
+        $this->_logger = $logger;
+    }
+
+    /**
+     * Retrieve cached data.
+     *
+     * @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.
+     */
+    abstract public function get($key, $lifetime);
+
+    /**
+     * 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 0 will not be GC'd.
+     */
+    abstract public function set($key, $data, $lifetime);
+
+    /**
+     * 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.
+     */
+    abstract public function exists($key, $lifetime);
+
+    /**
+     * Expire any existing data for the given key.
+     *
+     * @param string $key  Cache key to expire.
+     *
+     * @return boolean  Success or failure.
+     */
+    abstract public function expire($key);
+
+}
diff --git a/framework/Cache/lib/Horde/Cache/Storage/Apc.php b/framework/Cache/lib/Horde/Cache/Storage/Apc.php
new file mode 100644 (file)
index 0000000..12ea3a3
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+/**
+ * This class provides cache storage in the Alternative PHP Cache.
+ *
+ * Copyright 2006-2007 Duck <duck@obala.net>
+ *
+ * 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   Duck <duck@obala.net>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Cache
+ */
+class Horde_Cache_Storage_Apc extends Horde_Cache_Storage
+{
+    /**
+     */
+    public function get($key, $lifetime)
+    {
+        $key = $this->_params['prefix'] . $key;
+        $this->_setExpire($key, $lifetime);
+        return apc_fetch($key);
+    }
+
+    /**
+     */
+    public function set($key, $data, $lifetime)
+    {
+        $key = $this->_params['prefix'] . $key;
+        if (apc_store($key . '_expire', time(), $lifetime)) {
+            apc_store($key, $data, $lifetime);
+        }
+    }
+
+    /**
+     */
+    public function exists($key, $lifetime)
+    {
+        $key = $this->_params['prefix'] . $key;
+        $this->_setExpire($key, $lifetime);
+        return (apc_fetch($key) !== false);
+    }
+
+    /**
+     */
+    public function expire($key)
+    {
+        $key = $this->_params['prefix'] . $key;
+        apc_delete($key . '_expire');
+        return apc_delete($key);
+    }
+
+    /**
+     * Set expire time on each call since APC sets it on cache creation.
+     *
+     * @param string $key        Cache key to expire.
+     * @param integer $lifetime  Lifetime of the data in seconds.
+     */
+    protected function _setExpire($key, $lifetime)
+    {
+        $key = $this->_params['prefix'] . $key;
+        if ($lifetime == 0) {
+            // Don't expire.
+            return;
+        }
+
+        $expire = apc_fetch($key . '_expire');
+
+        // Set prune period.
+        if ($expire + $lifetime < time()) {
+            // Expired
+            apc_delete($key);
+            apc_delete($key . '_expire');
+        }
+    }
+
+}
diff --git a/framework/Cache/lib/Horde/Cache/Storage/Eaccelerator.php b/framework/Cache/lib/Horde/Cache/Storage/Eaccelerator.php
new file mode 100644 (file)
index 0000000..1a58a1b
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+/**
+ * This class provides cache storage in eAccelerator (version 0.9.5+).
+ *
+ * Copyright 2006-2007 Duck <duck@obala.net>
+ *
+ * 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   Duck <duck@obala.net>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Cache
+ */
+class Horde_Cache_Storage_Eaccelerator extends Horde_Cache_Storage
+{
+    /**
+     * @throws Horde_Cache_Exception
+     */
+    public function __construct(array $params = array())
+    {
+        if (!function_exists('eaccelerator_gc')) {
+            throw new Horde_Cache_Exception('eAccelerator must be compiled with support for shared memory to use as caching backend.');
+        }
+
+        parent::__construct($params);
+    }
+
+    /**
+     */
+    public function get($key, $lifetime)
+    {
+        $key = $this->_params['prefix'] . $key;
+        $this->_setExpire($key, $lifetime);
+        return eaccelerator_get($key);
+    }
+
+    /**
+     */
+    public function set($key, $data, $lifetime)
+    {
+        $key = $this->_params['prefix'] . $key;
+        if (eaccelerator_put($key . '_expire', time(), $lifetime)) {
+            eaccelerator_put($key, $data, $lifetime);
+        }
+    }
+
+    /**
+     */
+    public function exists($key, $lifetime)
+    {
+        $key = $this->_params['prefix'] . $key;
+        $this->_setExpire($key, $lifetime);
+        return eaccelerator_get($key) !== false;
+    }
+
+    /**
+     */
+    public function expire($key)
+    {
+        $key = $this->_params['prefix'] . $key;
+        eaccelerator_rm($key . '_expire');
+        return eaccelerator_rm($key);
+    }
+
+    /**
+     * Set expire time on each call since eAccelerator sets it on
+     * cache creation.
+     *
+     * @param string $key        Cache key to expire.
+     * @param integer $lifetime  Lifetime of the data in seconds.
+     */
+    protected function _setExpire($key, $lifetime)
+    {
+        if ($lifetime == 0) {
+            // Don't expire.
+            return;
+        }
+
+        $key = $this->_params['prefix'] . $key;
+        $expire = eaccelerator_get($key . '_expire');
+
+        // Set prune period.
+        if ($expire + $lifetime < time()) {
+            // Expired
+            eaccelerator_rm($key);
+            eaccelerator_rm($key . '_expire');
+        }
+    }
+
+}
diff --git a/framework/Cache/lib/Horde/Cache/Storage/File.php b/framework/Cache/lib/Horde/Cache/Storage/File.php
new file mode 100644 (file)
index 0000000..84cae41
--- /dev/null
@@ -0,0 +1,227 @@
+<?php
+/**
+ * This class provides cache storage in the filesystem.
+ *
+ * Copyright 1999-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   Anil Madhavapeddy <anil@recoil.org>
+ * @author   Chuck Hagenbuch <chuck@horde.org>
+ * @author   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Cache
+ */
+class Horde_Cache_Storage_File extends Horde_Cache_Storage
+{
+    /* Location of the garbage collection data file. */
+    const GC_FILE = 'horde_cache_gc';
+
+    /**
+     * The location of the temp directory.
+     *
+     * @var string
+     */
+    protected $_dir;
+
+    /**
+     * List of key to filename mappings.
+     *
+     * @var array
+     */
+    protected $_file = array();
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Optional parameters:
+     * <pre>
+     * 'dir' - (string) The base directory to store the cache files in.
+     *         DEFAULT: System default
+     * 'prefix' - (string) The filename prefix to use for the cache files.
+     *            DEFAULT: 'cache_'
+     * 'sub' - (integer) If non-zero, the number of subdirectories to create
+     *         to store the file (i.e. PHP's session.save_path).
+     *         DEFAULT: 0
+     * </pre>
+     */
+    public function __construct(array $params = array())
+    {
+        $params = array_merge(array(
+            'prefix' => 'cache_',
+            'sub' => 0
+        ), $params);
+
+        $this->_dir = (isset($params['dir']) && @is_dir($params['dir']))
+            ? $params['dir']
+            : Horde_Util::getTempDir();
+
+        parent::__construct($params);
+    }
+
+    /**
+     * Destructor.
+     */
+    public function __destruct()
+    {
+        /* Only do garbage collection 0.1% of the time we create an object. */
+        if (rand(0, 999) != 0) {
+            return;
+        }
+
+        $filename = $this->_dir . '/' . self::GC_FILE;
+        $excepts = array();
+
+        if (file_exists($filename)) {
+            $gc_file = file($filename, FILE_IGNORE_NEW_LINES);
+            reset($gc_file);
+            next($gc_file);
+            while (list(,$data) = each($gc_file)) {
+                $parts = explode("\t", $data, 2);
+                $excepts[$parts[0]] = $parts[1];
+            }
+        }
+
+        try {
+            $it = empty($this->_params['sub'])
+                ? new DirectoryIterator($this->_dir)
+                : new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->_dir), RecursiveIteratorIterator::CHILD_FIRST);
+        } catch (UnexpectedValueException $e) {
+            return;
+        }
+
+        $c_time = time();
+
+        foreach ($it as $key => $val) {
+            if (!$val->isDir() &&
+                (strpos($val->getFilename(), $this->_params['prefix']) === 0)) {
+                $d_time = isset($excepts[$key])
+                    ? $excepts[$key]
+                    : $this->_params['lifetime'];
+
+                if (!empty($d_time) &&
+                    (($c_time - $d_time) > filemtime($key))) {
+                    @unlink($key);
+                    unset($excepts[$key]);
+                }
+            }
+        }
+
+        $fp = fopen($filename, 'w');
+        foreach ($excepts as $key => $val) {
+            fwrite($fp, $key . "\t" . $val . "\n");
+        }
+        fclose($fp);
+    }
+
+    /**
+     */
+    public function get($key, $lifetime)
+    {
+        if (!$this->exists($key, $lifetime)) {
+            /* Nothing cached, return failure. */
+            return false;
+        }
+
+        $filename = $this->_keyToFile($key);
+        $size = filesize($filename);
+
+        return $size
+            ? @file_get_contents($filename)
+            : '';
+    }
+
+    /**
+     */
+    public function set($key, $data, $lifetime)
+    {
+        $filename = $this->_keyToFile($key, true);
+        $tmp_file = Horde_Util::getTempFile('HordeCache', true, $this->_dir);
+        if (isset($this->_params['umask'])) {
+            chmod($tmp_file, 0666 & ~$this->_params['umask']);
+        }
+
+        if (file_put_contents($tmp_file, $data) === false) {
+            throw new Horde_Cache_Exception('Cannot write to cache directory ' . $this->_dir);
+        }
+
+        @rename($tmp_file, $filename);
+
+        if (($lifetime != $this->_params['lifetime']) &&
+            ($fp = @fopen($this->_dir . '/horde_cache_gc', 'a'))) {
+            // This may result in duplicate entries in horde_cache_gc, but we
+            // will take care of these whenever we do GC and this is quicker
+            // than having to check every time we access the file.
+            fwrite($fp, $filename . "\t" . (empty($lifetime) ? 0 : time() + $lifetime) . "\n");
+            fclose($fp);
+        }
+    }
+
+    /**
+     */
+    public function exists($key, $lifetime)
+    {
+        $filename = $this->_keyToFile($key);
+
+        /* Key exists in the cache */
+        if (file_exists($filename)) {
+            /* 0 means no expire.
+             * Also, If the file was been created after the supplied value,
+             * the data is valid (fresh). */
+            if (($lifetime == 0) ||
+                (time() - $lifetime <= filemtime($filename))) {
+                return true;
+            }
+
+            @unlink($filename);
+        }
+
+        return false;
+    }
+
+    /**
+     */
+    public function expire($key)
+    {
+        $filename = $this->_keyToFile($key);
+        return @unlink($filename);
+    }
+
+    /**
+     * Map a cache key to a unique filename.
+     *
+     * @param string $key     Cache key.
+     * @param string $create  Create path if it doesn't exist?
+     *
+     * @return string  Fully qualified filename.
+     */
+    protected function _keyToFile($key, $create = false)
+    {
+        if ($create || !isset($this->_file[$key])) {
+            $dir = $this->_dir . '/';
+            $md5 = hash('md5', $key);
+            $sub = '';
+
+            if (!empty($this->_params['sub'])) {
+                $max = min($this->_params['sub'], strlen($md5));
+                for ($i = 0; $i < $max; $i++) {
+                    $sub .= $md5[$i];
+                    if ($create && !is_dir($dir . $sub)) {
+                        if (!mkdir($dir . $sub)) {
+                            $sub = '';
+                            break;
+                        }
+                    }
+                    $sub .= '/';
+                }
+            }
+
+            $this->_file[$key] = $dir . $sub . $this->_params['prefix'] . $md5;
+        }
+
+        return $this->_file[$key];
+    }
+
+}
diff --git a/framework/Cache/lib/Horde/Cache/Storage/Memcache.php b/framework/Cache/lib/Horde/Cache/Storage/Memcache.php
new file mode 100644 (file)
index 0000000..484d41c
--- /dev/null
@@ -0,0 +1,136 @@
+<?php
+/**
+ * This class provides cache storage in a memcache installation.
+ * Horde caching system.
+ *
+ * Copyright 2006-2007 Duck <duck@obala.net>
+ * Copyright 2007-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   Duck <duck@obala.net>
+ * @author   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Cache
+ */
+class Horde_Cache_Storage_Memcache extends Horde_Cache_Storage implements Serializable
+{
+    /**
+     * Cache results of expire() calls (since we will get the entire object
+     * on an expire() call anyway).
+     *
+     * @var array
+     */
+    protected $_expirecache = array();
+
+    /**
+     * Memcache object.
+     *
+     * @var Horde_Memcache
+     */
+    protected $_memcache;
+
+    /**
+     * Construct a new Horde_Cache_Memcache object.
+     *
+     * @param array $params  Parameter array:
+     * <pre>
+     * 'memcache' - (Horde_Memcache) A Horde_Memcache object.
+     * </pre>
+     *
+     * @throws InvalidArgumentException
+     */
+    public function __construct($params = array())
+    {
+        if (!isset($params['memcache'])) {
+            throw new InvalidArgumentException('Missing memcache object');
+        }
+
+        $this->_memcache = $params['memcache'];
+        unset($params['memcache']);
+
+        parent::__construct($params);
+    }
+
+    /**
+     */
+    public function get($key, $lifetime)
+    {
+        $key = $this->_params['prefix'] . $key;
+        if (isset($this->_expirecache[$key])) {
+            return $this->_expirecache[$key];
+        }
+
+        $key_list = array($key);
+        if (!empty($lifetime)) {
+            $key_list[] = $key . '_e';
+        }
+
+        $res = $this->_memcache->get($key_list);
+
+        if ($res === false) {
+            unset($this->_expirecache[$key]);
+        } else {
+            // If we can't find the expire time, assume we have exceeded it.
+            if (empty($lifetime) ||
+                (($res[$key . '_e'] !== false) && ($res[$key . '_e'] + $lifetime > time()))) {
+                $this->_expirecache[$key] = $res[$key];
+            } else {
+                $res[$key] = false;
+                $this->expire($key);
+            }
+        }
+
+        return $res[$key];
+    }
+
+    /**
+     */
+    public function _set($key, $data, $lifetime)
+    {
+        $key = $this->_params['prefix'] . $key;
+
+        if ($this->_memcache->set($key . '_e', time(), $lifetime) !== false) {
+            $this->_memcache->set($key, $data, $lifetime);
+        }
+    }
+
+    /**
+     */
+    public function exists($key, $lifetime)
+    {
+        $key = $this->_params['prefix'] . $key;
+
+        return ($this->get($key, $lifetime) !== false);
+    }
+
+    /**
+     */
+    public function expire($key)
+    {
+        $key = $this->_params['prefix'] . $key;
+        unset($this->_expirecache[$key]);
+        $this->_memcache->delete($key . '_e');
+
+        return $this->_memcache->delete($key);
+    }
+
+    /* Serializable methods. */
+
+    /**
+     */
+    public function serialize()
+    {
+        return serialize($this->_memcache);
+    }
+
+    /**
+     */
+    public function unserialize($data)
+    {
+        $this->_memcache = unserialize($data);
+    }
+
+}
diff --git a/framework/Cache/lib/Horde/Cache/Storage/Mock.php b/framework/Cache/lib/Horde/Cache/Storage/Mock.php
new file mode 100644 (file)
index 0000000..735241f
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+/**
+ * This class provides cache storage in PHP memory.
+ * It persists only during a script run and ignores the object lifetime
+ * because of that.
+ *
+ * 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   Gunnar Wrobel <wrobel@pardus.de>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=Cache
+ * @package  Cache
+ */
+class Horde_Cache_Storage_Mock extends Horde_Cache_Storage
+{
+    /**
+     * The storage location for this cache.
+     *
+     * @var array
+     */
+    private $_cache = array();
+
+    /**
+     */
+    public function get($key, $lifetime)
+    {
+        return isset($this->_cache[$key])
+            ? $this->_cache[$key]
+            : false;
+    }
+
+    /**
+     */
+    public function set($key, $data, $lifetime)
+    {
+        $this->_cache[$key] = $data;
+    }
+
+    /**
+     */
+    public function exists($key, $lifetime = 1)
+    {
+        return isset($this->_cache[$key]);
+    }
+
+    /**
+     */
+    public function expire($key)
+    {
+        unset($this->_cache[$key]);
+    }
+
+}
diff --git a/framework/Cache/lib/Horde/Cache/Storage/Null.php b/framework/Cache/lib/Horde/Cache/Storage/Null.php
new file mode 100644 (file)
index 0000000..36b88ec
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+/**
+ * This class provides a null cache storage driver.
+ *
+ * Copyright 2006-2007 Duck <duck@obala.net>
+ *
+ * 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   Duck <duck@obala.net>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Cache
+ */
+class Horde_Cache_Storage_Null extends Horde_Cache_Storage
+{
+    /**
+     */
+    public function get($key, $lifetime)
+    {
+        return false;
+    }
+
+    /**
+     */
+    public function set($key, $data, $lifetime)
+    {
+    }
+
+    /**
+     */
+    public function exists($key, $lifetime)
+    {
+        return false;
+    }
+
+    /**
+     */
+    public function expire($key)
+    {
+        return false;
+    }
+
+}
diff --git a/framework/Cache/lib/Horde/Cache/Storage/Session.php b/framework/Cache/lib/Horde/Cache/Storage/Session.php
new file mode 100644 (file)
index 0000000..3a5d75f
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+/**
+ * This class provides cache storage in a PHP session.
+ *
+ * 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@curecanti.org>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Cache
+ */
+class Horde_Cache_Storage_Session extends Horde_Cache_Storage
+{
+    /**
+     * Pointer to the session entry.
+     *
+     * @var array
+     */
+    protected $_sess;
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Optional parameters:
+     * <pre>
+     * 'session' - (string) Store session data in this entry.
+     *             DEFAULT: 'horde_cache_session'
+     * </pre>
+     */
+    public function __construct(array $params = array())
+    {
+        $params = array_merge(array(
+            'sess_name' => 'horde_cache_session'
+        ), $params);
+
+        parent::__construct($params);
+
+        if (!isset($_SESSION[$this->_params['sess_name']])) {
+            $_SESSION[$this->_params['sess_name']] = array();
+        }
+        $this->_sess = &$_SESSION[$this->_params['sess_name']];
+    }
+
+    /**
+     */
+    public function get($key, $lifetime)
+    {
+        return $this->exists($key, $lifetime)
+            ? $this->_sess[$key]['d']
+            : false;
+    }
+
+    /**
+     */
+    public function set($key, $data, $lifetime)
+    {
+        $this->_sess[$key] = array(
+            'd' => $data,
+            'l' => $lifetime
+        );
+    }
+
+    /**
+     */
+    public function exists($key, $lifetime)
+    {
+        if (isset($this->_sess[$key])) {
+            /* 0 means no expire. */
+            if (($lifetime == 0) ||
+                ((time() - $lifetime) <= $this->_sess[$key]['l'])) {
+                return true;
+            }
+
+            unset($this->_sess[$key]);
+        }
+
+        return false;
+    }
+
+    /**
+     */
+    public function expire($key)
+    {
+        if (isset($this->_sess[$key])) {
+            unset($this->_sess[$key]);
+            return true;
+        }
+
+        return false;
+    }
+
+}
diff --git a/framework/Cache/lib/Horde/Cache/Storage/Sql.php b/framework/Cache/lib/Horde/Cache/Storage/Sql.php
new file mode 100644 (file)
index 0000000..c17e3d4
--- /dev/null
@@ -0,0 +1,224 @@
+<?php
+/**
+ * This class provides cache storage in a SQL databsae.
+ *
+ * The table structure for the cache is as follows:
+ * <pre>
+ * CREATE TABLE horde_cache (
+ *     cache_id          VARCHAR(32) NOT NULL,
+ *     cache_timestamp   BIGINT NOT NULL,
+ *     cache_data        LONGBLOB,
+ *     (Or on PostgreSQL:)
+ *     cache_data        TEXT,
+ *     (Or on some other DBMS systems:)
+ *     cache_data        IMAGE,
+ *
+ *     PRIMARY KEY (cache_id)
+ * );
+ * </pre>
+ *
+ * Copyright 2007-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   Ben Klang <ben@alkaloid.net>
+ * @author   Michael Slusarz <slusarz@horde.org>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Cache
+ */
+class Horde_Cache_Storage_Sql extends Horde_Cache_Storage
+{
+    /**
+     * Handle for the current database connection.
+     *
+     * @var Horde_Db_Adapter
+     */
+    protected $_db;
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Parameters:
+     * <pre>
+     * 'db' - (Horde_Db_Adapter) [REQUIRED] The DB instance.
+     * 'table' - (string) The name of the cache table.
+     *           DEFAULT: 'horde_cache'
+     * </pre>
+     *
+     * @throws Horde_Cache_Exception
+     */
+    public function __construct($params = array())
+    {
+        if (!isset($params['db'])) {
+            throw new Horde_Cache_Exception('Missing db parameter.');
+        }
+        $this->_db = $params['db'];
+        unset($params['db']);
+
+        $params = array_merge(array(
+            'table' => 'horde_cache',
+        ), $params);
+
+        parent::__construct($params);
+    }
+
+    /**
+     * Destructor.
+     */
+    public function __destruct()
+    {
+        /* Only do garbage collection 0.1% of the time we create an object. */
+        if (rand(0, 999) != 0) {
+            return;
+        }
+
+        $query = 'DELETE FROM ' . $this->_params['table'] .
+                 ' WHERE cache_expiration < ? AND cache_expiration <> 0';
+        $values = array(time());
+
+        try {
+            $this->_db->delete($query, $values);
+        } catch (Horde_Db_Exception $e) {}
+    }
+
+    /**
+     */
+    public function get($key, $lifetime)
+    {
+        $okey = $key;
+        $key = hash('md5', $key);
+
+        $timestamp = time();
+        $maxage = $timestamp - $lifetime;
+
+        /* Build SQL query. */
+        $query = 'SELECT cache_data FROM ' . $this->_params['table'] .
+                 ' WHERE cache_id = ?';
+        $values = array($key);
+
+        // 0 lifetime checks for objects which have no expiration
+        if ($lifetime != 0) {
+            $query .= ' AND cache_timestamp >= ?';
+            $values[] = $maxage;
+        }
+
+        try {
+            $result = $this->_db->selectValue($query, $values);
+        } catch (Horde_Db_Exception $e) {
+            return false;
+        }
+
+        if (is_null($result)) {
+            /* No rows were found - cache miss */
+            if ($this->_logger) {
+                $this->_logger->log(sprintf('Cache miss: %s (Id %s newer than %d)', $okey, $key, $maxage), 'DEBUG');
+            }
+            return false;
+        }
+
+        if ($this->_logger) {
+            $this->_logger->log(sprintf('Cache hit: %s (Id %s newer than %d)', $okey, $key, $maxage), 'DEBUG');
+        }
+
+        return $result;
+    }
+
+    /**
+     */
+    public function _set($key, $data, $lifetime)
+    {
+        $okey = $key;
+        $key = hash('md5', $key);
+
+        $timestamp = time();
+
+        // 0 lifetime indicates the object should not be GC'd.
+        $expiration = ($lifetime === 0)
+            ? 0
+            : ($lifetime + $timestamp);
+
+        if ($this->_logger) {
+            $this->_logger->log(sprintf('Cache set: %s (Id %s set at %d expires at %d)', $okey, $key, $timestamp, $expiration), 'DEBUG');
+        }
+
+        // Remove any old cache data and prevent duplicate keys
+        $query = 'DELETE FROM ' . $this->_params['table'] . ' WHERE cache_id=?';
+        $values = array($key);
+        try {
+            $this->_db->delete($query, $values);
+        } catch (Horde_Db_Exception $e) {}
+
+        /* Build SQL query. */
+        $query = 'INSERT INTO ' . $this->_params['table'] .
+                 ' (cache_id, cache_timestamp, cache_expiration, cache_data)' .
+                 ' VALUES (?, ?, ?, ?)';
+        $values = array($key, $timestamp, $expiration, $data);
+
+        try {
+            $this->_db->insert($query, $values);
+        } catch (Horde_Db_Exception $e) {
+            throw new Horde_Cache_Exception($e);
+        }
+    }
+
+    /**
+     */
+    public function exists($key, $lifetime)
+    {
+        $okey = $key;
+        $key = hash('md5', $key);
+
+        /* Build SQL query. */
+        $query = 'SELECT 1 FROM ' . $this->_params['table'] .
+                 ' WHERE cache_id = ?';
+        $values = array($key);
+
+        // 0 lifetime checks for objects which have no expiration
+        if ($lifetime != 0) {
+            $query .= ' AND cache_timestamp >= ?';
+            $values[] = time() - $lifetime;
+        }
+
+        try {
+            $result = $this->_db->selectValue($query, $values);
+        } catch (Horde_Db_Exception $e) {
+            return false;
+        }
+
+        $timestamp = time();
+        if (empty($result)) {
+            if ($this->_logger) {
+                $this->_logger->log(sprintf('Cache exists() miss: %s (Id %s newer than %d)', $okey, $key, $timestamp), 'DEBUG');
+            }
+            return false;
+        }
+
+        if ($this->_logger) {
+            $this->_logger->log(sprintf('Cache exists() hit: %s (Id %s newer than %d)', $okey, $key, $timestamp), 'DEBUG');
+        }
+
+        return true;
+    }
+
+    /**
+     */
+    public function expire($key)
+    {
+        $key = hash('md5', $key);
+
+        $query = 'DELETE FROM ' . $this->_params['table'] .
+                 ' WHERE cache_id = ?';
+        $values = array($key);
+
+        try {
+            $this->_db->delete($query, $values);
+        } catch (Horde_Db_Exception $e) {
+            return false;
+        }
+
+        return true;
+    }
+
+}
diff --git a/framework/Cache/lib/Horde/Cache/Storage/Stack.php b/framework/Cache/lib/Horde/Cache/Storage/Stack.php
new file mode 100644 (file)
index 0000000..6362a61
--- /dev/null
@@ -0,0 +1,117 @@
+<?php
+/**
+ * This class loops through a given list of storage 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
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Cache
+ */
+class Horde_Cache_Storage_Stack extends Horde_Cache_Storage
+{
+    /**
+     * Stack of cache drivers.
+     *
+     * @var string
+     */
+    protected $_stack = array();
+
+    /**
+     * Constructor.
+     *
+     * @param array $params  Parameters:
+     * <pre>
+     * 'stack' - (array) [REQUIRED] An array of storage instances to loop
+     *           through, in order of priority. The last entry is considered
+     *           the 'master' driver, for purposes of writes.
+     * </pre>
+     *
+     * @throws InvalidArgumentException
+     */
+    public function __construct(array $params = array())
+    {
+        if (!isset($params['stack'])) {
+            throw new InvalidArgumentException('Missing stack parameter.');
+        }
+        $this->_stack[] = $params['stack'];
+        unset($params['stack']);
+
+        parent::__construct($params);
+    }
+
+    /**
+     */
+    public function get($key, $lifetime)
+    {
+        foreach ($this->_stack as $val) {
+            $result = $val->get($key, $lifetime);
+            if ($result !== false) {
+                return $result;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     */
+    public function set($key, $data, $lifetime)
+    {
+        /* 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;
+                }
+
+                /* Attempt to invalidate cache if write failed. */
+                $val->expire($id);
+            }
+            $master = false;
+        }
+    }
+
+    /**
+     */
+    public function exists($key, $lifetime)
+    {
+        foreach ($this->_stack as $val) {
+            $result = $val->exists($key, $lifetime);
+            if ($result === true) {
+                break;
+            }
+        }
+
+        return $result;
+    }
+
+    /**
+     */
+    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;
+    }
+
+}
diff --git a/framework/Cache/lib/Horde/Cache/Storage/Xcache.php b/framework/Cache/lib/Horde/Cache/Storage/Xcache.php
new file mode 100644 (file)
index 0000000..0c71248
--- /dev/null
@@ -0,0 +1,81 @@
+<?php
+/**
+ * This class provides cache storage in Xcache.
+ *
+ * Copyright 2006-2007 Duck <duck@obala.net>
+ *
+ * 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   Duck <duck@obala.net>
+ * @category Horde
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @package  Cache
+ */
+class Horde_Cache_Storage_Xcache extends Horde_Cache_Storage
+{
+    /**
+     */
+    public function get($key, $lifetime)
+    {
+        $key = $this->_params['prefix'] . $key;
+        $this->_setExpire($key, $lifetime);
+        $result = xcache_get($key);
+
+        return empty($result)
+            ? false
+            : $result;
+    }
+
+    /**
+     */
+    public function set($key, $data, $lifetime)
+    {
+        $key = $this->_params['prefix'] . $key;
+        if (xcache_set($key . '_expire', time(), $lifetime)) {
+            xcache_set($key, $data, $lifetime);
+        }
+    }
+
+    /**
+     */
+    public function exists($key, $lifetime)
+    {
+        $key = $this->_params['prefix'] . $key;
+        $this->_setExpire($key, $lifetime);
+        return xcache_isset($key);
+    }
+
+    /**
+     */
+    public function expire($key)
+    {
+        $key = $this->_params['prefix'] . $key;
+        xcache_unset($key . '_expire');
+        return xcache_unset($key);
+    }
+
+    /**
+     * Set expire time on each call since memcache sets it on cache creation.
+     *
+     * @param string $key        Cache key to expire.
+     * @param integer $lifetime  Lifetime of the data in seconds.
+     */
+    protected function _setExpire($key, $lifetime)
+    {
+        if ($lifetime == 0) {
+            // don't expire
+            return;
+        }
+        $key = $this->_params['prefix'] . $key;
+        $expire = xcache_get($key . '_expire');
+
+        // set prune period
+        if ($expire + $lifetime < time()) {
+            // Expired
+            xcache_unset($key . '_expire');
+            xcache_unset($key);
+        }
+    }
+
+}
diff --git a/framework/Cache/lib/Horde/Cache/Xcache.php b/framework/Cache/lib/Horde/Cache/Xcache.php
deleted file mode 100644 (file)
index 2366bfa..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-<?php
-/**
- * The Horde_Cache_Xcache:: class provides an XCache implementation of
- * the Horde caching system.
- *
- * Copyright 2006-2007 Duck <duck@obala.net>
- *
- * 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   Duck <duck@obala.net>
- * @category Horde
- * @package  Cache
- */
-class Horde_Cache_Xcache extends Horde_Cache
-{
-    /**
-     */
-    protected function _get($key, $lifetime)
-    {
-        $key = $this->_params['prefix'] . $key;
-        $this->_setExpire($key, $lifetime);
-        $result = xcache_get($key);
-
-        return empty($result)
-            ? false
-            : $result;
-    }
-
-    /**
-     */
-    protected function _set($key, $data, $lifetime)
-    {
-        $key = $this->_params['prefix'] . $key;
-        $lifetime = $this->_getLifetime($lifetime);
-        if (xcache_set($key . '_expire', time(), $lifetime)) {
-            xcache_set($key, $data, $lifetime);
-        }
-    }
-
-    /**
-     * 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)
-    {
-        $key = $this->_params['prefix'] . $key;
-        $this->_setExpire($key, $lifetime);
-        return xcache_isset($key);
-    }
-
-    /**
-     * Expire any existing data for the given key.
-     *
-     * @param string $key  Cache key to expire.
-     *
-     * @return boolean  Success or failure.
-     */
-    public function expire($key)
-    {
-        $key = $this->_params['prefix'] . $key;
-        xcache_unset($key . '_expire');
-        return xcache_unset($key);
-    }
-
-    /**
-     * Set expire time on each call since memcache sets it on cache creation.
-     *
-     * @param string $key        Cache key to expire.
-     * @param integer $lifetime  Lifetime of the data in seconds.
-     */
-    protected function _setExpire($key, $lifetime)
-    {
-        if ($lifetime == 0) {
-            // don't expire
-            return;
-        }
-        $key = $this->_params['prefix'] . $key;
-        $expire = xcache_get($key . '_expire');
-
-        // set prune period
-        if ($expire + $lifetime < time()) {
-            // Expired
-            xcache_unset($key . '_expire');
-            xcache_unset($key);
-        }
-    }
-
-}
index 4cf25aa..82c6939 100644 (file)
@@ -33,7 +33,8 @@ Performance Suite&apos;s content cache), memcached, or an SQL table.
   <api>beta</api>
  </stability>
  <license uri="http://www.gnu.org/copyleft/lesser.html">LGPL</license>
- <notes>* Add option to transparently compress cache data using lzf.
+ <notes>* Abstracted storage-specific code into &apos;Storage&apos; drivers.
+ * Add option to transparently compress cache data using lzf.
  * Added Horde_Cache_Session::.
  * Horde_Cache::set() no longer returns a boolean result.
  * Added Horde_Cache_Exception::.
@@ -47,17 +48,20 @@ Performance Suite&apos;s content cache), memcached, or an SQL table.
    <dir name="lib">
     <dir name="Horde">
      <dir name="Cache">
-      <file name="Apc.php" role="php" />
-      <file name="Eaccelerator.php" role="php" />
+      <dir name="Storage">
+       <file name="Apc.php" role="php" />
+       <file name="Eaccelerator.php" role="php" />
+       <file name="File.php" role="php" />
+       <file name="Memcache.php" role="php" />
+       <file name="Mock.php" role="php" />
+       <file name="Null.php" role="php" />
+       <file name="Session.php" role="php" />
+       <file name="Sql.php" role="php" />
+       <file name="Stack.php" role="php" />
+       <file name="Xcache.php" role="php" />
+      </dir> <!-- /lib/Horde/Cache/Storage -->
       <file name="Exception.php" role="php" />
-      <file name="File.php" role="php" />
-      <file name="Memcache.php" role="php" />
-      <file name="Mock.php" role="php" />
-      <file name="Null.php" role="php" />
-      <file name="Session.php" role="php" />
-      <file name="Sql.php" role="php" />
-      <file name="Stack.php" role="php" />
-      <file name="Xcache.php" role="php" />
+      <file name="Storage.php" role="php" />
      </dir> <!-- /lib/Horde/Cache -->
      <file name="Cache.php" role="php" />
     </dir> <!-- /lib/Horde -->
@@ -109,17 +113,18 @@ Performance Suite&apos;s content cache), memcached, or an SQL table.
  </dependencies>
  <phprelease>
   <filelist>
-   <install name="lib/Horde/Cache/Apc.php" as="Horde/Cache/Apc.php" />
-   <install name="lib/Horde/Cache/Eaccelerator.php" as="Horde/Cache/Eaccelerator.php" />
+   <install name="lib/Horde/Cache/Storage/Apc.php" as="Horde/Cache/Storage/Apc.php" />
+   <install name="lib/Horde/Cache/Storage/Eaccelerator.php" as="Horde/Cache/Storage/Eaccelerator.php" />
+   <install name="lib/Horde/Cache/Storage/File.php" as="Horde/Cache/Storage/File.php" />
+   <install name="lib/Horde/Cache/Storage/Memcache.php" as="Horde/Cache/Storage/Memcache.php" />
+   <install name="lib/Horde/Cache/Storage/Mock.php" as="Horde/Cache/Storage/Mock.php" />
+   <install name="lib/Horde/Cache/Storage/Null.php" as="Horde/Cache/Storage/Null.php" />
+   <install name="lib/Horde/Cache/Storage/Session.php" as="Horde/Cache/Storage/Session.php" />
+   <install name="lib/Horde/Cache/Storage/Sql.php" as="Horde/Cache/Storage/Sql.php" />
+   <install name="lib/Horde/Cache/Storage/Stack.php" as="Horde/Cache/Storage/Stack.php" />
+   <install name="lib/Horde/Cache/Storage/Xcache.php" as="Horde/Cache/Storage/Xcache.php" />
    <install name="lib/Horde/Cache/Exception.php" as="Horde/Cache/Exception.php" />
-   <install name="lib/Horde/Cache/File.php" as="Horde/Cache/File.php" />
-   <install name="lib/Horde/Cache/Memcache.php" as="Horde/Cache/Memcache.php" />
-   <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/Session.php" as="Horde/Cache/Session.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/Storage.php" as="Horde/Cache/Storage.php" />
    <install name="lib/Horde/Cache.php" as="Horde/Cache.php" />
   </filelist>
  </phprelease>
index 28352df..b59baf4 100644 (file)
@@ -46,51 +46,51 @@ class Horde_Core_Factory_Cache
         if (isset($GLOBALS['conf']['cache']['default_lifetime'])) {
             $params['lifetime'] = $GLOBALS['conf']['cache']['default_lifetime'];
         }
+        $params['logger'] = $injector->getInstance('Horde_Log_Logger');
 
-        $logger = $injector->getInstance('Horde_Log_Logger');
-        $params['logger'] = $logger;
-
-        $base_params = $params;
-
-        if (strcasecmp($driver, 'Memcache') === 0) {
+        $lc_driver = Horde_String::lower($driver);
+        switch ($lc_driver) {
+        case 'Memcache':
             $params['memcache'] = $injector->getInstance('Horde_Memcache');
-        } elseif (strcasecmp($driver, 'Sql') === 0) {
+            break;
+
+        case 'Sql':
             $params['db'] = $injector->getInstance('Horde_Db_Adapter');
+            break;
         }
 
+        $storage = $this->_getStorage($driver, $params);
+
         if (!empty($GLOBALS['conf']['cache']['use_memorycache']) &&
-            ((strcasecmp($driver, 'Sql') === 0) ||
-             (strcasecmp($driver, 'File') === 0))) {
+            in_array($lc_driver, array('File', 'Sql'))) {
             if (strcasecmp($GLOBALS['conf']['cache']['use_memorycache'], 'Memcache') === 0) {
-                $base_params['memcache'] = $injector->getInstance('Horde_Memcache');
+                $params['memcache'] = $injector->getInstance('Horde_Memcache');
             }
 
-            $class1 = $this->_driverToClassname($GLOBALS['conf']['cache']['use_memorycache']);
-            $class2 = $this->_driverToClassname($driver);
-            $params = array(
+            $cname = $this->_driverToClassname($GLOBALS['conf']['cache']['use_memorycache']);
+            $storage = new Horde_Cache_Storage_Stack(array(
                 'stack' => array(
-                    new $class1($base_params),
-                    new $class2($params),
+                    $this->_getStorage($GLOBALS['conf']['cache']['use_memorycache'], $params),
+                    $storage
                 )
-            );
-            $driver = 'Stack';
+            ));
         }
 
-        $classname = $this->_driverToClassname($driver);
-        return new $classname($params);
+        return new Horde_Cache($storage, $params);
     }
 
     /**
      */
-    protected function _driverToClassname($driver)
+    protected function _getStorage($driver, $params)
     {
         $driver = ucfirst(basename($driver));
-        $classname = 'Horde_Cache_' . $driver;
+        $classname = 'Horde_Cache_Storage_' . $driver;
+
         if (!class_exists($classname)) {
-            $classname = 'Horde_Cache_Null';
+            $classname = 'Horde_Cache_Storage_Null';
         }
 
-        return $classname;
+        return new $classname($params);
     }
 
 }