Index: branches/5.1.x/core/kernel/utility/cache.php =================================================================== diff -u -N -r13086 -r13168 --- branches/5.1.x/core/kernel/utility/cache.php (.../cache.php) (revision 13086) +++ branches/5.1.x/core/kernel/utility/cache.php (.../cache.php) (revision 13168) @@ -1,6 +1,6 @@ debugCache = $this->Application->isDebugMode() && constOn('DBG_CACHE'); + $this->debugCache = defined('DBG_CACHE') && DBG_CACHE && $this->Application->isDebugMode(); - $memcached_servers = false; // 'localhost:11211'; // $this->Application->ConfigValue('MemcachedServers'); - - if ($memcached_servers && class_exists('Memcache')) { - $this->_storage = new MemcacheCacheStorage($memcached_servers); + if (class_exists('Memcache')) { + $this->_storage = new MemcacheCacheStorage(); } - /*else if (false || $this->Application->ConfigValue('UseFileCache')) { - $this->_storage = new FileCacheStorage('file_cache.tmp'); - }*/ else { $this->_storage = new CacheStorage(); } @@ -50,58 +57,90 @@ } /** - * Adds new value to cache $cache_name and identified by key $key + * Adds new value to cache $cache_name and identified by key $name * - * @param string $cache_name cache name - * @param int $key key name to add to cache + * @param int $name key name to add to cache * @param mixed $value value of chached record + * @param int $expires expiration */ - function setCache($cache_name, $key, $value, $expires = 3600) + function setCache($name, $value, $expires = 0) { - return $this->_storage->set($cache_name, $key, $value, $expires); + return $this->_storage->set($name, $value, $expires); } + function reset() + { + return $this->_storage->reset(); + } + /** - * Returns cached $key value from cache named $cache_name + * Returns cached $name value from cache named $cache_name * - * @param string $cache_name cache name - * @param int $key key name from cache + * @param int $name key name from cache + * @param bool $store_locally store data locally after retrieved * @return mixed */ - function getCache($cache_name, $key) + function getCache($name, $store_locally = true) { - $ret = $this->_storage->get($cache_name, $key); + $ret = $this->_storage->get($name, $store_locally); - $this->setStatistics($cache_name, $key, $ret); + if ($this->debugCache && $store_locally) { + $this->setStatistics($name, $ret); + } return $ret; } - function setStatistics($cache_name, $key, $found) + /** + * Deletes cached $name value from cache named $cache_name + * + * @param int $name key name from cache + * @return mixed + */ + function delete($name) { - if (!$this->debugCache) { - return true; + $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($key, $this->statistics[$cache_name])) { - $this->statistics[$cache_name][$key] = 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][$key][$status_key])) { - $this->statistics[$cache_name][$key][$status_key] = 0; + + if (!isset($this->statistics[$cache_name][$name][$status_key])) { + $this->statistics[$cache_name][$name][$status_key] = 0; } - $this->statistics[$cache_name][$key][$status_key]++; + $this->statistics[$cache_name][$name][$status_key]++; } function printStatistics() { - $cache_size = strlen(serialize($this->_storage)); + $cache_size = $this->_storage->getStorageSize(); $this->Application->Debugger->appendHTML('Cache Size: ' . formatSize($cache_size) . ' (' . $cache_size . ')'); @@ -120,7 +159,19 @@ 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 @@ -130,160 +181,292 @@ 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 $cache_name - * @param string $key + * @param string $name * @param mixed $value * @param int $expires cache record expiration time in seconds */ - function set($cache_name, $key, $value, $expires) + function set($name, $value, $expires) { + list ($cache_name, $name) = $this->_getKeyInfo($name); + $cache = parent::Get($cache_name, Array()); - $cache[$key] = $value; + $cache[$name] = $value; parent::Set($cache_name, $cache); } /** * Returns value from cache * - * @param string $cache_name - * @param string $key + * @param string $name + * @param bool $store_locally store data locally after retrieved * @return mixed */ - function get($cache_name, $key) + function get($name, $store_locally = false) { + list ($cache_name, $name) = $this->_getKeyInfo($name); + $cache = parent::Get($cache_name, Array()); - $ret = array_key_exists($key, $cache) ? $cache[$key] : false; + $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 { + 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; - function MemcacheCacheStorage($servers) + var $_enabled = false; + + var $_debugMode = false; + + function MemcacheCacheStorage() { - $this->_handler = new Memcache; + parent::kBase(); - $servers = explode(';', $servers); - foreach ($servers as $server) { - list ($server, $port) = strpos($server, ':') !== false ? explode(':', $server, 2) : Array ($server, 11211); - $this->_handler->addServer($server, $port); - } + 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->_handler->getVersion() !== false; + return $this->_enabled; } /** * Stores value to cache * - * @param string $cache_name - * @param string $key + * @param string $name * @param mixed $value * @param int $expires cache record expiration time in seconds */ - function set($cache_name, $key, $value, $expires) + function set($name, $value, $expiration) { - return $this->_handler->set($cache_name . '-' . $key, $value, false, $expires); // false could be MEMCACHE_COMPRESSED to compress values in memory + 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 $cache_name - * @param string $key + * @param string $name + * @param bool $store_locally store data locally after retrieved + * @param bool $replace_serials * @return mixed */ - function get($cache_name, $key) + function get($name, $store_locally = true, $replace_serials = true) { - return $this->_handler->get($cache_name . '-' . $key); - } - } + if (!$this->_enabled) { + return false; + } - class FileCacheStorage extends Params { + $name = $this->prepareKeyName($name, $replace_serials); - /** - * Expiration time for each variable in cache - * - * @var resource - */ - var $_expiration = Array (); + if ($store_locally) { + if (array_key_exists($name, $this->_localStorage)) { + return $this->_localStorage[$name]; + } + } - /** - * Filename for storing cache - * - * @var string - */ - var $_filename = ''; + $res = $this->_handler->get($name); - function FileCacheStorage($filename = '') - { - $this->_filename = WRITEABLE . '/cache' . '/' . $filename; + 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 (file_exists($this->_filename)) { - $cache_data = unserialize(file_get_contents($this->_filename)); + if (strlen($res_display) > 200) { + $res_display = substr($res_display, 0, 50) . ' ...'; + } + + $this->Application->Debugger->appendHTML('Restoring key "' . $name . '" resulted [' . $res_display . ']'); + } } - else { - $cache_data = Array (); - } + + $this->_localStorage[$name] = $res; + + return $res; } /** - * Determines, that cache storage is working fine + * Replaces serials in cache variable name * - * @return bool + * @param string $name + * @param bool $replace_serials + * @return string */ - function isWorking() + function prepareKeyName($name, $replace_serials = true) { - return false; + // 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; } /** - * Stores value to cache + * Deletes value from cache * - * @param string $cache_name - * @param string $key - * @param mixed $value - * @param int $expires cache record expiration time in seconds + * @param string $name + * @return mixed */ - function set($cache_name, $key, $value, $expires) + function delete($name) { - $cache = parent::Get($cache_name, Array()); - $cache[$key] = $value; + if (!$this->_enabled) { + return false; + } - parent::Set($cache_name, $cache); + $name = $this->prepareKeyName($name); + unset($this->_localStorage[$name]); + + return $this->_handler->delete($name); } /** - * Returns value from cache - * - * @param string $cache_name - * @param string $key - * @return mixed + * Reset's all memory cache at once */ - function get($cache_name, $key) + function reset() { - $cache = parent::Get($cache_name, Array()); - $ret = array_key_exists($key, $cache) ? $cache[$key] : false; + // don't check for enabled, because we maybe need to reset cache anyway + $site_key = $this->_cachePrefix(true); - return $ret; + $this->_handler->set($site_key, $this->_handler->get($site_key) + 1); } - } \ No newline at end of file + }