Index: branches/5.2.x/core/kernel/utility/cache.php
===================================================================
--- branches/5.2.x/core/kernel/utility/cache.php	(revision 15309)
+++ branches/5.2.x/core/kernel/utility/cache.php	(revision 15310)
@@ -1,979 +1,979 @@
 <?php
 /**
 * @version	$Id$
 * @package	In-Portal
 * @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
 * @license      GNU/GPL
 * In-Portal is Open Source software.
 * This means that this software may have been modified pursuant
 * the GNU General Public License, and as distributed it includes
 * or is derivative of works licensed under the GNU General Public License
 * or other free or open source software licenses.
 * See http://www.in-portal.org/license for copyright notices and details.
 */
 
 	defined('FULL_PATH') or die('restricted access!');
 
 	/**
 	 * Manager of all implemented caching handlers
 	 *
 	 */
 	class kCache extends kBase {
 
 		/**
 		 * Rebuild cache now
 		 *
 		 */
 		const REBUILD_NOW = 1;
 
 		/**
 		 * Rebuild cache later
 		 *
 		 */
 		const REBUILD_LATER = 2;
 
 		/**
 		 * Cache waiting step (in seconds)
 		 *
 		 */
 		const WAIT_STEP = 2;
 
 		/**
 		 * Object of cache handler
 		 *
 		 * @var FakeCacheHandler
 		 */
 		var $_handler = null;
 
 		/**
 		 * Part of what we retrieve will be stored locally (per script run) not to bother memcache a lot
 		 *
 		 * @var Array
 		 */
 		var $_localStorage = Array ();
 
 		/**
 		 * What type of caching is being used
 		 *
 		 * @var int
 		 */
 		var $cachingType = CACHING_TYPE_NONE;
 
 		/**
 		 * Debug cache usage
 		 *
 		 * @var bool
 		 */
 		var $debugCache = false;
 
 		/**
 		 * Displays cache usage statistics
 		 *
 		 * @var bool
 		 * @access protected
 		 */
 		protected $_storeStatistics = false;
 
 		/**
 		 * Site key name
 		 * Prepended to each cached key name
 		 *
 		 * @var string
 		 */
 		var $siteKeyName = '';
 
 		/**
 		 * Site key value
 		 * Prepended to each cached key name
 		 *
 		 * @var string
 		 */
 		var $siteKeyValue = null;
 
 		/**
 		 * Creates cache manager
 		 *
 		 * @access public
 		 */
 		public function __construct()
 		{
 			parent::__construct();
 
 			$vars = kUtil::getConfigVars();
 			$this->siteKeyName = 'site_serial:' . crc32(SQL_TYPE . '://' . SQL_USER . ':' . SQL_PASS . '@' . SQL_SERVER . ':' . TABLE_PREFIX . ':' . SQL_DB);
 
 			// get cache handler class to use
 			$handler_class = (isset($vars['CacheHandler']) ? $vars['CacheHandler'] : '') . 'CacheHandler';
 
 			// defined cache handler doesn't exist -> use default
 			if ( !class_exists($handler_class) ) {
 				$handler_class = 'FakeCacheHandler';
 			}
 
 			$handler = new $handler_class($this);
 
 			if ( !$handler->isWorking() ) {
 				// defined cache handler is not working -> use default
 				trigger_error('Failed to initialize "<strong>' . $handler_class . '</strong>" caching handler.', E_USER_WARNING);
 
 				$handler = new FakeCacheHandler($this);
 			}
 			elseif ( $this->Application->isDebugMode() && ($handler->getCachingType() == CACHING_TYPE_MEMORY) ) {
 				$this->Application->Debugger->appendHTML('Memory Caching: "<strong>' . $handler_class . '</strong>"');
 			}
 
 			$this->_handler = $handler;
 			$this->cachingType = $handler->getCachingType();
 			$this->debugCache = $handler->getCachingType() == CACHING_TYPE_MEMORY && $this->Application->isDebugMode();
 			$this->_storeStatistics = defined('DBG_CACHE') && DBG_CACHE;
 
 			if ( $this->_storeStatistics ) {
 				// don't use FileHelper, since kFactory isn't ready yet
 				if ( !file_exists(RESTRICTED . DIRECTORY_SEPARATOR . 'cache_usage') ) {
 					mkdir(RESTRICTED . DIRECTORY_SEPARATOR . 'cache_usage');
 				}
 			}
 		}
 
 		/**
 		 * Returns caching type of current storage engine
 		 *
 		 * @return int
 		 */
 		function getCachingType()
 		{
 			return $this->cachingType;
 		}
 
 		/**
 		 * Stores value to cache
 		 *
 		 * @param string $name
 		 * @param mixed $value
 		 * @param int $expiration cache record expiration time in seconds
 		 * @return bool
 		 */
 		function setCache($name, $value, $expiration)
 		{
 			// 1. stores current version of serial for given cache key
 			$this->_setCache($name . '_serials', $this->replaceSerials($name), $expiration);
 			$this->storeStatistics($name, 'W');
 
 			// 2. don't replace serials within the key
 			$saved = $this->_setCache($name, $value, $expiration);
 			$this->storeStatistics($name, 'U');
 
 			// 3. remove rebuilding mark
 			$this->delete($name . '_rebuilding');
 
 			return $saved;
 		}
 
 		/**
 		 * Stores value to cache
 		 *
 		 * @param string $name
 		 * @param mixed $value
 		 * @param int $expiration cache record expiration time in seconds
 		 * @return bool
 		 */
 		function _setCache($name, $value, $expiration)
 		{
 			$prepared_name = $this->prepareKeyName($name);
 			$this->_localStorage[$prepared_name] = $value;
 
 			return $this->_handler->set($prepared_name, $value, $expiration);
 		}
 
 		/**
 		 * Stores value to cache (only if it's not there already)
 		 *
 		 * @param string $name
 		 * @param mixed $value
 		 * @param int $expiration cache record expiration time in seconds
 		 * @return bool
 		 */
 		function addCache($name, $value, $expiration)
 		{
 			// 1. stores current version of serial for given cache key
 			$this->_setCache($name . '_serials', $this->replaceSerials($name), $expiration);
 
 			// 2. remove rebuilding mark
 			$this->delete($name . '_rebuilding');
 
 			// 3. don't replace serials within the key
 			return $this->_addCache($name, $value, $expiration);
 		}
 
 		/**
 		 * Stores value to cache (only if it's not there already)
 		 *
 		 * @param string $name
 		 * @param mixed $value
 		 * @param int $expiration cache record expiration time in seconds
 		 * @return bool
 		 */
 		function _addCache($name, $value, $expiration)
 		{
 			$prepared_name = $this->prepareKeyName($name);
 			$added = $this->_handler->add($prepared_name, $value, $expiration);
 
 			if ( $added ) {
 				$this->_localStorage[$prepared_name] = $value;
 			}
 
 			return $added;
 		}
 
 		/**
 		 * Sets rebuilding mode for given cache
 		 *
 		 * @param string $name
 		 * @param int $mode
 		 * @param int $max_rebuilding_time
 		 * @param string $miss_type
 		 * @return bool
 		 */
 		function rebuildCache($name, $mode = null, $max_rebuilding_time = 0, $miss_type = 'M')
 		{
 			if ( !isset($mode) || $mode == self::REBUILD_NOW ) {
 				$this->storeStatistics($name, $miss_type);
 
 				if ( !$max_rebuilding_time ) {
 					return true;
 				}
 
 				// prevent parallel rebuild attempt by using "add" instead of "set" method
 				if ( !$this->_addCache($name . '_rebuilding', 1, $max_rebuilding_time) ) {
 					$this->storeStatistics($name, 'l');
 
 					return false;
 				}
 
 				$this->storeStatistics($name, 'L');
 				$this->delete($name . '_rebuild');
 			}
 			elseif ( $mode == self::REBUILD_LATER ) {
 				$this->_setCache($name . '_rebuild', 1, 0);
 				$this->delete($name . '_rebuilding');
 			}
 
 			return true;
 		}
 
 		/**
 		 * Returns value from cache
 		 *
 		 * @param string $name
 		 * @param bool $store_locally store data locally after retrieved
 		 * @param int $max_rebuild_seconds
 		 * @return mixed
 		 */
 		function getCache($name, $store_locally = true, $max_rebuild_seconds = 0)
 		{
-			$cached_data = $this->_getCache(Array ($name . '_rebuild', $name . '_serials'), Array (true, false));
+			$cached_data = $this->_getCache(Array ($name . '_rebuild', $name . '_serials'), Array (true, true));
 
 			if ( $cached_data[$name . '_rebuild'] ) {
 				// cache rebuild requested -> rebuild now
 				$this->delete($name . '_rebuild');
 
 				if ( $this->rebuildCache($name, self::REBUILD_NOW, $max_rebuild_seconds, '[M1]') ) {
 					return false;
 				}
 			}
 
 			// There are 2 key types:
 			// - with serials, e.g. with_serial_key[%LangSerial%]
 			// - without serials, e.g. without_serial
 			// Evaluated serials of each cache key are stored in '{$name}_serials' cache key.
 			// If cache is present, but serial is outdated, then cache value is assumed to be outdated.
 
 			$new_serial = $this->replaceSerials($name);
 			$old_serial = $cached_data[$name . '_serials'];
 
 			if ( $name == $new_serial || $new_serial != $old_serial ) {
 				// no serials in cache key OR cache is outdated
 				$wait_seconds = $max_rebuild_seconds;
 
 				while (true) {
 					$cached_data = $this->_getCache(Array ($name, $name . '_rebuilding'), Array ($store_locally, false));
 					$cache = $cached_data[$name];
 					$rebuilding = $cached_data[$name . '_rebuilding'];
 
 					if ( ($cache === false) && (!$rebuilding || $wait_seconds == 0) ) {
 						// cache missing and nobody rebuilding it -> rebuild; enough waiting for cache to be ready
 						$this->rebuildCache($name, self::REBUILD_NOW, $max_rebuild_seconds, '[M2' . ($rebuilding ? 'R' : '!R') . ',WS=' . $wait_seconds . ']');
 
 						return false;
 					}
 					elseif ( $cache !== false ) {
 						// re-read serial, since it might have been changed in parallel process !!!
 						$old_serial = $this->_getCache($name . '_serials', false);
 
 						// cache present (if other user is rebuilding it, then it's outdated cache) -> return it
 						if ( $rebuilding || $new_serial == $old_serial ) {
 							$this->storeStatistics($name, $rebuilding ? 'h' : 'H');
 
 							return $cache;
 						}
 
 						$this->rebuildCache($name, self::REBUILD_NOW, $max_rebuild_seconds, '[M3' . ($rebuilding ? 'R' : '!R') . ',WS=' . $wait_seconds . ']');
 
 						return false;
 					}
 
 					$wait_seconds -= self::WAIT_STEP;
 					sleep(self::WAIT_STEP);
 				}
 			}
 
 			$cache = $this->_getCache($name, $store_locally);
 
 			if ( $cache === false ) {
 				$this->rebuildCache($name, self::REBUILD_NOW, $max_rebuild_seconds, '[M4]');
 			}
 			else {
 				$this->storeStatistics($name, 'H');
 			}
 
 			return $cache;
 		}
 
 		/**
 		 * Returns cached value from local cache
 		 *
 		 * @param string $prepared_name Prepared key name from kCache::prepareKeyName() function
 		 * @return mixed
 		 * @see prepareKeyName
 		 * @access public
 		 */
 		public function getFromLocalStorage($prepared_name)
 		{
 			return array_key_exists($prepared_name, $this->_localStorage) ? $this->_localStorage[$prepared_name] : false;
 		}
 
 		/**
 		 * Returns value from cache
 		 *
 		 * @param string|Array $names
 		 * @param bool|Array $store_locally store data locally after retrieved
 		 * @return mixed
 		 */
 		function _getCache($names, $store_locally = true)
 		{
 			static $request_number = 1;
 
 			$res = Array ();
 			$names = (array)$names;
 			$store_locally = (array)$store_locally;
 			$to_get = $prepared_names = array_map(Array (&$this, 'prepareKeyName'), $names);
 
 			foreach ($prepared_names as $index => $prepared_name) {
 				$name = $names[$index];
 
 				if ( $store_locally[$index] && array_key_exists($prepared_name, $this->_localStorage) ) {
 					$res[$name] = $this->_localStorage[$prepared_name];
 					unset($to_get[$index]);
 				}
 			}
 
 			if ( $to_get ) {
 				$multi_res = $this->_handler->get($to_get);
 
 				foreach ($to_get as $index => $prepared_name) {
 					$name = $names[$index];
 
 					if ( array_key_exists($prepared_name, $multi_res) ) {
 						$res[$name] =& $multi_res[$prepared_name];
 					}
 					else {
 						$res[$name] = false;
 					}
 
 					$this->_postProcessGetCache($prepared_name, $res[$name], $store_locally[$index], $request_number);
 				}
 
 				$request_number++;
 			}
 
 			return count($res) == 1 ? array_pop($res) : $res;
 		}
 
 		/**
 		 * Stores variable in local cache & collects debug info about cache
 		 *
 		 * @param string $name
 		 * @param mixed $res
 		 * @param bool $store_locally
 		 * @param int $request_number
 		 * @return void
 		 * @access protected
 		 */
 		protected function _postProcessGetCache($name, &$res, $store_locally = true, $request_number)
 		{
 			if ( $this->debugCache ) {
 				// don't display subsequent serial cache retrievals (ones, that are part of keys)
 				if ( is_array($res) ) {
 					$this->Application->Debugger->appendHTML('r' . $request_number . ': 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('r' . $request_number . ': Restoring key "' . $name . '" resulted [' . $res_display . ']');
 				}
 			}
 
 			if ( $store_locally /*&& ($res !== false)*/ ) {
 				$this->_localStorage[$name] = $res;
 			}
 		}
 
 		/**
 		 * Deletes value from cache
 		 *
 		 * @param string $name
 		 * @return mixed
 		 */
 		function delete($name)
 		{
 			$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
 			if ($this->cachingType == CACHING_TYPE_TEMPORARY) {
 				return ;
 			}
 
 			$site_key = $this->_cachePrefix(true);
 
 			$this->_handler->set($site_key, $this->_handler->get($site_key) + 1);
 		}
 
 		/**
 		 * Replaces serials and adds unique site prefix to cache variable name
 		 *
 		 * @param string $name
 		 * @return string
 		 */
 		protected function prepareKeyName($name)
 		{
 			if ( $this->cachingType == CACHING_TYPE_TEMPORARY ) {
 				return $name;
 			}
 
 			// add site-wide prefix to key
 			return $this->_cachePrefix() . $name;
 		}
 
 		/**
 		 * Replaces serials within given string
 		 *
 		 * @param string $value
 		 * @return string
 		 * @access protected
 		 */
 		protected function replaceSerials($value)
 		{
 			if ( preg_match_all('/\[%(.*?)%\]/', $value, $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
 				$serial_names = $regs[1];
 				$serial_count = count($serial_names);
 				$store_locally = Array ();
 
 				for ($i = 0; $i < $serial_count; $i++) {
 					$store_locally[]  = true;
 				}
 
 				$serial_values = $this->_getCache($serial_names, $store_locally);
 
 				if ( !is_array($serial_values) ) {
 					$serial_values = Array (current($serial_names) => $serial_values);
 				}
 
 				foreach ($serial_names as $serial_name) {
 					$value = str_replace('[%' . $serial_name . '%]', '[' . $serial_name . '=' . $serial_values[$serial_name] . ']', $value);
 				}
 			}
 
 			return $value;
 		}
 
 		/**
 		 * Returns site-wide caching prefix
 		 *
 		 * @param bool $only_site_key_name
 		 * @return string
 		 */
 		function _cachePrefix($only_site_key_name = false)
 		{
 			if ($only_site_key_name) {
 				return $this->siteKeyName;
 			}
 
 			if ( !isset($this->siteKeyValue) ) {
 				$this->siteKeyValue = $this->_handler->get($this->siteKeyName);
 
 				if (!$this->siteKeyValue) {
 					$this->siteKeyValue = 1;
 					$this->_handler->set($this->siteKeyName, $this->siteKeyValue);
 				}
 			}
 
 			return "{$this->siteKeyName}:{$this->siteKeyValue}:";
 		}
 
 		/**
 		 * Stores statistics about cache usage in a file (one file per cache)
 		 *
 		 * @param string $name
 		 * @param string $action_type {M - miss, L - lock, W - write, U - unlock, H - actual hit, h - outdated hit}
 		 * @return void
 		 * @access public
 		 */
 		public function storeStatistics($name, $action_type)
 		{
 			if ( !$this->_storeStatistics ) {
 				return;
 			}
 
 			$name = str_replace(Array ('/', '\\', ':'), '_', $name);
 			$fp = fopen(RESTRICTED . DIRECTORY_SEPARATOR . 'cache_usage' . DIRECTORY_SEPARATOR . $name, 'a');
 			fwrite($fp, $action_type);
 			fclose($fp);
 		}
 	}
 
 
 	abstract class kCacheHandler {
 
 		/**
 		 * Remembers status of cache handler (working or not)
 		 *
 		 * @var bool
 		 * @access protected
 		 */
 		protected $_enabled = false;
 
 		/**
 		 * Caching type that caching handler implements
 		 *
 		 * @var int
 		 * @access protected
 		 */
 		protected $cachingType;
 
 		/**
 		 *
 		 * @var kCache
 		 * @access protected
 		 */
 		protected $parent;
 
 		public function __construct(kCache $parent)
 		{
 			$this->parent = $parent;
 		}
 
 		/**
 		 * Retrieves value from cache
 		 *
 		 * @param string $names
 		 * @return mixed
 		 * @access public
 		 */
 		abstract public function get($names);
 
 		/**
 		 * Stores value in cache
 		 *
 		 * @param string $name
 		 * @param mixed $value
 		 * @param int $expiration
 		 * @return bool
 		 * @access public
 		 */
 		abstract public function set($name, $value, $expiration = 0);
 
 		/**
 		 * Stores value in cache (only if it's not there already)
 		 *
 		 * @param string $name
 		 * @param mixed $value
 		 * @param int $expiration
 		 * @return bool
 		 * @access public
 		 */
 		abstract public function add($name, $value, $expiration = 0);
 
 		/**
 		 * Deletes key from cach
 		 *
 		 * @param string $name
 		 * @return bool
 		 * @access public
 		 */
 		abstract public function delete($name);
 
 		/**
 		 * Determines, that cache storage is working fine
 		 *
 		 * @return bool
 		 * @access public
 		 */
 		public function isWorking()
 		{
 			return $this->_enabled;
 		}
 
 		/**
 		 * Returns caching type of current storage engine
 		 *
 		 * @return int
 		 * @access public
 		 */
 		public function getCachingType()
 		{
 			return $this->cachingType;
 		}
 	}
 
 
 	class FakeCacheHandler extends kCacheHandler {
 
 		public function __construct(kCache $parent)
 		{
 			parent::__construct($parent);
 
 			$this->_enabled = true;
 			$this->cachingType = CACHING_TYPE_TEMPORARY;
 		}
 
 		/**
 		 * Retrieves value from cache
 		 *
 		 * @param string|Array $names
 		 * @return mixed
 		 * @access public
 		 */
 		public function get($names)
 		{
 			if ( is_array($names) ) {
 				$res = Array ();
 
 				foreach ($names as $name) {
 					$res[$name] = $this->parent->getFromLocalStorage($name);
 				}
 
 				return $res;
 			}
 
 			return $this->parent->getFromLocalStorage($names);
 		}
 
 		/**
 		 * Stores value in cache
 		 *
 		 * @param string $name
 		 * @param mixed $value
 		 * @param int $expiration
 		 * @return bool
 		 * @access public
 		 */
 		public function set($name, $value, $expiration = 0)
 		{
 			return true;
 		}
 
 		/**
 		 * Stores value in cache (only if it's not there already)
 		 *
 		 * @param string $name
 		 * @param mixed $value
 		 * @param int $expiration
 		 * @return bool
 		 * @access public
 		 */
 		public function add($name, $value, $expiration = 0)
 		{
 			return true;
 		}
 
 		/**
 		 * Deletes key from cach
 		 *
 		 * @param string $name
 		 * @return bool
 		 * @access public
 		 */
 		public function delete($name)
 		{
 			return true;
 		}
 	}
 
 
 	class MemcacheCacheHandler extends kCacheHandler {
 
 		/**
 		 * Memcache connection
 		 *
 		 * @var Memcache
 		 * @access protected
 		 */
 		protected $_handler = null;
 
 		public function __construct(kCache $parent, $default_servers = '')
 		{
 			parent::__construct($parent);
 
 			$this->cachingType = CACHING_TYPE_MEMORY;
 
 			$vars = kUtil::getConfigVars();
 			$memcached_servers = isset($vars['MemcacheServers']) ? $vars['MemcacheServers'] : $default_servers;
 
 			if ( $memcached_servers && class_exists('Memcache') ) {
 				$this->_enabled = true;
 				$this->_handler = new Memcache();
 				$servers = explode(';', $memcached_servers);
 
 				foreach ($servers as $server) {
 					if ( preg_match('/(.*):([\d]+)$/', $server, $regs) ) {
 						// "hostname:port" OR "unix:///path/to/socket:0"
 						$server = $regs[1];
 						$port = $regs[2];
 					}
 					else {
 						$port = 11211;
 					}
 
 					$this->_handler->addServer($server, $port);
 				}
 
 				// verify, that memcache server is working
 				if ( !$this->_handler->set('test', 1) ) {
 					$this->_enabled = false;
 				}
 			}
 		}
 
 		/**
 		 * Retrieves value from cache
 		 *
 		 * @param string $name
 		 * @return mixed
 		 * @access public
 		 */
 		public function get($name)
 		{
 			return $this->_handler->get($name);
 		}
 
 		/**
 		 * Stores value in cache
 		 *
 		 * @param string $name
 		 * @param mixed $value
 		 * @param int $expiration
 		 * @return bool
 		 * @access public
 		 */
 		public function set($name, $value, $expiration = 0)
 		{
 			// 0 - don't use compression
 			return $this->_handler->set($name, $value, 0, $expiration);
 		}
 
 		/**
 		 * Stores value in cache (only if it's not there already)
 		 *
 		 * @param string $name
 		 * @param mixed $value
 		 * @param int $expiration
 		 * @return bool
 		 * @access public
 		 */
 		public function add($name, $value, $expiration = 0)
 		{
 			// 0 - don't use compression
 			return $this->_handler->add($name, $value, 0, $expiration);
 		}
 
 		/**
 		 * Deletes key from cache
 		 *
 		 * @param string $name
 		 * @return bool
 		 * @access public
 		 */
 		public function delete($name)
 		{
 			return $this->_handler->delete($name, 0);
 		}
 	}
 
 
 	class ApcCacheHandler extends kCacheHandler {
 
 		public function __construct(kCache $parent)
 		{
 			parent::__construct($parent);
 
 			$this->cachingType = CACHING_TYPE_MEMORY;
 			$this->_enabled = function_exists('apc_fetch');
 
 			// verify, that apc is working
 			if ( $this->_enabled && !$this->set('test', 1) ) {
 				$this->_enabled = false;
 			}
 		}
 
 		/**
 		 * Retrieves value from cache
 		 *
 		 * @param string $name
 		 * @return mixed
 		 * @access public
 		 */
 		public function get($name)
 		{
 			return apc_fetch($name);
 		}
 
 		/**
 		 * Stores value in cache
 		 *
 		 * @param string $name
 		 * @param mixed $value
 		 * @param int $expiration
 		 * @return bool
 		 * @access public
 		 */
 		public function set($name, $value, $expiration = 0)
 		{
 			return apc_store($name, $value, $expiration);
 		}
 
 		/**
 		 * Stores value in cache (only if it's not there already)
 		 *
 		 * @param string $name
 		 * @param mixed $value
 		 * @param int $expiration
 		 * @return bool
 		 * @access public
 		 */
 		public function add($name, $value, $expiration = 0)
 		{
 			return apc_add($name, $value, $expiration);
 		}
 
 		/**
 		 * Deletes key from cache
 		 *
 		 * @param string $name
 		 * @return bool
 		 * @access public
 		 */
 		public function delete($name)
 		{
 			return apc_delete($name);
 		}
 	}
 
 
 	class XCacheCacheHandler extends kCacheHandler {
 
 		public function __construct(kCache $parent)
 		{
 			parent::__construct($parent);
 
 			$this->cachingType = CACHING_TYPE_MEMORY;
 			$this->_enabled = function_exists('xcache_get');
 
 			// verify, that xcache is working
 			if ( $this->_enabled && !$this->set('test', 1) ) {
 				$this->_enabled = false;
 			}
 		}
 
 		/**
 		 * Retrieves value from cache
 		 *
 		 * @param string|Array $names
 		 * @return mixed
 		 * @access public
 		 */
 		public function get($names)
 		{
 			if ( is_array($names) ) {
 				$res = Array ();
 
 				foreach ($names as $name) {
 					$res[$name] = $this->get($name);
 				}
 
 				return $res;
 			}
 
 			return xcache_isset($names) ? xcache_get($names) : false;
 		}
 
 		/**
 		 * Stores value in cache
 		 *
 		 * @param string $name
 		 * @param mixed $value
 		 * @param int $expiration
 		 * @return bool
 		 * @access public
 		 */
 		public function set($name, $value, $expiration = 0)
 		{
 			return xcache_set($name, $value, $expiration);
 		}
 
 		/**
 		 * Stores value in cache (only if it's not there already)
 		 *
 		 * @param string $name
 		 * @param mixed $value
 		 * @param int $expiration
 		 * @return bool
 		 * @access public
 		 */
 		public function add($name, $value, $expiration = 0)
 		{
 			// not atomic operation, like in Memcached and may fail
 			if ( xcache_isset($name) ) {
 				return false;
 			}
 
 			return $this->set($name, $value, $expiration);
 		}
 
 		/**
 		 * Deletes key from cache
 		 *
 		 * @param string $name
 		 * @return bool
 		 * @access public
 		 */
 		public function delete($name)
 		{
 			return xcache_unset($name);
 		}
 	}
\ No newline at end of file