最近学习的过程中接触到了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参数值的合法性。
<?
php
/*
*
* 可自由转载,请保留版权信息,谢谢使用!
* Class Name : Junin_Cache (For PHP5)
* Version : 1.0
* Description : 动态缓存类,用于控制页面自动生成缓存、调用缓存、更新缓存、删除缓存.
* Author : junin927@hotmail.com,Junin
* Author Page : http://sdts.blog.hexun.com/ http://blog.youkuaiyun.com/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
();
}
}
?>