打造属于自己的简易缓存系统
一直以来都在使用smarty的cache功能,但总觉得自己动手打造一个,才更有成就感。网上有很多大神开发的功能完备的缓存系统,不过我打算先从简单的做起,实现动态页面静态化的过程,再逐步完善。这两天我完成了一个较为简易的版本,在此记录一下整个过程。
一、技术要点
本次搭建用到的相关技术关键词包括:PHP、Apache,具体涉及以下技术:
- mod_rewrite:利用
RewriteCond
和RewriteRule
实现地址重写。 - ob系列函数:用于进行缓冲处理。
- file_put_contents:这个函数用于生成html文件。
二、运行流程
当用户发出请求url?id=x
时,系统会进行如下操作:
判断文章是否存在:
- 存在:直接转到对应的Html页面。
- 不存在:通过php读取数据库数据,然后生成html文件,并将其存放到指定目录。
三、实现方法
- 地址重写:使用Apache的
mod_rewrite
模块中的RewriteRule
指令来实现重写。 - 判断文章是否存在:借助Apache的
mod_rewrite
模块中的RewriteCond
指令进行判断。 生成html文件:
- 首先通过
ob_start()
打开缓冲。 - 接着将读取文章的php文件包含进来。
- 最后使用
file_put_contents
将获得的缓冲内容写入指定的HTML文件。
- 首先通过
四、代码详解
/Test目录下的.htaccess文件
RewriteEngine On
RewriteRule ^index.html$ /news.php [L]
RewriteCond %{REQUEST_FILENAME}!-s
RewriteRule ^html/news_([0-9]+).html$ getnews.php?id=$1 [L]
其中,第二句RewriteRule ^index.html$ Test/news.php [L]
实现了对news.php
的访问可通过localhost/Test/index.html
达成。
news.php
header("Content-Type:text/html; charset=gbk");//以防出现乱码
mysql_connect("localhost","root","");
mysql_query('SET NAMES gbk');//我的数据库用的gbk编码,请根据自己实际情况调整
mysql_select_db("test");
$sql="SELECT `id`,`title` FROM `arc` order by `id` DESC";
$rs=mysql_query($sql);
while($row=mysql_fetch_array($rs) ){
echo"<a xhref='/Test/html/news_$row[id].html'>$row[title]</a><br>";
}
此文件的作用是列出文章标题链接。
生成php静态页的过程
当点击链接发出对http://localhost/Test/html/news_3.html
的请求时,Apache会通过.htaccess
中的第三句:
RewriteCond %{REQUEST_FILENAME}!-s
来判断news_3.html
是否存在。RewriteCond
表示“定向重写发生条件”,REQUEST_FILENAME
是“客户端请求的文件名”,'-s'
表示测试指定文件是否存在且是一个尺寸大于0的常规文件,!
表示匹配条件的反转。所以该句意味着当请求链接不存在时,执行下面的RewriteRule
规则。
即当请求的news_3.html
不存在时,会重写地址让getnews.php?id=3
来处理(若news_3.html
存在则直接加载该html文件)。
getnews.php
$id=$_GET['id'];
$root=&$_SERVER['DOCUMENT_ROOT'];
$filename="news_".$id.".html";
$file=$root."/Test/html/".$filename;
ob_start();
include($root."/Test/newsDetail.php");
file_put_contents($file,ob_get_contents());
ob_end_flush();
该文件的功能是判断参数传输的完整性,并调用相应文件生成html文件。
newsDetail.php
header("Content-Type:text/html; charset=gbk");
if( isset($_GET['id']) ){
$id= &$_GET['id'];
}else{
header("Location: http://127.0.0.1/lean/Test/html/news_failed.html");
exit();
}
mysql_connect("localhost","root","");
mysql_query('SET NAMES gbk');
mysql_select_db("test");
$id=$_GET['id'];
$sql="SELECT `news` FROM `arc` WHERE `id`=$id";
$rs=mysql_query($sql);
while($row=mysql_fetch_array($rs) ){
echo$row['news'];
}
此文件从数据库中读取数据,产生新闻内容,其内容会被getnews.php
捕获。
最终,系统会在/Test/html
目录下产生以news_文章ID.html
命名的html文件。
五、问题与优化
- 循环重定向问题:一开始在判断是否存在相应html页面时,采用的是php内置的
file_exists()
判断,而未使用Apache的RewriteCond
。结果导致当news_3.html
不存在时,用getnews.php
生成news_3.html
后,因再次请求触发mod_rewrite
,又将news_3.html
重写为getnews.php?id=3
,形成了死循环。后来将文件存在性的判断交给RewriteCond
,指定的html文件不存在时才启用重写规则,成功解决了循环重定向的问题。 - 效率优化:起初没有采用
ob
系列函数,而是使用fopen
打开newsDetail.php
,再将生成的内容通过fwrite
写成html文件,然后用include
输出静态页面。在fhjr999
的提醒下,改为将newDetail.php
包含进getnews.php
,通过ob
系列函数将生成的内容放入缓冲,然后再生成html文件。经测试,ob
的效率约是前者的20倍。