debugCache = defined('DBG_CACHE') && DBG_CACHE && $this->Application->isDebugMode(); if (class_exists('Memcache')) { $this->_storage = new MemcacheCacheStorage(); } else { $this->_storage = new CacheStorage(); } if (!$this->_storage->isWorking()) { // when one of above cache storages fails to initialize fallback to memory cache $this->_storage = new CacheStorage(); } } /** * Adds new value to cache $cache_name and identified by key $name * * @param int $name key name to add to cache * @param mixed $value value of chached record * @param int $expires expiration */ function setCache($name, $value, $expires = 0) { return $this->_storage->set($name, $value, $expires); } function reset() { return $this->_storage->reset(); } /** * Returns cached $name value from cache named $cache_name * * @param int $name key name from cache * @param bool $store_locally store data locally after retrieved * @return mixed */ function getCache($name, $store_locally = true) { $ret = $this->_storage->get($name, $store_locally); if ($this->debugCache && $store_locally) { $this->setStatistics($name, $ret); } return $ret; } /** * Deletes cached $name value from cache named $cache_name * * @param int $name key name from cache * @return mixed */ function delete($name) { $this->_storage->delete($name); } /** * Returns caching type of current storage engine * * @return int */ function getCachingType() { return $this->_storage->cachingType; } function setStatistics($name, $found) { if (strpos($name, ']:') !== false) { list ($cache_name, $name) = explode(']:', $name, 2); } else { $cache_name = '-'; } if (!array_key_exists($cache_name, $this->statistics)) { $this->statistics[$cache_name] = Array (); } if (!array_key_exists($name, $this->statistics[$cache_name])) { $this->statistics[$cache_name][$name] = Array (); } $status_key = $found ? 'found' : 'not_found'; if (!isset($this->statistics[$cache_name][$name][$status_key])) { $this->statistics[$cache_name][$name][$status_key] = 0; } $this->statistics[$cache_name][$name][$status_key]++; } function printStatistics() { $cache_size = $this->_storage->getStorageSize(); $this->Application->Debugger->appendHTML('Cache Size: ' . formatSize($cache_size) . ' (' . $cache_size . ')'); foreach ($this->statistics as $cache_name => $cache_data) { foreach ($cache_data as $key => $value) { if (!array_key_exists('found', $value) || $value['found'] == 1) { // remove cached records, that were used only 1 or 2 times unset($this->statistics[$cache_name][$key]); } } } print_pre($this->statistics, 'Cache Statistics:'); } } class CacheStorage extends Params { var $cachingType = CACHING_TYPE_TEMPORARY; /** * Returns storage size in bytes * * @return int */ function getStorageSize() { return strlen( serialize($this->_Params) ); } /** * Determines, that cache storage is working fine * * @return bool */ function isWorking() { return true; } function _getKeyInfo($name) { if (strpos($name, ':') !== false) { list ($cache_name, $name) = explode(':', $name, 2); } else { $cache_name = '-'; } return Array ($cache_name, $name); } /** * Stores value to cache * * @param string $name * @param mixed $value * @param int $expires cache record expiration time in seconds */ function set($name, $value, $expires) { list ($cache_name, $name) = $this->_getKeyInfo($name); $cache = parent::Get($cache_name, Array()); $cache[$name] = $value; parent::Set($cache_name, $cache); } /** * Returns value from cache * * @param string $name * @param bool $store_locally store data locally after retrieved * @return mixed */ function get($name, $store_locally = false) { list ($cache_name, $name) = $this->_getKeyInfo($name); $cache = parent::Get($cache_name, Array()); $ret = array_key_exists($name, $cache) ? $cache[$name] : false; return $ret; } /** * Deletes value from cache * * @param string $name * @return mixed */ function delete($name) { list ($cache_name, $name) = $this->_getKeyInfo($name); $this->set($cache_name, $name, false, -3600); } function reset() { } } class MemcacheCacheStorage extends kBase { var $cachingType = CACHING_TYPE_MEMORY; /** * Part of what we retrieve will be stored locally (per script run) not to bother memcache a lot * * @var Array */ var $_localStorage = Array (); /** * Memcache connection * * @var Memcache */ var $_handler = null; var $_enabled = false; var $_debugMode = false; function MemcacheCacheStorage() { parent::kBase(); if (array_key_exists('MemcacheServers', $GLOBALS['vars'])) { // for advanced users, who want to save one SQL on each page load $memcached_servers = $GLOBALS['vars']['MemcacheServers']; } else { $memcached_servers = $this->Application->ConfigValue('MemcacheServers'); } if ($memcached_servers && class_exists('Memcache')) { $this->_enabled = true; $this->_handler = new Memcache(); $servers = explode(';', $memcached_servers); foreach ($servers as $server) { list ($server, $port) = strpos($server, ':') !== false ? explode(':', $server, 2) : Array ($server, 11211); $this->_handler->addServer($server, $port); } // try to set something to cache, if not working - set $this->Memcached to null if (!$this->_handler->set('test', 1)) { $this->_handler = null; $this->_enabled = false; } } $this->_debugMode = $this->Application->isDebugMode(); if ($this->_debugMode) { $this->Application->Debugger->appendHTML('MemCache Enabled: ' . ($this->_enabled ? 'YES' : 'NO')); } } /** * Returns storage size in bytes * * @return int */ function getStorageSize() { return strlen( serialize($this->_localStorage) ); } /** * Returns site-wide caching prefix * * @param bool $only_site_key_name * @return string */ function _cachePrefix($only_site_key_name = false) { if (!$this->_enabled) { return false; } // don't use SERVER_NAME here, because it may be cron, or command line request also $site_key = 'site_serial:' . crc32(FULL_PATH); if ($only_site_key_name) { return $site_key; } $site_serial = $this->_handler->get($site_key); if (!$site_serial) { $site_serial = 1; $this->_handler->set($site_key, $site_serial); } return "$site_key:$site_serial:"; } /** * Determines, that cache storage is working fine * * @return bool */ function isWorking() { return $this->_enabled; } /** * Stores value to cache * * @param string $name * @param mixed $value * @param int $expires cache record expiration time in seconds */ function set($name, $value, $expiration) { if (!$this->_enabled) { return false; } $name = $this->prepareKeyName($name); unset($this->_localStorage[$name]); // 0 - don't use compression return $this->_handler->set($name, $value, 0, $expiration); } /** * Returns value from cache * * @param string $name * @param bool $store_locally store data locally after retrieved * @param bool $replace_serials * @return mixed */ function get($name, $store_locally = true, $replace_serials = true) { if (!$this->_enabled) { return false; } $name = $this->prepareKeyName($name, $replace_serials); if ($store_locally) { if (array_key_exists($name, $this->_localStorage)) { return $this->_localStorage[$name]; } } $res = $this->_handler->get($name); if ($replace_serials && $this->_debugMode) { // don't display subsequent serial cache retrievals (ones, that are part of keys) if (is_array($res)) { $this->Application->Debugger->appendHTML('Restoring key "' . $name . '". Type: ' . gettype($res) . '.'); } else { $res_display = strip_tags($res); if (strlen($res_display) > 200) { $res_display = substr($res_display, 0, 50) . ' ...'; } $this->Application->Debugger->appendHTML('Restoring key "' . $name . '" resulted [' . $res_display . ']'); } } $this->_localStorage[$name] = $res; return $res; } /** * Replaces serials in cache variable name * * @param string $name * @param bool $replace_serials * @return string */ function prepareKeyName($name, $replace_serials = true) { // replace serials in key name if ($replace_serials && preg_match_all('/\[%(.*?)%\]/', $name, $regs)) { // [%LangSerial%] - prefix-wide serial in case of any change in "lang" prefix // [%LangIDSerial:5%] - one id-wide serial in case of data, associated with given id was changed // [%CiIDSerial:ItemResourceId:5%] - foreign key-based serial in case of data, associated with given foreign key was changed foreach ($regs[1] as $serial_name) { $name = str_replace('[%' . $serial_name . '%]', '[' . $serial_name . '=' . $this->get($serial_name, true, false) . ']', $name); } } // add site-wide prefix to key return $this->_cachePrefix() . $name; } /** * Deletes value from cache * * @param string $name * @return mixed */ function delete($name) { if (!$this->_enabled) { return false; } $name = $this->prepareKeyName($name); unset($this->_localStorage[$name]); return $this->_handler->delete($name); } /** * Reset's all memory cache at once */ function reset() { // don't check for enabled, because we maybe need to reset cache anyway $site_key = $this->_cachePrefix(true); $this->_handler->set($site_key, $this->_handler->get($site_key) + 1); } }