все форумы все темы добавить тему
Smarty3 + memcache
Вышла новая версия шаблонизатора Smarty которая, кроме улучшений, принесла еще и некоторые проблемы. Самой большой из которых, оказалось изменение реализации механизма кэширования. Причем, изменения эти никто пока документировать не собирается (в официальной документации до сих пор примеры от старой версии). Итак, что же изменилось?
Прежде всего, перестало работать свойство cache_handler. Теперь свой кэшер подключается так:
 
PHP
 
$smarty->caching_type='memcache';
 

При этом smarty ищет в каталогах с плагинами файл cacheresource.memcache.php, а внутри него ожидает увидеть класс Smarty_CacheResource_Memcache.
Указанный класс должен иметь следующие методы:
getCachedTimestamp
getCachedFilepath (в случае, если включено cache_modified_check)
getCachedContents
writeCachedContent
clearAll
clear
 
Так как за наличием файлов кэша следит сам memcache, а фалов у нас никаких нет, то опцию cache_modified_check сразу отключаем:
 
PHP
 
$smarty->caching_type='memcache';
$smarty->cache_modified_check=false;
 

при этом, понятное дело, метод getCachedFilepath становится не нужен.
Определение времени сохранения кэша можно либо эмулировать (memcache все равно сам следит за его актуальностью) или сохранить его в memcache вместе с контентом. Я пока что выбрал второй вариант.
Ну и еще один неприятный момент - в качестве кэша сохраняется не html, а php код, которые после загрузки необходимо выполнить.
В итоге класс выглядит примерно так:
 
PHP
 
 
class Smarty_CacheResource_Memcache {
        function __construct($smarty)
        {
                $this->smarty = $smarty;
 
                $this->_cacher = new Cache_Memcache(
                        LabConfig::$cfg["memcached_host"],
                        LabConfig::$cfg["memcached_port"],
                        LabConfig::$cfg["memcached_compress"]);
        }
 
        public function getCachedTimestamp($obj)
        {
                $cache_id=$this->getCacheId($obj);
                if ($this->_result[$cache_id]['timestamp'])
                        return $this->_result[$cache_id]['timestamp'];
                if ($result=$this->_cacher->get($cache_id))
                {
                        //так как кэш все равно понадобится
                        //сразу его помещаем в свойства объекта
                        $this->_result[$cache_id]=unserialize($result);
                        if ($this->_result[$cache_id]['timestamp'])
                                return $this->_result[$cache_id]['timestamp'];
                }
                return false;
        }
       
        public function getCacheId($_template)
        {
                if (!$this->cache_id)
                        $this->cache_id=md5($_template->template_resource
                        .$_template->cache_id
                        .$_template->compile_id);
                return $this->cache_id;
        }
 
        public function getCachedContents($_template, $no_render = false)
        {
                if (!$no_render)
                {
                        ob_start();
            }
                $_smarty_tpl = $_template;
                $cache_id=$this->getCacheId($_template);
                if (isset($this->_result[$cache_id]['content']))
                {
                        $result=$this->_result[$cache_id]['content'];
                }
                elseif($result=$this->_cacher->get($cache_id))
                {
                        $result=unserialize($result);
                        if (isset($result['content']))
                                $result=$result['content'];
                }
                //в переменной содержится php код,
                //который необходимо выполнить
                eval('?'.'>'.$result);
                if ($no_render)
                {
                        return null;
                }
                else
                {
                        return ob_get_clean();
                }
               
                return null;
        }
 
        public function writeCachedContent($_template, $content)
        {
                if (!$_template->resource_object->isEvaluated)
                {
                        //создаем суррогатный ключ timestamp
                        //можно обойтись без него, но при этом
                        //логика кода станет менее очевидной
                        $content=serialize(array('timestamp'=>time(),
                                                'content'=>$content));
                        $this->_cacher->save($content,
                                $this->getCacheId($_template),
                                $_template->cache_lifetime);
                        return true;
                }
                return false;
        }
 
        public function clearAll()
        {
                return $this->_cacher->clean();
        }
       
        public function clear($resource_name, $cache_id, $compile_id)
        {
                return $this->_cacher->remove(md5($resource_name
                                                .$cache_id.$compile_id));
        }
}