till
2011-11-02 8ae0937604c7466b0b97b3ac5610dff034845569
program/include/rcube_cache.php
@@ -28,7 +28,7 @@
 * @package    Cache
 * @author     Thomas Bruederli <roundcube@gmail.com>
 * @author     Aleksander Machniak <alec@alec.pl>
 * @version    1.0
 * @version    1.1
 */
class rcube_cache
{
@@ -42,6 +42,7 @@
    private $userid;
    private $prefix;
    private $ttl;
    private $packed;
    private $index;
    private $cache         = array();
    private $cache_keys    = array();
@@ -56,8 +57,11 @@
     * @param int    $userid User identifier
     * @param string $prefix Key name prefix
     * @param int    $ttl    Expiration time of memcache/apc items in seconds (max.2592000)
     * @param bool   $packed Enables/disabled data serialization.
     *                       It's possible to disable data serialization if you're sure
     *                       stored data will be always a safe string
     */
    function __construct($type, $userid, $prefix='', $ttl=0)
    function __construct($type, $userid, $prefix='', $ttl=0, $packed=true)
    {
        $rcmail = rcmail::get_instance();
        $type   = strtolower($type);
@@ -75,9 +79,10 @@
            $this->db   = $rcmail->get_dbh();
        }
        $this->userid = (int) $userid;
        $this->ttl    = (int) $ttl;
        $this->prefix = $prefix;
        $this->userid    = (int) $userid;
        $this->ttl       = (int) $ttl;
        $this->packed    = $packed;
        $this->prefix    = $prefix;
    }
@@ -109,6 +114,38 @@
        $this->cache[$key]         = $data;
        $this->cache_changed       = true;
        $this->cache_changes[$key] = true;
    }
    /**
     * Returns cached value without storing it in internal memory.
     *
     * @param string $key Cache key name
     *
     * @return mixed Cached value
     */
    function read($key)
    {
        if (array_key_exists($key, $this->cache)) {
            return $this->cache[$key];
        }
        return $this->read_record($key, true);
    }
    /**
     * Sets (add/update) value in cache and immediately saves
     * it in the backend, no internal memory will be used.
     *
     * @param string $key  Cache key name
     * @param mixed  $data Cache data
     *
     * @param boolean True on success, False on failure
     */
    function write($key, $data)
    {
        return $this->write_record($key, $this->packed ? serialize($data) : $data);
    }
@@ -151,6 +188,24 @@
    /**
     * Remove cache records older than ttl
     */
    function expunge()
    {
        if ($this->type == 'db' && $this->db) {
            $this->db->query(
                "DELETE FROM ".get_table_name('cache').
                " WHERE user_id = ?".
                " AND cache_key LIKE ?".
                " AND " . $this->db->unixtimestamp('created')." < ?",
                $this->userid,
                $this->prefix.'.%',
                time() - $this->ttl);
        }
    }
    /**
     * Writes the cache back to the DB.
     */
    function close()
@@ -164,7 +219,7 @@
            if ($this->cache_changes[$key]) {
                // Make sure we're not going to write unchanged data
                // by comparing current md5 sum with the sum calculated on DB read
                $data = serialize($data);
                $data = $this->packed ? serialize($data) : $data;
                if (!$this->cache_sums[$key] || $this->cache_sums[$key] != md5($data)) {
                    $this->write_record($key, $data);
@@ -179,28 +234,41 @@
    /**
     * Reads cache entry.
     *
     * @param string $key Cache key name
     * @param string  $key     Cache key name
     * @param boolean $nostore Enable to skip in-memory store
     *
     * @return mixed Cached value
     */
    private function read_record($key)
    private function read_record($key, $nostore=false)
    {
        if (!$this->db) {
            return null;
        }
        if ($this->type == 'memcache') {
            $data = $this->db->get($this->ckey($key));
        }
        else if ($this->type == 'apc') {
            $data = apc_fetch($this->ckey($key));
       }
        if ($this->type != 'db') {
            if ($this->type == 'memcache') {
                $data = $this->db->get($this->ckey($key));
            }
            else if ($this->type == 'apc') {
                $data = apc_fetch($this->ckey($key));
           }
        if ($data) {
            $this->cache_sums[$key] = md5($data);
            $this->cache[$key]      = unserialize($data);
            if ($data) {
                $md5sum = md5($data);
                $data   = $this->packed ? unserialize($data) : $data;
                if ($nostore) {
                    return $data;
                }
                $this->cache_sums[$key] = $md5sum;
                $this->cache[$key]      = $data;
            }
            else {
                $this->cache[$key] = null;
            }
        }
        else if ($this->type == 'db') {
        else {
            $sql_result = $this->db->limitquery(
                "SELECT cache_id, data, cache_key".
                " FROM ".get_table_name('cache').
@@ -214,10 +282,20 @@
            if ($sql_arr = $this->db->fetch_assoc($sql_result)) {
                $key = substr($sql_arr['cache_key'], strlen($this->prefix)+1);
                $md5sum = $sql_arr['data'] ? md5($sql_arr['data']) : null;
                $data   = $sql_arr['data'] ? unserialize($sql_arr['data']) : null;
                if ($sql_arr['data']) {
                    $data = $this->packed ? unserialize($sql_arr['data']) : $sql_arr['data'];
                }
                if ($nostore) {
                    return $data;
                }
                $this->cache[$key]      = $data;
               $this->cache_sums[$key] = $md5sum;
                $this->cache_keys[$key] = $sql_arr['cache_id'];
            }
            else {
                $this->cache[$key] = null;
            }
        }
@@ -230,6 +308,8 @@
     *
     * @param string $key  Cache key name
     * @param mxied  $data Serialized cache data 
     *
     * @param boolean True on success, False on failure
     */
    private function write_record($key, $data)
    {
@@ -257,7 +337,7 @@
        // update existing cache record
        if ($key_exists) {
            $this->db->query(
            $result = $this->db->query(
                "UPDATE ".get_table_name('cache').
                " SET created = ". $this->db->now().", data = ?".
                " WHERE user_id = ?".
@@ -268,14 +348,14 @@
        else {
            // for better performance we allow more records for one key
            // so, no need to check if record exist (see rcube_cache::read_record())
            $this->db->query(
            $result = $this->db->query(
                "INSERT INTO ".get_table_name('cache').
                " (created, user_id, cache_key, data)".
                " VALUES (".$this->db->now().", ?, ?, ?)",
                $this->userid, $key, $data);
        }
        return true;
        return $this->db->affected_rows($result);
    }
@@ -341,28 +421,45 @@
    /**
     * Adds entry into memcache/apc DB.
     *
     * @param string  $key   Cache key name
     * @param mxied   $data  Serialized cache data
     * @param bollean $index Enables immediate index update
     *
     * @param boolean True on success, False on failure
     */
    private function add_record($key, $data)
    private function add_record($key, $data, $index=false)
    {
        if ($this->type == 'memcache') {
            $result = $this->db->replace($key, $data, MEMCACHE_COMPRESSED, $this->ttl);
            if (!$result)
                $result = $this->db->set($key, $data, MEMCACHE_COMPRESSED, $this->ttl);
            return $result;
        }
        if ($this->type == 'apc') {
        else if ($this->type == 'apc') {
            if (apc_exists($key))
                apc_delete($key);
            return apc_store($key, $data, $this->ttl);
            $result = apc_store($key, $data, $this->ttl);
        }
        // Update index
        if ($index && $result) {
            $this->load_index();
            if (array_search($key, $this->index) === false) {
                $this->index[] = $key;
                $data = serialize($this->index);
                $this->add_record($this->ikey(), $data);
            }
        }
        return $result;
    }
    /**
     * Deletes entry from memcache/apc DB.
     */
    private function delete_record($index=true)
    private function delete_record($key, $index=true)
    {
        if ($this->type == 'memcache')
            $this->db->delete($this->ckey($key));