* // Получает пользователя по его логину * public function GetUserByLogin($sLogin) { * // Пытаемся получить значение из кеша * if (false === ($oUser = $this->Cache_Get("user_login_{$sLogin}"))) { * // Если значение из кеша получить не удалось, то обращаемся к базе данных * $oUser = $this->oMapper->GetUserByLogin($sLogin); * // Записываем значение в кеш * $this->Cache_Set($oUser, "user_login_{$sLogin}", array(), 60*60*24*5); * } * return $oUser; * } * * // Обновляет пользовател в БД * public function UpdateUser($oUser) { * // Удаляем кеш конкретного пользователя * $this->Cache_Delete("user_login_{$oUser->getLogin()}"); * // Удалем кеш со списком всех пользователей * $this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG,array('user_update')); * // Обновлем пользовател в базе данных * return $this->oMapper->UpdateUser($oUser); * } * * // Получает список всех пользователей * public function GetUsers() { * // Пытаемся получить значение из кеша * if (false === ($aUserList = $this->Cache_Get("users"))) { * // Если значение из кеша получить не удалось, то обращаемся к базе данных * $aUserList = $this->oMapper->GetUsers(); * // Записываем значение в кеш * $this->Cache_Set($aUserList, "users", array('user_update'), 60*60*24*5); * } * return $aUserList; * } * * * @package engine.modules * @since 1.0 */ class ModuleCache extends Module { /** * Объект бэкенда кеширования * * @var Zend_Cache_Backend */ protected $oBackendCache=null; /** * Используется кеширование или нет * * @var bool */ protected $bUseCache; /** * Тип кеширования, прописан в глобльном конфиге config.php * * @var string */ protected $sCacheType; /** * Статистика кеширования * * @var array */ protected $aStats=array( 'time' =>0, 'count' => 0, 'count_get' => 0, 'count_set' => 0, ); /** * Хранилище для кеша на время сессии * @see SetLife * @see GetLife * * @var array */ protected $aStoreLife=array(); /** * Префикс для "умного" кеширования * @see SmartSet * @see SmartGet * * @var string */ protected $sPrefixSmartCache='for-smart-cache-'; /** * Инициализируем нужный тип кеша * */ public function Init() { $this->bUseCache=Config::Get('sys.cache.use'); $this->sCacheType=Config::Get('sys.cache.type'); if (!$this->bUseCache) { return false; } /** * Файловый кеш */ if ($this->sCacheType==SYS_CACHE_TYPE_FILE) { require_once(LS_DKCACHE_PATH.'Zend/Cache/Backend/File.php'); $sDirCache=Config::Get('sys.cache.dir').'/system/'; if (!is_dir($sDirCache)) @mkdir($sDirCache,0777,true); $oCahe = new Zend_Cache_Backend_File( array( 'cache_dir' => $sDirCache, 'file_name_prefix' => Config::Get('sys.cache.prefix'), 'read_control_type' => 'crc32', 'hashed_directory_level' => Config::Get('sys.cache.directory_level'), 'read_control' => true, 'file_locking' => true, ) ); $this->oBackendCache = new Dklab_Cache_Backend_Profiler($oCahe,array($this,'CalcStats')); /** * Кеш на основе Memcached */ } elseif ($this->sCacheType==SYS_CACHE_TYPE_MEMORY) { require_once(LS_DKCACHE_PATH.'Zend/Cache/Backend/Memcached.php'); $aConfigMem=Config::Get('memcache'); $oCahe = new Dklab_Cache_Backend_MemcachedMultiload($aConfigMem); $this->oBackendCache = new Dklab_Cache_Backend_TagEmuWrapper(new Dklab_Cache_Backend_Profiler($oCahe,array($this,'CalcStats'))); /** * Кеш на основе XCache */ } elseif ($this->sCacheType==SYS_CACHE_TYPE_XCACHE) { require_once(LS_DKCACHE_PATH.'Zend/Cache/Backend/Xcache.php'); $aConfigMem=Config::Get('xcache'); $oCahe = new Zend_Cache_Backend_Xcache(is_array($aConfigMem) ? $aConfigMem : array()); $this->oBackendCache = new Dklab_Cache_Backend_TagEmuWrapper(new Dklab_Cache_Backend_Profiler($oCahe,array($this,'CalcStats'))); } else { throw new Exception("Wrong type of caching: ".$this->sCacheType." (file, memory, xcache)"); } /** * Дабы не засорять место протухшим кешем, удаляем его в случайном порядке, например 1 из 50 раз */ if (rand(1,50)==33) { $this->Clean(Zend_Cache::CLEANING_MODE_OLD); } } /** * Получить значение из кеша * * @param string $sName Имя ключа * @return mixed|bool */ public function Get($sName) { if (!$this->bUseCache) { return false; } /** * Т.к. название кеша может быть любым то предварительно хешируем имя кеша */ if (!is_array($sName)) { $sName=md5(Config::Get('sys.cache.prefix').$sName); $data=$this->oBackendCache->load($sName); if ($this->sCacheType==SYS_CACHE_TYPE_FILE and $data!==false) { return unserialize($data); } else { return $data; } } else { return $this->multiGet($sName); } } /** * Получения значения из "умного" кеша для борьбы с конкурирующими запросами * Если кеш "протух", и за ним обращаются много запросов, то только первый запрос вернет FALSE, остальные будут получать чуть устаревшие данные из временного кеша, пока их не обновит первый запрос * * @param $sName Имя ключа * @return bool|mixed */ public function SmartGet($sName) { if (!$this->bUseCache) { return false; } /** * Если данных в основном кеше нет, то перекладываем их из временного */ if (($data=$this->Get($sName))===false) { $this->Set($this->Get($this->sPrefixSmartCache.$sName),$sName,array(),60); // храним данные из временного в основном не долго } return $data; } /** * Поддержка мульти-запросов к кешу * Такие запросы поддерживает только memcached, поэтому для остальных типов делаем эмуляцию * * @param array $aName Имя ключа * @return bool|array */ public function multiGet($aName) { if (count($aName)==0) { return false; } if ($this->sCacheType==SYS_CACHE_TYPE_MEMORY) { $aKeys=array(); $aKv=array(); foreach ($aName as $sName) { $aKeys[]=md5(Config::Get('sys.cache.prefix').$sName); $aKv[md5(Config::Get('sys.cache.prefix').$sName)]=$sName; } $data=$this->oBackendCache->load($aKeys); if ($data and is_array($data)) { $aData=array(); foreach ($data as $key => $value) { $aData[$aKv[$key]]=$value; } if (count($aData)>0) { return $aData; } } return false; } else { $aData=array(); foreach ($aName as $key => $sName) { if ((false !== ($data = $this->Get($sName)))) { $aData[$sName]=$data; } } if (count($aData)>0) { return $aData; } return false; } } /** * Записать значение в кеш * * @param mixed $data Данные для хранения в кеше * @param string $sName Имя ключа * @param array $aTags Список тегов, для возможности удалять сразу несколько кешей по тегу * @param int $iTimeLife Время жизни кеша в секундах * @return bool */ public function Set($data,$sName,$aTags=array(),$iTimeLife=false) { if (!$this->bUseCache) { return false; } /** * Т.к. название кеша может быть любым то предварительно хешируем имя кеша */ $sName=md5(Config::Get('sys.cache.prefix').$sName); if ($this->sCacheType==SYS_CACHE_TYPE_FILE) { $data=serialize($data); } return $this->oBackendCache->save($data,$sName,$aTags,$iTimeLife); } /** * Устанавливаем значение в "умном" кеша для борьбы с конкурирующими запросами * Дополнительно сохраняет значение во временном кеше на чуть большее время * * @param mixed $data Данные для хранения в кеше * @param string $sName Имя ключа * @param array $aTags Список тегов, для возможности удалять сразу несколько кешей по тегу * @param int $iTimeLife Время жизни кеша в секундах * @return bool */ public function SmartSet($data,$sName,$aTags=array(),$iTimeLife=false) { $this->Set($data,$this->sPrefixSmartCache.$sName,array(),$iTimeLife!==false ? $iTimeLife+60 : false); return $this->Set($data,$sName,$aTags,$iTimeLife); } /** * Удаляет значение из кеша по ключу(имени) * * @param string $sName Имя ключа * @return bool */ public function Delete($sName) { if (!$this->bUseCache) { return false; } /** * Т.к. название кеша может быть любым то предварительно хешируем имя кеша */ $sName=md5(Config::Get('sys.cache.prefix').$sName); return $this->oBackendCache->remove($sName); } /** * Чистит кеши * * @param int $cMode Режим очистки кеша * @param array $aTags Список тегов, актуально для режима Zend_Cache::CLEANING_MODE_MATCHING_TAG * @return bool */ public function Clean($cMode = Zend_Cache::CLEANING_MODE_ALL, $aTags = array()) { if (!$this->bUseCache) { return false; } return $this->oBackendCache->clean($cMode,$aTags); } /** * Подсчет статистики использования кеша * * @param int $iTime Время выполнения метода * @param string $sMethod имя метода */ public function CalcStats($iTime,$sMethod) { $this->aStats['time']+=$iTime; $this->aStats['count']++; if ($sMethod=='Dklab_Cache_Backend_Profiler::load') { $this->aStats['count_get']++; } if ($sMethod=='Dklab_Cache_Backend_Profiler::save') { $this->aStats['count_set']++; } } /** * Возвращает статистику использования кеша * * @return array */ public function GetStats() { return $this->aStats; } /** * Сохраняет значение в кеше на время исполнения скрипта(сессии), некий аналог Registry * * @param mixed $data Данные для сохранения в кеше * @param string $sName Имя ключа */ public function SetLife($data,$sName) { $this->aStoreLife[$sName]=$data; } /** * Получает значение из текущего кеша сессии * * @param string $sName Имя ключа * @return mixed */ public function GetLife($sName) { if (array_key_exists($sName,$this->aStoreLife)) { return $this->aStoreLife[$sName]; } return false; } } ?>