Location: PHPKode > projects > Recess PHP Framework > recess/recess/cache/Cache.class.php
<?php
/**
 * Cache is a low level service offering volatile key-value pair
 * storage. Chain-of-command allows multiple providers to be used.
 * 
 * Placing all cache providers in this Cache file to avoid costs
 * of including individual providers. For the perf concerned be 
 * encouraged to remove unused cache providers from this file.
 * 
 * @author Kris Jordan <hide@address.com>
 * @package Recess PHP Framework
 * @license MIT
 * @link http://www.recessframework.org/
 **/
abstract class Cache {
	protected static $reportsTo;
	
	protected static $inMemory = array();

	/**
	 * Push a cache provider onto the providers stack. Most expensive providers
	 * should be pushed first.
	 *
	 * @param ICacheProvider $cache
	 */
	static function reportsTo(ICacheProvider $cache) {
		if(!$cache instanceof ICacheProvider) {
			$cache = new NoOpCacheProvider();
		}
		
		if(isset(self::$reportsTo)) {
			$temp = self::$reportsTo;
			self::$reportsTo = $cache;
			self::$reportsTo->reportsTo($temp);
		} else {
			self::$reportsTo = $cache;
		}
	}
	
	/**
	 * Set a cache value.
	 *
	 * @param string $key
	 * @param mixed $value
	 * @param int $duration
	 */
	static function set($key, $value, $duration = 0) {
		self::$inMemory[$key] = $value;
		self::$reportsTo->set($key, $value, $duration);
	}
	
	/**
	 * Fetch a value from cache.
	 *
	 * @param string $key
	 * @return mixed
	 */
	static function get($key) {
		if(isset(self::$inMemory[$key])) {
			return self::$inMemory[$key];
		} else {
			return self::$reportsTo->get($key);
		}
	}
	
	/**
	 * Remove a key value pair from cache.
	 *
	 * @param string $key
	 */
	static function delete($key) {
		unset(self::$inMemory[$key]);
		self::$reportsTo->delete($key);
	}
	
	/**
	 * Clear all values from the cache.
	 */
	static function clear() {
		self::$inMemory = array();
		self::$reportsTo->clear();
	}
}

/**
 * Common interface for caching subsystems.
 * @author Kris Jordan <hide@address.com>
 */
interface ICacheProvider {
	/**
	 * Enter description here...
	 *
	 * @param string $key
	 * @param mixed $value
	 * @param unknown_type $duration
	 */
	function set($key, $value, $duration = 0);
	function get($key);
	function delete($key);
	function clear();
}

class NoOpCacheProvider implements ICacheProvider {
	function set($key, $value, $duration = 0) {}
	function get($key) { return false; }
	function delete($key) {}
	function clear() {}
}

Cache::reportsTo(new NoOpCacheProvider());

/**
 * Alternative PHP Cache provider. 
 * @see http://us3.php.net/apc
 */
class ApcCacheProvider implements ICacheProvider {
	protected $reportsTo;

	function reportsTo(ICacheProvider $cache) {
		if(!$cache instanceof ICacheProvider) {
			$cache = new NoOpCacheProvider();
		}
		
		if(isset($this->reportsTo)) {
			$temp = $this->reportsTo;
			$this->reportsTo = $cache;
			$this->reportsTo->reportsTo($temp);
		} else {
			$this->reportsTo = $cache;
		}
	}
	
	function set($key, $value, $duration = 0) {
		apc_store($key, $value, $duration);
		$this->reportsTo->set($key, $value, $duration);
	}
	
	function get($key) {
		$result = apc_fetch($key);
		if($result === false) {
			$result = $this->reportsTo->get($key);
			if($result !== false) {
				$this->set($key, $result);	
			}
		}
		return $result;
	}
	
	function delete($key) {
		apc_delete($key);
		$this->reportsTo->delete($key);
	}
	
	function clear() {
		apc_clear_cache('user');
		$this->reportsTo->clear();
	}
}

/**
 * Memcache Provider
 * @see http://us2.php.net/memcache
 */
class MemcacheCacheProvider implements ICacheProvider {
	protected $reportsTo;
	protected $memcache;
	
	function __construct($host = 'localhost', $port = 11211) {
		$this->memcache = new Memcache;
		$this->memcache->pconnect($host, $port);
	}

	function reportsTo(ICacheProvider $cache) {
		if(!$cache instanceof ICacheProvider) {
			$cache = new NoOpCacheProvider();
		}
		
		if(isset($this->reportsTo)) {
			$temp = $this->reportsTo;
			$this->reportsTo = $cache;
			$this->reportsTo->reportsTo($temp);
		} else {
			$this->reportsTo = $cache;
		}
	}
	
	function set($key, $value, $duration = 0) {
		$this->memcache->set($key, $value, null, $duration);
		$this->reportsTo->set($key, $value, $duration);
	}
	
	function get($key) {
		$result = $this->memcache->get($key);
		if($result === false) {
			$result = $this->reportsTo->get($key);
			if($result !== false) {
				$this->set($key, $result);	
			}
		}
		return $result;
	}
	
	function delete($key) {
		$this->memcache->delete($key);
		$this->reportsTo->delete($key);
	}
	
	function clear() {
		$this->memcache->flush();
		$this->reportsTo->clear();
	}
}

/**
 * Provider implemented with Sqlite backend. Less preferable than 
 * APC/Memcache but works well for shared hosts.
 */
class SqliteCacheProvider implements ICacheProvider {
	protected $reportsTo;
	protected $pdo;
	protected $setStatement;
	protected $getStatement;
	protected $getManyStatement;
	protected $deleteStatement;
	protected $time;
	
	protected $entries = array();

	const VALUE = 0;
	const EXPIRE = 1;
	const KEY = 2;
	
	function __construct() {
		$this->pdo = new Pdo('sqlite:' . $_ENV['dir.temp'] . 'sqlite-cache.db');
		$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
		
		$tries = 0;
		while($tries < 2) {
			try {
				$tries++;
				$this->setStatement = $this->pdo->prepare('INSERT OR REPLACE INTO cache (key,value,expire) values (:key,:value,:expire)');
				$this->getStatement = $this->pdo->prepare('SELECT value,expire FROM cache WHERE key = :key');
				$this->getManyStatement = $this->pdo->prepare('SELECT value,expire,key FROM cache WHERE key LIKE :key');
				break;
			} catch(PDOException $e) {
			
				try {
					$this->pdo->exec('CREATE TABLE "cache" ("key" TEXT PRIMARY KEY  NOT NULL , "value" TEXT NOT NULL , "expire" INTEGER NOT NULL)');
					$this->pdo->exec('CREATE INDEX "expiration" ON "cache" ("expire" ASC)');
				} catch(PDOException $e) {
					if($tries == 2) {
						die('Could not create cache table.');
					}
					sleep(1);
					continue;
				}
			}
		}
		
		$this->time = time();
	}
	
	function reportsTo(ICacheProvider $cache) {
		if(!$cache instanceof ICacheProvider) {
			$cache = new NoOpCacheProvider();
		}
		
		if(isset($this->reportsTo)) {
			$temp = $this->reportsTo;
			$this->reportsTo = $cache;
			$this->reportsTo->reportsTo($temp);
		} else {
			$this->reportsTo = $cache;
		}
	}
	
	function set($key, $value, $duration = 0) {
		$this->setStatement->execute(array(':key' => $key, ':value' => serialize($value), ':expire' => $duration == 0 ? 0 : time() + $duration));
		$this->reportsTo->set($key, $value, $duration);
		$this->entries[$key] = $value;
	}
	
	function clearStaleEntries() {
		$this->pdo->exec('DELETE FROM cache WHERE expire != 0 AND expire < ' . $this->time);
	}
	
	function get($key) {
		if(isset($this->entries[$key])) {
			return $this->entries[$key];
		}

		if(($starPos = strpos($key,'*')) === false) {
			// Fetch Single
			$this->getStatement->execute(array(':key' => $key));
			$entries = $this->getStatement->fetchAll(PDO::FETCH_NUM);
		} else {
			// Prefetch With Wildcard
			$this->getManyStatement->execute(array(':key' => substr($key,0,$starPos+1) . '%'));
			$entries = $this->getManyStatement->fetchAll(PDO::FETCH_NUM);
		}
		
		$clearStaleEntries = false;
		foreach($entries as $entry) {
			if($entry[self::EXPIRE] == 0 || $entry[self::EXPIRE] <= $this->time) {
				if(isset($entry[self::KEY])) {
					$this->entries[$entry[self::KEY]] = unserialize($entry[self::VALUE]);
				} else {
					$this->entries[$key] = unserialize($entry[self::VALUE]);
				}
			} else {
				$clearStaleEntries = true;
			}
		}
		
		if($clearStaleEntries) {
			$this->clearStaleEntries();
		}
		
		if(isset($this->entries[$key])) {
			return $this->entries[$key];
		} else{
			return $this->reportsTo->get($key);
		}
	}
	
	function delete($key) {
		if($this->deleteStatement == null) {
			$this->deleteStatement = $this->pdo->prepare('DELETE FROM cache WHERE key = :key OR (expire != 0 AND expire < ' . $this->time . ')');
		}
		$this->deleteStatement->execute(array(':key' => $key));
		$this->reportsTo->delete($key);
	}
	
	function clear() {
		$this->pdo->exec('DELETE FROM cache');
		$this->reportsTo->clear();
	}
}
?>
Return current item: Recess PHP Framework