PHP 缓存实现代码及详细注释


Posted in PHP onMay 16, 2010
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 { 
protected $_cachesDir = 'cache'; 
public function __construct() { 
if (defined('DATA_DIR')) { 
$this->_setCacheDir(DATA_DIR . '/cache'); 
} 
} 
/** 
* 获取缓存文件 
* 
* @param string $key 
* @return string 
*/ 
protected function _getCacheFile($key) { 
return $this->_cachesDir . '/' . substr($key, 0, 2) . '/' . $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)) { 
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" target="_blank">!@mkdir($cacheDir, 0755, true)) { 
throw new CacheException("Could not make cache directory"); 
} 
} 
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($cacheFile" target="_blank">!@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" target="_blank">!@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; 
} 
}
PHP 相关文章推荐
一个简单的自动发送邮件系统(三)
Oct 09 PHP
php中突破基于HTTP_REFERER的防盗链措施(stream_context_create)
Mar 29 PHP
php增删改查示例自己写的demo
Sep 04 PHP
一个显示效果非常不错的PHP错误、异常处理类
Mar 21 PHP
CodeIgniter采用config控制的多语言实现根据浏览器语言自动转换功能
Jul 18 PHP
php递归调用删除数组空值元素的方法
Apr 28 PHP
PHP IDE PHPStorm配置支持友好Laravel代码提示方法
May 12 PHP
PHP中使用foreach()遍历二维数组的简单实例
Jun 13 PHP
Windows2003下php5.4安装配置教程(IIS)
Jun 30 PHP
php实现微信公众平台发红包功能
Jun 14 PHP
PHP 中 var_export、print_r、var_dump 调试中的区别
Jun 19 PHP
laravel5.6 框架操作数据 Eloquent ORM用法示例
Jan 26 PHP
PHP 防恶意刷新实现代码
May 16 #PHP
PHP 全角转半角实现代码
May 16 #PHP
php5.3 废弃函数小结
May 16 #PHP
memcached 和 mysql 主从环境下php开发代码详解
May 16 #PHP
php 中文和编码判断代码
May 16 #PHP
PHP URL地址获取函数代码(端口等) 推荐
May 15 #PHP
php select,radio和checkbox默认选择的实现方法
May 15 #PHP
You might like
php运行出现Call to undefined function curl_init()的解决方法
2010/11/02 PHP
PHP中子类重载父类的方法【parent::方法名】
2016/05/06 PHP
PHP实现自动识别原编码并对字符串进行编码转换的方法
2016/07/13 PHP
Paypal实现循环扣款(订阅)功能
2017/03/23 PHP
用js统计用户下载网页所需时间的脚本
2008/10/15 Javascript
jQuery控制图片的hover效果(smartRollover.js)
2012/03/18 Javascript
Jquery post传递数组方法实现思路及代码
2013/04/28 Javascript
AngularJS入门教程之Hello World!
2014/12/06 Javascript
node.js中的fs.chownSync方法使用说明
2014/12/16 Javascript
在IE8上JS实现combobox支持拼音检索功能
2016/05/23 Javascript
ES5学习教程之Array对象
2017/04/01 Javascript
JavaScript学习总结之正则的元字符和一些简单的应用
2017/06/30 Javascript
详解升级react-router 4 踩坑指南
2017/08/14 Javascript
mpvue中配置vuex并持久化到本地Storage图文教程解析
2018/03/15 Javascript
详解vue中使用protobuf踩坑记
2019/05/07 Javascript
微信公众号平台接口开发 获取access_token过程解析
2019/08/14 Javascript
js prototype和__proto__的关系是什么
2019/08/23 Javascript
微信小程序HTTP请求从0到1封装
2019/09/09 Javascript
微信小程序scroll-view的滚动条设置实现
2020/03/02 Javascript
深入浅析JavaScript中的in关键字和for-in循环
2020/04/20 Javascript
原生JS运动实现轮播图
2021/01/02 Javascript
[54:27]TNC vs Serenity 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
python3中set(集合)的语法总结分享
2017/03/24 Python
matplotlib设置legend图例代码示例
2017/12/19 Python
PyQt5每天必学之关闭窗口
2018/04/19 Python
Python正则表达式和元字符详解
2018/11/29 Python
python实现猜拳小游戏
2020/04/05 Python
解决Keyerror ''acc'' KeyError: ''val_acc''问题
2020/06/18 Python
Python之字典对象的几种创建方法
2020/09/30 Python
巴西男士个人护理产品商店:SHOP4MEN
2017/08/07 全球购物
师范生自荐信
2013/10/27 职场文书
化工操作工岗位职责
2014/04/29 职场文书
学校端午节活动总结
2015/02/11 职场文书
学习杨善洲同志先进事迹心得体会
2016/01/23 职场文书
Nginx使用X-Accel-Redirect实现静态文件下载的统计、鉴权、防盗链、限速等
2021/04/04 Servers
MySQL之DML语言
2021/04/05 MySQL