Show:

File: platform/classes/Q/Cache.php

<?php

/**
 * @module Q
 */
/**
 * Used to maintain arbitrary data in persistent cache storage
 * @class Q_Cache
 */
class Q_Cache
{
	/**
	 * @method init
	 * @static
	 */
	static function init() {
		Q_Cache::$namespace = "Q_Cache\t".(defined('APP_DIR') ? APP_DIR : '');
		self::$apc = extension_loaded('apc');
		self::$apcu = extension_loaded('apcu');
	}
	
	/**
	 * Check if Q_Cache is connected to some PHP cache engine (currently APC)
	 * @method connected
	 * @static
	 * @return {boolean} Whether cache is currently connected
	 */
	static function connected() {
		return self::$apc or self::$apcu;
	}
	
	/**
	 * Can be used to ignore the cache for a while, to re-populate it
	 * @method ignore
	 * @static
	 * @param {boolean} $setting Whether to start or to stop ignoring the cache during Q_Cache::get
	 * @return {boolean} Returns the old setting
	 */
	static function ignore($setting)
	{
		$old_setting = self::$ignore;
		self::$ignore = $setting;
		return $old_setting;
	}

	/**
	 * Set Q_Cache entry
	 * @method set
	 * @static
	 * @param {string} $key The key of cache entry
	 * @param {mixed} $value The value to set in cache
	 * @param {string} [$namespace=self::$namespace] The namespace to use
	 * @return {boolean} Whether cache was fetched (if not, it will attempt to be saved at script shutdown)
	 */
	static function set($key, $value, $namespace = null) {
		if (!isset($namespace)) {
			$namespace = self::$namespace;
		}
		$store = self::fetchStore($namespace, $fetched);
		$store[$key] = $value;
		self::$changed[$namespace] = true; // it will be saved at shutdown
		return $fetched;
	}

	/**
	 * Check if a Q_Cache entry exists
	 * @method exists
	 * @static
	 * @param {string} $key The key of cache entry
	 * @param {string} [$namespace=self::$namespace] The namespace to use
	 * @return {boolean} Whether it exists
	 */
	static function exists($key, $namespace = null)
	{
		if (!isset($namespace)) {
			$namespace = self::$namespace;
		}
		if (self::$ignore) {
			return false;
		}
		$store = self::fetchStore($namespace);
		return array_key_exists($key, $store);
	}

	/**
	 * Get Q_Cache entry
	 * @method get
	 * @static
	 * @param {string} $key The key of cache entry
	 * @param {mixed} [$default=null] In case the entry isn't there
	 * @param {string} [$namespace=self::$namespace] The namespace to use
	 * @return {mixed} The value of Q_Cache entry, or null on failure
	 */
	static function get($key, $default = null, $namespace = null)
	{
		if (!isset($namespace)) {
			$namespace = self::$namespace;
		}
		$success = false;
		if (self::$ignore) {
			return $default;
		}
		$store = self::fetchStore($namespace);
		if (!array_key_exists($key, $store)) {
			return $default; // no such $key is stored in cache
		}
		return $store[$key];
	}

	/**
	 * Clear Q_Cache entry
	 * @method clear
	 * @static
	 * @param {string|true} $key The key of cache entry. Skip this to clear all the keys.
	 *   Pass true to also clear the user files cache.
	 * @param {boolean} [$prefix=false] Whether to clear all keys for which $key is a prefix
	 * @param {string} [$namespace=self::$namespace] The namespace to use
	 * @return {boolean} Whether an apc cache was fetched.
	 */
	static function clear($key, $prefix = false, $namespace = null)
	{
		if (!isset($namespace)) {
			$namespace = self::$namespace;
		}
		$store = self::fetchStore($namespace, $fetched);
		if (!isset($key) or $key === true) {
			$store = array();
			self::$changed[$namespace] = true; // it will be saved at shutdown
			if ($key === true) {
				if (is_callable('apcu_clear_cache')) {
					apcu_clear_cache();
				} else if (is_callable('apc_clear_cache')) {
					apc_clear_cache('user');
				}
			}
			return $store;
		}
		if (array_key_exists($key, $store)) {
			if ($prefix) {
				$len = strlen($key);
				foreach ($store as $k => $v) {
					if (substr($k, 0, $len) === $key) {
						unset($store[$namespace][$k]);
					}
				}
			} else {
				unset($store[$key]);
			}
			self::$changed[$namespace] = true; // it will be saved at shutdown
		}
		return $fetched;
	}

	/**
	 * Fetches the cache store from APC.
	 * In either case, prepares self::$store[$namespace] to be used as an array.
	 * @method fetchStore
	 * @protected
	 * @static
	 * @param {string} [$namespace=self::$namespace] The namespace to use
	 * @param {boolean} [$fetched] If passed, this is filled with whether the store was fetched
	 * @return {array} A reference to the cache store, or to an empty array if nothing was fetched
	 */
	protected static function &fetchStore($namespace = null, &$fetched = null)
	{
		if (!isset($namespace)) {
			$namespace = self::$namespace;
		}
		$namespace = "Q_Cache\t".$namespace;
		if (!self::$apc and !self::$apcu) {
			$fetched = false;
			self::$store[$namespace] = array();
		} else {
			if (is_callable('apcu_fetch')) {
				$store = apcu_fetch($namespace, $fetched);
			} else {
				$store = apc_fetch($namespace, $fetched);
			}
            self::$store[$namespace] = $fetched ? $store : array();
        }
		return self::$store[$namespace];
	}

	/**
	 * Set the duration for a given cache store. The default is in Q/cache/duration config.
	 * @method setDuration
	 * @protected
	 * @static
	 * @param {integer} $duration The number of seconds to store until the cache under this namespace expires
	 * @param {string} [$namespace=self::$namespace] The namespace to use
	 */
	public static function setDuration($duration, $namespace = null)
	{
		if (!isset($namespace)) {
			$namespace = self::$namespace;
		}
		self::$durations[$namespace] = $duration;
	}
	
	/**
	 * @method shutdownFunction
	 * @static
	 */
	static function shutdownFunction()
	{
		if (!self::$apc and !self::$apcu) {
			return;
		}
		foreach (self::$changed as $namespace => $changed) {
			$namespace = "Q_Cache\t".$namespace;
			self::set("Q_Cache\tupdateTime", time());
			$duration = Q_Config::get('Q', 'cache', 'duration', 0);
			$duration = Q::ifset(self::$durations, $namespace, $duration);
			if (is_callable('apcu_store')) {
				apcu_store($namespace, self::$store[$namespace], $duration);
			} else if (is_callable('apc_store')) {
				apc_store($namespace, self::$store[$namespace], $duration);
			}
		}
	}

	/**
	 * @property $ignore
	 * @protected
	 * @type boolean
	 */
	protected static $ignore = false;
	/**
	 * @property $store
	 * @protected
	 * @type array
	 */
	protected static $store = null;
	/**
	 * @property $changed
	 * @protected
	 * @type array
	 */
	protected static $changed = false;
	/**
	 * @property $durations
	 * @protected
	 * @type array
	 */
	protected static $durations = false;
	/**
	 * @property $namespace
	 * @protected
	 * @type string
	 */
	protected static $namespace;
	/**
	 * @property $apc
	 * @protected
	 * @type boolean
	 */
	protected static $apc;
	/**
	 * @property $apcu
	 * @protected
	 * @type boolean
	 */
	protected static $apcu;
}

Q_Cache::init();