Location: PHPKode > projects > Nette Framework > NetteFramework-2.0beta-PHP5.2/Nette/Caching/Cache.php
<?php

/**
 * This file is part of the Nette Framework (http://nette.org)
 *
 * Copyright (c) 2004, 2011 David Grudl (http://davidgrudl.com)
 *
 * For the full copyright and license information, please view
 * the file license.txt that was distributed with this source code.
 * @package Nette\Caching
 */







/**
 * Implements the cache for a application.
 *
 * @author     David Grudl
 */
class NCache extends NObject implements ArrayAccess
{
	/** dependency */
	const PRIORITY = 'priority',
		EXPIRATION = 'expire',
		EXPIRE = 'expire',
		SLIDING = 'sliding',
		TAGS = 'tags',
		FILES = 'files',
		ITEMS = 'items',
		CONSTS = 'consts',
		CALLBACKS = 'callbacks',
		ALL = 'all';

	/** @internal */
	const NAMESPACE_SEPARATOR = "\x00";

	/** @var ICacheStorage */
	private $storage;

	/** @var string */
	private $namespace;

	/** @var string  last query cache */
	private $key;

	/** @var mixed  last query cache */
	private $data;



	public function __construct(ICacheStorage $storage, $namespace = NULL)
	{
		$this->storage = $storage;
		$this->namespace = $namespace . self::NAMESPACE_SEPARATOR;
	}



	/**
	 * Returns cache storage.
	 * @return ICacheStorage
	 */
	public function getStorage()
	{
		return $this->storage;
	}



	/**
	 * Returns cache namespace.
	 * @return string
	 */
	public function getNamespace()
	{
		return (string) substr($this->namespace, 0, -1);
	}



	/**
	 * Returns new nested cache object.
	 * @param  string
	 * @return NCache
	 */
	public function derive($namespace)
	{
		$derived = new self($this->storage, $this->namespace . $namespace);
		return $derived;
	}



	/**
	 * Discards the internal cache.
	 * @return void
	 */
	public function release()
	{
		$this->key = $this->data = NULL;
	}



	/**
	 * Retrieves the specified item from the cache or returns NULL if the key is not found.
	 * @param  mixed key
	 * @return mixed|NULL
	 */
	public function load($key)
	{
		$key = is_scalar($key) ? (string) $key : serialize($key);
		if ($this->key === $key) {
			return $this->data;
		}
		$this->key = $key;
		$this->data = $this->storage->read($this->namespace . md5($key));
		return $this->data;
	}



	/**
	 * Writes item into the cache.
	 * Dependencies are:
	 * - NCache::PRIORITY => (int) priority
	 * - NCache::EXPIRATION => (timestamp) expiration
	 * - NCache::SLIDING => (bool) use sliding expiration?
	 * - NCache::TAGS => (array) tags
	 * - NCache::FILES => (array|string) file names
	 * - NCache::ITEMS => (array|string) cache items
	 * - NCache::CONSTS => (array|string) cache items
	 *
	 * @param  mixed  key
	 * @param  mixed  value
	 * @param  array  dependencies
	 * @return mixed  value itself
	 * @throws InvalidArgumentException
	 */
	public function save($key, $data, array $dp = NULL)
	{
		$this->key = is_scalar($key) ? (string) $key : serialize($key);
		$key = $this->namespace . md5($this->key);

		// convert expire into relative amount of seconds
		if (isset($dp[NCache::EXPIRATION])) {
			$dp[NCache::EXPIRATION] = NDateTime53::from($dp[NCache::EXPIRATION])->format('U') - time();
		}

		// convert FILES into CALLBACKS
		if (isset($dp[self::FILES])) {
			//clearstatcache();
			foreach ((array) $dp[self::FILES] as $item) {
				$dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkFile'), $item, @filemtime($item)); // @ - stat may fail
			}
			unset($dp[self::FILES]);
		}

		// add namespaces to items
		if (isset($dp[self::ITEMS])) {
			$dp[self::ITEMS] = (array) $dp[self::ITEMS];
			foreach ($dp[self::ITEMS] as $k => $item) {
				$dp[self::ITEMS][$k] = $this->namespace . md5(is_scalar($item) ? $item : serialize($item));
			}
		}

		// convert CONSTS into CALLBACKS
		if (isset($dp[self::CONSTS])) {
			foreach ((array) $dp[self::CONSTS] as $item) {
				$dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkConst'), $item, constant($item));
			}
			unset($dp[self::CONSTS]);
		}

		if ($data instanceof NCallback || $data instanceof Closure) {
			NCriticalSection::enter();
			$data = $data->__invoke();
			NCriticalSection::leave();
		}

		if (is_object($data)) {
			$dp[self::CALLBACKS][] = array(array(__CLASS__, 'checkSerializationVersion'), get_class($data),
				NClassReflection::from($data)->getAnnotation('serializationVersion'));
		}

		$this->data = $data;
		if ($data === NULL) {
			$this->storage->remove($key);
		} else {
			$this->storage->write($key, $data, (array) $dp);
		}
		return $data;
	}



	/**
	 * Removes items from the cache by conditions.
	 * Conditions are:
	 * - NCache::PRIORITY => (int) priority
	 * - NCache::TAGS => (array) tags
	 * - NCache::ALL => TRUE
	 *
	 * @param  array
	 * @return void
	 */
	public function clean(array $conds = NULL)
	{
		$this->release();
		$this->storage->clean((array) $conds);
	}



	/**
	 * Caches results of function/method calls.
	 * @param  mixed
	 * @return mixed
	 */
	public function call($function)
	{
		$key = func_get_args();
		if ($this->load($key) === NULL) {
			array_shift($key);
			return $this->save($this->key, call_user_func_array($function, $key));
		} else {
			return $this->data;
		}
	}



	/**
	 * Starts the output cache.
	 * @param  mixed  key
	 * @return NCachingHelper|NULL
	 */
	public function start($key)
	{
		if ($this->offsetGet($key) === NULL) {
			return new NCachingHelper($this, $key);
		} else {
			echo $this->data;
		}
	}



	/********************* interface ArrayAccess ****************d*g**/



	/**
	 * Inserts (replaces) item into the cache (ArrayAccess implementation).
	 * @param  mixed key
	 * @param  mixed
	 * @return void
	 * @throws InvalidArgumentException
	 */
	public function offsetSet($key, $data)
	{
		$this->save($key, $data);
	}



	/**
	 * Retrieves the specified item from the cache or NULL if the key is not found (ArrayAccess implementation).
	 * @param  mixed key
	 * @return mixed|NULL
	 * @throws InvalidArgumentException
	 */
	public function offsetGet($key)
	{
		return $this->load($key);
	}



	/**
	 * Exists item in cache? (ArrayAccess implementation).
	 * @param  mixed key
	 * @return bool
	 * @throws InvalidArgumentException
	 */
	public function offsetExists($key)
	{
		return $this->load($key) !== NULL;
	}



	/**
	 * Removes the specified item from the cache.
	 * @param  mixed key
	 * @return void
	 * @throws InvalidArgumentException
	 */
	public function offsetUnset($key)
	{
		$this->save($key, NULL);
	}



	/********************* dependency checkers ****************d*g**/



	/**
	 * Checks CALLBACKS dependencies.
	 * @param  array
	 * @return bool
	 */
	public static function checkCallbacks($callbacks)
	{
		foreach ($callbacks as $callback) {
			$func = array_shift($callback);
			if (!call_user_func_array($func, $callback)) {
				return FALSE;
			}
		}
		return TRUE;
	}



	/**
	 * Checks CONSTS dependency.
	 * @param  string
	 * @param  mixed
	 * @return bool
	 */
	private static function checkConst($const, $value)
	{
		return defined($const) && constant($const) === $value;
	}



	/**
	 * Checks FILES dependency.
	 * @param  string
	 * @param  int
	 * @return bool
	 */
	private static function checkFile($file, $time)
	{
		return @filemtime($file) == $time; // @ - stat may fail
	}



	/**
	 * Checks object @serializationVersion label.
	 * @param  string
	 * @param  mixed
	 * @return bool
	 */
	private static function checkSerializationVersion($class, $value)
	{
		return NClassReflection::from($class)->getAnnotation('serializationVersion') === $value;
	}

}
Return current item: Nette Framework