自定义缓存实现

Smarty默认是使用基于文件的缓存机制,作为可选的方案,你可以自定义一套缓存机制的实现,来进行缓存文件的读写和删除。

Note

Smarty2使用$cache_handler_func的回调函数来实现此功能。 而Smarty3使用了Smarty_CacheResource模块来实现。

自定义缓存实现可以实现类似下面的目的: 用更快的存储引擎来替代较慢的文件系统, 使缓存可以分布到多台服务器上。

Smarty可以通过API Smarty_CacheResource_Custom 或者 Smarty_CacheResource_KeyValueStore 来实现缓存机制。 Smarty_CacheResource_Custom是比较简单的API,直接通过覆盖读、写、删除等操作来实现缓存机制。 该API可以使用于任何你觉得适合的方式,或存储到任何你觉得适合的地方。 Smarty_CacheResource_KeyValueStore的API可让你使用K-V存储模式(比如APC,Memcache等)来实现缓存机制。 更进一步,就算是多层的缓存组如"a|b|c",该API也让你可以通过删除缓存组"a"来将整个嵌套的缓存组删除, 即使K-V存储机制本身无法实现这种层次结构的存储。

自定义缓存可以放到$plugins_dir目录下并命名为cacheresource.foobarxyz.php, 或者在运行时通过registerCacheResource() 来进行注册。 上面两种方式都必须设置$caching_type 来启动你的自定义缓存机制。

Example 15.15. 通过MySQL实现自定义缓存机制


<?php

require_once 'libs/Smarty.class.php';
$smarty = new Smarty();
$smarty->caching_type = 'mysql';

/**
 * MySQL 缓存
 *
 * 通过自定义缓存的接口API,让MySQL来作为Smarty的输出缓存存储器。
 *
 * 表定义:
 * <pre>CREATE TABLE IF NOT EXISTS `output_cache` (
 *   `id` CHAR(40) NOT NULL COMMENT 'sha1 hash',
 *   `name` VARCHAR(250) NOT NULL,
 *   `cache_id` VARCHAR(250) NULL DEFAULT NULL,
 *   `compile_id` VARCHAR(250) NULL DEFAULT NULL,
 *   `modified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
 *   `content` LONGTEXT NOT NULL,
 *   PRIMARY KEY (`id`),
 *   INDEX(`name`),
 *   INDEX(`cache_id`),
 *   INDEX(`compile_id`),
 *   INDEX(`modified`)
 * ) ENGINE = InnoDB;</pre>
 *
 * @package CacheResource-examples
 * @author Rodney Rehm
 */
class Smarty_CacheResource_Mysql extends Smarty_CacheResource_Custom {
    // PDO 对象
    protected $db;
    protected $fetch;
    protected $fetchTimestamp;
    protected $save;
    
    public function __construct() {
        try {
            $this->db = new PDO("mysql:dbname=test;host=127.0.0.1", "smarty", "smarty");
        } catch (PDOException $e) {
            throw new SmartyException('Mysql 源无法链接: ' . $e->getMessage());
        }
        $this->fetch = $this->db->prepare('SELECT modified, content FROM output_cache WHERE id = :id');
        $this->fetchTimestamp = $this->db->prepare('SELECT modified FROM output_cache WHERE id = :id');
        $this->save = $this->db->prepare('REPLACE INTO output_cache (id, name, cache_id, compile_id, content)
            VALUES  (:id, :name, :cache_id, :compile_id, :content)');
    }

    /**
	 * 从数据表中获取缓存的内容及修改时间
     *
     * @param string $id 缓存内容的唯一识别ID
     * @param string $name 模板名称
     * @param string $cache_id 缓存ID
     * @param string $compile_id 编译ID
     * @param string $content (引用的)缓存内容
     * @param integer $mtime 缓存修改的时间戳 (epoch)
     * @return void
     */
    protected function fetch($id, $name, $cache_id, $compile_id, &$content, &$mtime)
    {
        $this->fetch->execute(array('id' => $id));
        $row = $this->fetch->fetch();
        $this->fetch->closeCursor();        
        if ($row) {
            $content = $row['content'];
            $mtime = strtotime($row['modified']);
        } else {
            $content = null;
            $mtime = null;
        }
    }
    
    /**
	 * 从数据表中获取缓存的修改时间
     *
     * @note 这是个可选的实现接口。在你确定仅获取修改时间会比获取整个内容要更快的时候,使用此接口。
     * @param string $id 缓存内容的唯一识别ID
     * @param string $name 模板名称
     * @param string $cache_id 缓存ID
     * @param string $compile_id 编译ID
     * @return integer|boolean 返回模板修改时间,如果找不到缓存则返回false
     */
    protected function fetchTimestamp($id, $name, $cache_id, $compile_id)
    {
        $this->fetchTimestamp->execute(array('id' => $id));
        $mtime = strtotime($this->fetchTimestamp->fetchColumn());
        $this->fetchTimestamp->closeCursor();
        return $mtime;
    }
    
    /**
     * 保存缓存内容到数据表
     *
     * @param string $id 缓存内容的唯一识别ID
     * @param string $name 模板名称
     * @param string $cache_id 缓存ID
     * @param string $compile_id 编译ID
     * @param integer|null $exp_time 缓存过期时间,或null
     * @param string $content 需要缓存的内容
     * @return boolean 成功true,失败false
     */
    protected function save($id, $name, $cache_id, $compile_id, $exp_time, $content)
    {
        $this->save->execute(array(
            'id' => $id,
            'name' => $name,
            'cache_id' => $cache_id,
            'compile_id' => $compile_id,
            'content' => $content,
        ));
        return !!$this->save->rowCount();
    }
    
    /**
     * 从数据表中删除缓存
     *
     * @param string $name 模板名称
     * @param string $cache_id 缓存ID
     * @param string $compile_id 编译ID
     * @param integer|null $exp_time 缓存过期时间,或null
     * @return integer 返回被删除的缓存数量
     */
    protected function delete($name, $cache_id, $compile_id, $exp_time)
    {
        // 删除整个缓存
        if ($name === null && $cache_id === null && $compile_id === null && $exp_time === null) {
			// 返回删除缓存记录的数量,需要再进行一次查询来计算。
            $query = $this->db->query('TRUNCATE TABLE output_cache');
            return -1;
        }
        // 组成查找条件
        $where = array();
        // 匹配名称
        if ($name !== null) {
            $where[] = 'name = ' . $this->db->quote($name);
        }
        // 匹配编译ID
        if ($compile_id !== null) {
            $where[] = 'compile_id = ' . $this->db->quote($compile_id);
        }
        // 匹配过期时间范围
        if ($exp_time !== null) {
            $where[] = 'modified < DATE_SUB(NOW(), INTERVAL ' . intval($exp_time) . ' SECOND)';
        }
        // 匹配缓存ID和缓存组的子ID
        if ($cache_id !== null) {
            $where[] = '(cache_id = '. $this->db->quote($cache_id)
                . ' OR cache_id LIKE '. $this->db->quote($cache_id .'|%') .')';
        }
        // 执行删除
        $query = $this->db->query('DELETE FROM output_cache WHERE ' . join(' AND ', $where));
        return $query->rowCount();
    }
}
?>

   

Example 15.16. 通过Memcache实现自定义缓存机制


<?php

require_once 'libs/Smarty.class.php';
$smarty = new Smarty();
$smarty->caching_type = 'memcache';

/**
 * Memcache 缓存
 *
 * 通过K-V存储的API来把memcache作为Smarty的输出缓存器。
 *
 * 注意memcache要求key的长度只能是256个字符以内,
 * 所以程序中,key都进行sha1哈希计算后才使用。
 *
 * @package CacheResource-examples
 * @author Rodney Rehm
 */
class Smarty_CacheResource_Memcache extends Smarty_CacheResource_KeyValueStore {
    /**
     * memcache 对象
     * @var Memcache
     */
    protected $memcache = null;
    
    public function __construct()
    {
        $this->memcache = new Memcache();
        $this->memcache->addServer( '127.0.0.1', 11211 );
    }
    
    /**
	 * 从memcache中获取一系列key的值。
     *
     * @param array $keys 多个key
     * @return array 按key的顺序返回的对应值
     * @return boolean 成功返回true,失败返回false
     */
    protected function read(array $keys)
    {
        $_keys = $lookup = array();
        foreach ($keys as $k) {
            $_k = sha1($k);
            $_keys[] = $_k;
            $lookup[$_k] = $k;
        }
        $_res = array();
        $res = $this->memcache->get($_keys);
        foreach ($res as $k => $v) {
            $_res[$lookup[$k]] = $v;
        }
        return $_res;
    }
    
    /**
	 * 将一系列的key对应的值存储到memcache中。
     *
     * @param array $keys 多个kv对应的数据值
     * @param int $expire 过期时间
     * @return boolean 成功返回true,失败返回false
     */
    protected function write(array $keys, $expire=null)
    {
        foreach ($keys as $k => $v) {
            $k = sha1($k);
            $this->memcache->set($k, $v, 0, $expire);
        }
        return true;
    }

    /**
	 * 从memcache中删除
     *
     * @param array $keys 待删除的多个key
     * @return boolean 成功返回true,失败返回false
     */
    protected function delete(array $keys)
    {
        foreach ($keys as $k) {
            $k = sha1($k);
            $this->memcache->delete($k);
        }
        return true;
    }

    /**
     * 清空全部的值
     *
     * @return boolean 成功返回true,失败返回false
     */
    protected function purge()
    {
        return $this->memcache->flush();
    }
}
?>