php支持apc和文件缓存的缓存类


PHP #文件缓存 # #apc2012-10-30 13:35

刚发现一个PHP缓存实现,实现了apc和文件缓存,继承Cache_Abstract即可实现调用第三方的缓存工具。参考shindig的缓存类和apc。

类代码如下:

<?php
class CacheException extends Exception {
}

/**
 * 缓存抽象类
 */
abstract class Cache_Abstract {
	/**
	 * 读缓存变量
	 *
	 * @param string $key 缓存下标
	 * @return mixed
	 */
	abstract public function fetch($key);

	/**
	 * 缓存变量
	 *
	 * @param string $key 缓存变量下标
	 * @param string $value 缓存变量的值
	 * @return bool
	 */
	abstract public function store($key, $value);

	/**
	 * 删除缓存变量
	 *
	 * @param string $key 缓存下标
	 * @return Cache_Abstract
	 */
	abstract public function delete($key);

	/**
	 * 清(删)除所有缓存
	 *
	 * @return Cache_Abstract
	 */
	abstract public function clear();

	/**
	 * 锁定缓存变量
	 *
	 * @param string $key 缓存下标
	 * @return Cache_Abstract
	 */
	abstract public function lock($key);

	/**
	 * 缓存变量解锁
	 *
	 * @param string $key 缓存下标
	 * @return Cache_Abstract
	 */
	abstract public function unlock($key);

	/**
	 * 取得缓存变量是否被锁定
	 *
	 * @param string $key 缓存下标
	 * @return bool
	 */
	abstract public function isLocked($key);

	/**
	 * 确保不是锁定状态
	 * 最多做$tries次睡眠等待解锁,超时则跳过并解锁
	 *
	 * @param string $key 缓存下标
	 */
	public function checkLock($key) {
		if (!$this -> isLocked($key)) {
			return $this;
		}

		$tries = 10;
		$count = 0;
		do {
			usleep(200);
			$count++;
		} while ($count <= $tries && $this->isLocked($key));// 最多做十次睡眠等待解锁,超时则跳过并解锁

		$this -> isLocked($key) && $this -> unlock($key);

		return $this;
	}

}

/**
 * APC扩展缓存实现
 *
 *
 * @category   Mjie
 * @package    Cache
 * @author     流水孟春
 * @copyright  Copyright (c) 2008- <cmpan(at)qq.com>
 * @license    New BSD License
 * @version    $Id: Cache/Apc.php 版本号 2010-04-18 23:02 cmpan $
 */
class Cache_Apc extends Cache_Abstract {

	protected $_prefix = 'cache.mjie.net';

	public function __construct() {
		if (!function_exists('apc_cache_info')) {
			throw new CacheException('apc extension didn't installed');
		}
	}

	/**
	 * 保存缓存变量
	 *
	 * @param string $key
	 * @param mixed $value
	 * @return bool
	 */
	public function store($key, $value) {
		return apc_store($this -> _storageKey($key), $value);
	}

	/**
	 * 读取缓存
	 *
	 * @param string $key
	 * @return mixed
	 */
	public function fetch($key) {
		return apc_fetch($this -> _storageKey($key));
	}

	/**
	 * 清除缓存
	 *
	 * @return Cache_Apc
	 */
	public function clear() {
		apc_clear_cache();
		return $this;
	}

	/**
	 * 删除缓存单元
	 *
	 * @return Cache_Apc
	 */
	public function delete($key) {
		apc_delete($this -> _storageKey($key));
		return $this;
	}

	/**
	 * 缓存单元是否被锁定
	 *
	 * @param string $key
	 * @return bool
	 */
	public function isLocked($key) {
		if ((apc_fetch($this -> _storageKey($key) . '.lock')) === false) {
			return false;
		}

		return true;
	}

	/**
	 * 锁定缓存单元
	 *
	 * @param string $key
	 * @return Cache_Apc
	 */
	public function lock($key) {
		apc_store($this -> _storageKey($key) . '.lock', '', 5);
		return $this;
	}

	/**
	 * 缓存单元解锁
	 *
	 * @param string $key
	 * @return Cache_Apc
	 */
	public function unlock($key) {
		apc_delete($this -> _storageKey($key) . '.lock');
		return $this;
	}

	/**
	 * 完整缓存名
	 *
	 * @param string $key
	 * @return string
	 */
	private function _storageKey($key) {
		return $this -> _prefix . '_' . $key;
	}

}

/**
 * 文件缓存实现
 *
 *
 * @category   Mjie
 * @package    Cache
 * @author     流水孟春
 * @copyright  Copyright (c) 2008- <cmpan(at)qq.com>
 * @license    New BSD License
 * @version    $Id: Cache/File.php 版本号 2010-04-18 16:46 cmpan $
 */
class Cache_File extends Cache_Abstract {
	public $useSubdir = false;

	protected $_cachesDir = 'cache';

	public function __construct() {
		if (defined('DATA_DIR')) {
			$this -> _setCacheDir(DATA_DIR . '/cache');
		}
	}

	/**
	 * 获取缓存文件
	 *
	 * @param string $key
	 * @return string
	 */
	protected function _getCacheFile($key) {
		$subdir = $this -> useSubdir ? substr($key, 0, 2) . '/' : '';
		return $this -> _cachesDir . '/' . $subdir . $key . '.php';
	}

	/**
	 * 读取缓存变量
	 * 为防止信息泄露,缓存文件格式为php文件,并以"<?php exit;?>"开头
	 *
	 * @param string $key 缓存下标
	 * @return mixed
	 */
	public function fetch($key) {
		$cacheFile = self::_getCacheFile($key);
		if (file_exists($cacheFile) && is_readable($cacheFile)) {
			// include 方式
			//return include $cacheFile;
			// 系列化方式

			return unserialize(@file_get_contents($cacheFile, false, NULL, 13));
		}

		return false;
	}

	/**
	 * 缓存变量
	 * 为防止信息泄露,缓存文件格式为php文件,并以"<?php exit;?>"开头
	 *
	 * @param string $key 缓存变量下标
	 * @param string $value 缓存变量的值
	 * @return bool
	 */
	public function store($key, $value) {
		$cacheFile = self::_getCacheFile($key);
		$cacheDir = dirname($cacheFile);

		if (!is_dir($cacheDir)) {
			if (!@mkdir($cacheDir, 0755, true)) {
				throw new CacheException("Could not make cache directory");
			}
		}
		// 用include方式
		//return @file_put_contents($cacheFile, '<?php return ' . var_export($value, true). ';');

		return @file_put_contents($cacheFile, '<?php exit;?>' . serialize($value));
	}

	/**
	 * 删除缓存变量
	 *
	 * @param string $key 缓存下标
	 * @return Cache_File
	 */
	public function delete($key) {
		if (emptyempty($key)) {
			throw new CacheException("Missing argument 1 for Cache_File::delete()");
		}

		$cacheFile = self::_getCacheFile($key);
		if (!@unlink($cacheFile)) {
			throw new CacheException("Cache file could not be deleted");
		}

		return $this;
	}

	/**
	 * 缓存单元是否已经锁定
	 *
	 * @param string $key
	 * @return bool
	 */
	public function isLocked($key) {
		$cacheFile = self::_getCacheFile($key);
		clearstatcache();
		return file_exists($cacheFile . '.lock');
	}

	/**
	 * 锁定
	 *
	 * @param string $key
	 * @return Cache_File
	 */
	public function lock($key) {
		$cacheFile = self::_getCacheFile($key);
		$cacheDir = dirname($cacheFile);
		if (!is_dir($cacheDir)) {
			if (!@mkdir($cacheDir, 0755, true)) {
				if (!is_dir($cacheDir)) {
					throw new CacheException("Could not make cache directory");
				}
			}
		}

		// 设定缓存锁文件的访问和修改时间
		@touch($cacheFile . '.lock');
		return $this;
	}

	/**
	 * 解锁
	 *
	 * @param string $key
	 * @return Cache_File
	 */
	public function unlock($key) {
		$cacheFile = self::_getCacheFile($key);
		@unlink($cacheFile . '.lock');
		return $this;
	}

	/**
	 * 设置文件缓存目录
	 * @param string $dir
	 * @return Cache_File
	 */
	protected function _setCacheDir($dir) {
		$this -> _cachesDir = rtrim(str_replace('\', '/', trim($dir)), '/');
		clearstatcache();
		if (!is_dir($this -> _cachesDir)) {
			mkdir($this -> _cachesDir, 0755, true);
		}
		//
		return $this;
	}

	/**
	 * 清空所有缓存
	 *
	 * @return Cache_File
	 */
	public function clear() {
		// 遍历目录清除缓存
		$cacheDir = $this -> _cachesDir;
		$d = dir($cacheDir);
		while (false !== ($entry = $d -> read())) {
			if ('.' == $entry[0]) {
				continue;
			}

			$cacheEntry = $cacheDir . '/' . $entry;
			if (is_file($cacheEntry)) {
				@unlink($cacheEntry);
			} elseif (is_dir($cacheEntry)) {
				// 缓存文件夹有两级
				$d2 = dir($cacheEntry);
				while (false !== ($entry = $d2 -> read())) {
					if ('.' == $entry[0]) {
						continue;
					}

					$cacheEntry .= '/' . $entry;
					if (is_file($cacheEntry)) {
						@unlink($cacheEntry);
					}
				}
				$d2 -> close();
			}
		}
		$d -> close();

		return $this;
	}

}

/**
 * 缓存单元的数据结构
 * array(
 *         'time' => time(),     // 缓存写入时的时间戳
 *         'expire' => $expire, // 缓存过期时间
 *         'valid' => true,         // 缓存是否有效
 *         'data' => $value         // 缓存的值
 * );
 */
final class Cache {
	/**
	 * 缓存过期时间长度(s)
	 *
	 * @var int
	 */
	private $_expire = 3600;
	/**
	 * 缓存处理类
	 *
	 * @var Cache_Abstract
	 */
	private $_storage = null;
	/**
	 * @return Cache
	 */
	static public function createCache($cacheClass = 'Cache_File') {
		return new self($cacheClass);
	}

	private function __construct($cacheClass) {
		$this -> _storage = new $cacheClass();
	}

	/**
	 * 设置缓存
	 *
	 * @param string $key
	 * @param mixed $value
	 * @param int $expire
	 */
	public function set($key, $value, $expire = false) {
		if (!$expire) {
			$expire = $this -> _expire;
		}

		$this -> _storage -> checkLock($key);

		$data = array('time' => time(), 'expire' => $expire, 'valid' => true, 'data' => $value);
		$this -> _storage -> lock($key);

		try {
			$this -> _storage -> store($key, $data);
			$this -> _storage -> unlock($key);
		} catch (CacheException $e) {
			$this -> _storage -> unlock($key);
			throw $e;
		}
	}

	/**
	 * 读取缓存
	 *
	 * @param string $key
	 * @return mixed
	 */
	public function get($key) {
		$data = $this -> fetch($key);
		if ($data && $data['valid'] && !$data['isExpired']) {
			return $data['data'];
		}

		return false;
	}

	/**
	 * 读缓存,包括过期的和无效的,取得完整的存贮结构
	 *
	 * @param string $key
	 */
	public function fetch($key) {
		$this -> _storage -> checkLock($key);
		$data = $this -> _storage -> fetch($key);
		if ($data) {
			$data['isExpired'] = (time() - $data['time']) > $data['expire'] ? true : false;
			return $data;
		}

		return false;
	}

	/**
	 * 删除缓存
	 *
	 * @param string $key
	 */
	public function delete($key) {
		$this -> _storage -> checkLock($key) -> lock($key) -> delete($key) -> unlock($key);
	}

	public function clear() {
		$this -> _storage -> clear();
	}

	/**
	 * 把缓存设为无效
	 *
	 * @param string $key
	 */
	public function setInvalidate($key) {
		$this -> _storage -> checkLock($key) -> lock($key);
		try {
			$data = $this -> _storage -> fetch($key);
			if ($data) {
				$data['valid'] = false;
				$this -> _storage -> store($key, $data);
			}
			$this -> _storage -> unlock($key);
		} catch (CacheException $e) {
			$this -> _storage -> unlock($key);
			throw $e;
		}
	}

	/**
	 * 设置缓存过期时间(s)
	 *
	 * @param int $expire
	 */
	public function setExpire($expire) {
		$this -> _expire = (int)$expire;
		return $this;
	}

}


相关文章

粤ICP备11097351号-1