最近学习的过程中接触到了PHP的缓存 ,一开始什么APC、Memcache、eAccelerator、xcache、mmcache、zend cache、pear cache还有各种网上的缓存类等等把我都搞糊涂了,经过慢慢整理才有了一个粗浅的认识。
上面列举的那些缓存是不尽相同的,有些是比较底层的编译型缓存,对于这些缓存,更确切的说是给PHP加速,例如APC;有些是页面层次的缓存,缓存一些页面所需的数据或是页面的某个部分(某个函数、某个代码片段之类)或是整个页面,具体的可以缓存到文件、内存或是数据库,例如pear_cache。
或许还有一些其它类型的缓存,但是我目前还没接触到,而且对于上面列举的那些缓存具体是什么类型的缓存,我目前也没有具体的认识,以后还要花功夫学习。
看了网上的一些页面缓存类,自己也写了一个(下面会贴出来),功能很简单,就是缓存整个页面,以后有需要再做扩展。
这里说一下这个缓存类的安全问题,其实网上其它的类似缓存类都有同样的问题,包括smarty的缓存,这个在smarty的手册里有说明(具体在缓存章节的每页多个缓存小节),问题的根源在于cacheid的设定,cacheid一般是根据url的文件名和post或get的参数来生成,这样的话,如果某个不怀好意的人写一些代码来连续快速发送post或get参数,就会让服务器一直生成缓存文件,消耗服务器资源,具体的理解结合以下代码会更容易。暂时好像还没有好的解决方案,一个不太好的方法是在页面里要自己添加代码检查用于设定cacheid的post或get参数值的合法性。
/* *
* 可自由转载,请保留版权信息,谢谢使用!
* Class Name : Junin_Cache (For PHP5)
* Version : 1.0
* Description : 动态缓存类,用于控制页面自动生成缓存、调用缓存、更新缓存、删除缓存.
* Author : junin927@hotmail.com,Junin
* Author Page : http://sdts.blog.hexun.com/ http://blog.csdn.net/sdts/
* Last Modify : 2007-8-23
* Remark :
1.此版本为PHP5版本,本人暂没有写PHP4的版本,如需要请自行参考修改.
2.此版本为utf-8编码,如果网站采用其它编码请自行转换,Windows系统用记事本打开另存为,选择相应编码即可(一般ANSI),linux下请使用相应编辑软件或iconv命令行.
3.拷贝粘贴的就不用管上面第2条了.
4.请在实例化对象后调用SetCacheId()函数,此调用要先于其它缓存操作.
* 关于缓存的一点感想:
* 动态缓存和静态缓存的根本差别在于其是自动的,用户访问页面过程就是生成缓存、浏览缓存、更新缓存的过程,无需人工操作干预.
* 静态缓存指的就是生成静态页面,相关操作一般是在网站后台完成,需人工操作(也就是手动生成).
*/
/* *
* 使用方法举例
* Demo1
<?php
header('content-type:text/html;charset=utf-8');
require_once('Junin_Cache.php');
$cachedir = './Cache/'; //设定缓存目录
$cache = new Junin_Cache($cachedir,10); //省略参数即采用缺省设置, $cache = new Cache($cachedir);
$cache->SetCacheId(); //根据get或post的参数设置cache
if ($_GET['cacheact'] != 'rewrite') //此处为一技巧,通过xx.php?cacheact=rewrite更新缓存,以此类推,还可以设定一些其它操作
$cache->Load(); //装载缓存,缓存有效则不执行以下页面代码
//页面代码开始
echo date('H:i:s jS F');
//页面代码结束
$cache->Write(); //首次运行或缓存过期,生成缓存
?>
*Demo2
<?php
header('content-type:text/html;charset=utf-8');
require_once('Junin_Cache.php');
$cachedir = './Cache/'; //设定缓存目录
$cache = new Junin_Cache($cachedir,10); //省略参数即采用缺省设置, $cache = new Cache($cachedir);
$cache->SetCacheId('classid','articleid'); //根据get或post的参数设置cache,针对类似这样的url:http://xxx/article.php?classid=1&articleid=1001
if ($_GET['cacheact'] != 'rewrite') //此处为一技巧,通过xx.php?cacheact=rewrite更新缓存,以此类推,还可以设定一些其它操作
$cache->Load(); //装载缓存,缓存有效则不执行以下页面代码
//页面代码开始
$content = date('H:i:s jS F');
echo $content;
//页面代码结束
$cache->Write(1,$content); //首次运行或缓存过期,生成缓存
?>
*Demo3
<?php
header('content-type:text/html;charset=utf-8');
require_once('Junin_Cache.php');
define('CACHEENABLE',true);
if (CACHEENABLE) {
$cachedir = './Cache/'; //设定缓存目录
$cache = new Junin_Cache($cachedir,10); //省略参数即采用缺省设置, $cache = new Cache($cachedir);
$cache->SetCacheId(); //根据get或post的参数设置cache
if ($_GET['cacheact'] != 'rewrite') //此处为一技巧,通过xx.php?cacheact=rewrite更新缓存,以此类推,还可以设定一些其它操作
$cache->Load(); //装载缓存,缓存有效则不执行以下页面代码
}
//页面代码开始
$content = date('H:i:s jS F');
echo $content;
//页面代码结束
if (CACHEENABLE)
$cache->Write(1,$content); //首次运行或缓存过期,生成缓存
?>
*/
class Junin_Cache {
/* *
* 注意 : 请在实例化对象后调用SetCacheId()函数,此调用要先于其它缓存操作.
*
* $mDir : 缓存文件存放目录
* $mLifeTime : 缓存文件有效期,单位为秒
* $mCacheId : 缓存文件路径,包含文件名
* $mExt : 缓存文件扩展名(可以不用),这里使用是为了查看文件方便
*/
private $mDir ;
private $mLifeTime ;
private $mCacheId ;
private $mExt ;
/* *
* 析构函数,检查缓存目录是否有效,默认赋值
*/
function __construct( $pDir = '' , $pLifeTime = 1800 ) {
if ( $this -> CheckDir( $pDir )) {
$this -> mDir = $pDir ;
$this -> mLifeTime = $pLifeTime ;
$this -> mExt = ' .php ' ;
$this -> mCacheId = '' ;
}
}
/* *
* 设定CacheId
* 把页面文件名和查询参数值组合(这样的组合在整个Web应用程序中唯一)加密生成唯一标识作为缓存文件名
* 必须在所有缓存操作前调用
*/
public function SetCacheId() {
$param = func_get_args ();
$str = $_SERVER [ ' PHP_SELF ' ];
if ( is_array ( $param ) &&! empty ( $param )) {
foreach ( $param as $value ) {
$str .= $_REQUEST [ $value ];
}
}
$this -> mCacheId = $this -> mDir . md5 ( $str ) . $this -> mExt;
}
/* *
* 检查缓存是否有效,此处无需调用CheckCacheId()检查,因为该操作的调用在CheckCacheId()之后,并且如果mCacheId未设置,同样会返回false
*/
private function IsValid() {
if ( ! file_exists ( $this -> mCacheId)) return false ;
if ( ! (@ $mtime = filemtime ( $this -> mCacheId))) return false ;
if ( mktime () - $mtime > $this -> mLifeTime) return false ;
return true ;
}
/* *
* 写入缓存
* $mode == 0 , 以浏览器缓存的方式取得页面内容
* $mode == 1 , 以直接赋值(通过$content参数接收)的方式取得页面内容
* $mode == 2 , 以本地读取(fopen ile_get_contents)的方式取得页面内容(似乎这种方式没什么必要)
*/
public function Write( $pMode = 0 , $pContent = '' ) {
if ( ! $this -> CheckCacheId())
$this -> HandleError( ' mCacheId属性未设置,请在操作缓存前调用SetCacheId()设置该属性. ' );
switch ( $pMode ) {
case 0 :
$pContent = ob_get_contents ();
break ;
default :
break ;
}
ob_end_flush ();
if ( is_writeable ( $this -> mCacheId))
file_put_contents ( $this -> mCacheId , $pContent );
else
$this -> HandleError( ' 写入缓存失败!请检查目录权限! ' );
}
/* *
* 加载缓存
* exit() 载入缓存后终止原页面程序的执行,缓存无效则运行原页面程序生成缓存
* ob_start() 开启浏览器缓存用于在页面结尾处取得页面内容
*/
public function Load() {
if ( ! $this -> CheckCacheId())
$this -> HandleError( ' mCacheId属性未设置,请在加载和生成缓存前调用SetCacheId()设置该属性. ' );
if ( $this -> IsValid()) {
echo " <span style="display:none;">This is Cache.</span> " ;
// 以下两种方式,哪种方式好?????
require_once ( $this -> mCacheId);
// echo file_get_contents($this->mCacheId);
exit ();
}
else {
ob_start ();
}
}
/* *
* 清除缓存
*/
public function Clean() {
if ( ! $this -> CheckCacheId())
$this -> HandleError( ' mCacheId属性未设置,请在加载和生成缓存前调用SetCacheId()设置该属性. ' );
if (@ unlink ( $this -> mCacheId))
$this -> HandleError( ' 清除缓存文件失败!请检查目录权限! ' );
}
/* *
* 检查$mCacheId属性是否已设置,即检查SetCacheId()函数是否执行,SetCacheId后每个缓存操作都需要调用此函数检查mCacheId属性是否已赋值
*/
private function CheckCacheId() {
if ( '' == $this -> mCacheId) return false ;
return true ;
}
/* *
* 检查目录是否存在或是否可创建
*/
private function CheckDir( $pDir ) {
if ( is_dir ( $pDir )) return true ;
if (@ mkdir ( $pDir , 0777 )) {
$this -> HandleError( ' 所设定缓存目录不存在并且创建失败!请检查目录权限! ' );
return false ;
}
return true ;
}
/* *
* 输出错误信息
*/
private function HandleError( $pStr ) {
echo ' <div style="color:red;"> ' . $pStr . ' </div> ' ;
exit ();
}
}
?>