控制PHP的输出:缓存并压缩动态页面


Posted in PHP onJune 11, 2013

mod_gzip是一个Apache模块,其功能是使用Gzip压缩静态的html页面,遵循IETF标准的浏览器可以接受gzip编码(IE, Netscape等)。mod_gzip可以将页面的下载时间提高4-5倍。我强烈建议你在你的web服务器上使用mod_gzip。然而,我们还必须用PHP建立我们自己的压缩引擎。在这篇文章里,我将要介绍如何使用PHP的输出控制函数来大幅加速页面载入速度。

介绍PHP的输出控制函数

PHP4中最令人满意的事是——你可以让PHP缓存所有由脚本生成的输出,在你决定把它们送出之前,浏览器方是不会收到任何内容的。在脚本程序中你能用这些函数来设置header、cookies,然而这只是强大的输出函数的一小部分功能。

<?php 
void ob_start(void); 
?>

告诉PHP处理器把所有的输出重定向到一个内部的缓存(buffer)中。在调用ob_start之前,没有输出会被送到浏览器去。

<?php 
string ob_get_contents(void); 
?>

该函数将“输出缓存”(output buffer)以字符串的形式返回。你可以调用该函数把积累下来的输出送到浏览器中。(仅在把buffering功能关闭之后!!)

<?php 
int ob_get_length(void); 
?>

返回缓存中的字符串的长度。

<?php 
void ob_end_clean(void); 
?>

清空输出缓存,并将输出缓存关闭。在缓存中的内容输出到浏览器之前,必须使用这个函数。
void 501([int flag])
用来打开/关闭隐含的flush动作开关(默认是关)。如果flush是开的,每次调用print/echo或是其它输出命令的时候,输出的内容会被立即送到浏览器端。

使用输出控制来压缩PHP输出
你必须使用PHP4里编译的Zlib扩展包来压缩输出。如果需要的话,可以查看PHP文档中有关Zlib包的安装指导。
首先,初始化输出缓存:

<?php 
ob_start(); 
ob_implicit_flush(0); 
?>

之后,用print, echo, 或其他你喜欢的方法生成所有输出内容,例如:

<?php
print("Hey this is a compressed output!"); 
?>

页面生成后,我们取回输出内容:

<?php 
$contents = ob_get_contents(); 
ob_end_clean(); 
?>

之后,必须检测浏览器是否支持压缩数据。如果支持,浏览器会发给服务器端一个ACCEPT-ENCODEING HTTP头。我们只需检查$HTTP_ACCEPT_ENCODING变量中是否有“gzip,deflate”字串。

<?php 
if(ereg('gzip, deflate',$HTTP_ACCEPT_ENCODING)) { 
// 在这里生成 Gzip 压缩的内容 
} else { 
echo $contents; 
} 
?>

这种方法使用起来既简单又结构清晰。下面让我们看看如何生成压缩的输出:

<?php 
//告诉浏览器将要收到的是gzip数据 
//当然在此之前,你已经检查了它们是否支持gzip,x-gzip数据格式 
//如果支持的是x-gzip,那么下面的头就要用z-gzip来代替 
header("Content-Encoding: gzip"); 
//显示gzip文件的头 
//只需显示一次 
echo "x1fx8bx08x00x00x00x00x00"; 
//计算出文件的大小和CRC码 
$Size = strlen($contents); 
$Crc = crc32($contents); 
//压缩数据 
$contents = gzcompress($contents, 9); 
//我们不能就这样输出,因为CRC码是混乱的。 
//如果我在这里使用“echo $contents”,压缩的数据会被送出, 
//但是却不完整。文件最后的四个字节是CRC校验码,可是只发出去了三个字节。 
//最后一个字节被丢掉了。我不知道这个bug在4.0.2版中解决了没有, 
//不过最好避免错误的方法是把正确的CRC校验码加到压缩的数据的末尾。 
// 
//把旧的CRC校验码剥离 
$contents = substr($contents, 0, strlen($contents) - 4); 
//仅显示压缩的数据 
echo $contents; 
//输出CRC,和原来数据的大小(字节) 
gzip_PrintFourChars($Crc); 
gzip_PrintFourChars($Size); 
function gzip_PrintFourChars($Val) { 
for ($i = 0; $i <4; $i ++) { 
echo chr($Val % 256); 
$Val = floor($Val / 256); 
} 
} 
?> 
//好了,你还可以按此方式附加上更多的压缩数据。

要想进行实际的测试,所有的脚本代码如下:

<?php 
ob_start(); 
ob_implicit_flush(0); 
print("I'm compressed!n"); 
$contents = ob_get_contents(); 
ob_end_clean(); 
header("Content-Encoding: gzip"); 
echo "x1fx8bx08x00x00x00x00x00"; 
$Size = strlen($contents); 
$Crc = crc32($contents); 
$contents = gzcompress($contents, 9); 
$contents = substr($contents, 0, strlen($contents) - 4); 
echo $contents; 
gzip_PrintFourChars($Crc); 
gzip_PrintFourChars($Size); 
function gzip_PrintFourChars($Val) { 
for ($i = 0; $i <4; $i ++) { 
echo chr($Val % 256); 
$Val = floor($Val / 256); 
} 
} 
?>

缓存PHP输出

当PHP4还没问世,我不得不使用PHP3的时候,我对开发一些缓存机制来减少数据库的载入、对文件系统的存取十分感兴趣。在PHP3中没有什么特别好的方法,但是有了输出缓存之后,在PHP4中一切变得容易多了。
这有一个简单的例子:

<?php 
//为请求的URI构造一个文件名 
$cached_file=md5($REQUEST_URI); 
if((!file_exists("/cache/$cached_file"))||(!is_valid("/cache/$cached_file"))) { 
//is_valid函数验证缓存,你可以用这个函数检查Cache是否过期或其他特定的条件。 
//如果文件不在Cache中或者不可用则生成输出 
ob_start(); 
ob_implicit_flush(0); 
//在此输出…… 
$contents = ob_get_contents(); 
ob_end_clean(); 
$fil=fopen($cached_file,"w+"); 
fwrite($fil,$contents,$strlen($contents)); 
fclose($fil); 
} 
/如果请求的文件在缓存中且可用,则: 
readfile($cached_file); 
?>

这是一个简单的例子,使用输出缓存,你可以建立一个复杂的内容生成系统,对不同的块或程序使用不同的缓存机制,等等……

结论

PHP输出控制函数对把脚本生成的输出重定向到缓存中十分有用。为支持gzip的浏览器输出压过的缓存数据可以减少载入时间。也可作为缓存机制来减少对数据源的存取(数据库或文件),这对使用XML意义重大。
如果我们用PHP建立一个引擎,缓存从数据源得到的数据(xml文档和数据库),并且动态的生成XML格式的内容(没有外观-presentation)我们可以得到这些XML的输出,并使用XSLT转换成任意一种我们想要的外观格式(html, wap, palm, pdf等)。使用PHP4的输出缓存和Sablotron XSLT扩展可以很好地完成这个任务。

PHP 相关文章推荐
DW中链接mysql数据库时,建立字符集中文出现乱码的解决方法
Mar 27 PHP
PHP执行linux系统命令的常用函数使用说明
Apr 27 PHP
php ajax 静态分页过程形式
Sep 02 PHP
php数组函数序列之array_sum() - 计算数组元素值之和
Oct 29 PHP
基于php实现长连接的方法与注意事项的问题
May 10 PHP
php实现把url转换迅雷thunder资源下载地址的方法
Nov 07 PHP
php数组添加与删除单元的常用函数实例分析
Feb 16 PHP
PHP使用数组依次替换字符串中匹配项
Jan 08 PHP
PHP文件下载实例代码浅析
Aug 17 PHP
php+ajax实现带进度条的上传图片功能【附demo源码下载】
Sep 14 PHP
php使用正则表达式获取字符串中的URL
Dec 29 PHP
基于Laravel 多个中间件的执行顺序详解
Oct 21 PHP
基于PHP导出Excel的小经验 完美解决乱码问题
Jun 10 #PHP
win7+apache+php+mysql环境配置操作详解
Jun 10 #PHP
浅谈php中mysql与mysqli的区别分析
Jun 10 #PHP
探讨php中防止SQL注入最好的方法是什么
Jun 10 #PHP
php防注入,表单提交值转义的实现详解
Jun 10 #PHP
PHP获取当前页面完整URL的实现代码
Jun 10 #PHP
如何判断php数组的维度
Jun 10 #PHP
You might like
php模板中出现空行解决方法
2011/03/08 PHP
PHP中几个可以提高运行效率的代码写法、技巧分享
2014/08/21 PHP
PhpStorm连接服务器并实现自动上传功能
2020/12/09 PHP
jquery 的 $(&quot;#id&quot;).html() 无内容的解决方法
2010/06/07 Javascript
从盛大通行证上摘下来的身份证验证js代码
2011/01/11 Javascript
基于jquery实现的上传图片及图片大小验证、图片预览效果代码
2011/04/12 Javascript
灵活应用js调试技巧解决样式问题的步骤分享
2012/03/15 Javascript
浅析Node在构建超媒体API中的作用
2014/07/30 Javascript
JavaScript实现防止网页被嵌入Frame框架的代码分享
2014/12/29 Javascript
JS实现在网页中弹出一个输入框的方法
2015/03/03 Javascript
使用jQuery在移动页面上添加按钮和给按钮添加图标
2015/12/04 Javascript
jQuery实现为LI列表前3行设置样式的方法【2种方法】
2016/09/04 Javascript
浅谈JavaScript事件绑定的常用方法及其优缺点分析
2016/11/01 Javascript
原生js实现打字动画游戏
2017/02/04 Javascript
JavaScript原生数组Array常用方法
2017/04/06 Javascript
深入理解Javascript中的作用域链和闭包
2017/04/25 Javascript
JS实现瀑布流布局
2017/10/21 Javascript
webpack多入口多出口的实现方法
2018/08/17 Javascript
vue2.0 可折叠列表 v-for循环展示的实例
2018/09/07 Javascript
在vue中解决提示警告 for循环报错的方法
2018/09/28 Javascript
jQuery利用FormData上传文件实现批量上传
2018/12/04 jQuery
微信小程序常用的3种提示弹窗实现详解
2019/09/19 Javascript
详谈Object.defineProperty 及实现数据双向绑定
2020/07/18 Javascript
python实现数通设备端口监控示例
2014/04/02 Python
Python简单实现TCP包发送十六进制数据的方法
2016/04/16 Python
利用 Monkey 命令操作屏幕快速滑动
2016/12/07 Python
利用Python查看目录中的文件示例详解
2017/08/28 Python
对pandas replace函数的使用方法小结
2018/05/18 Python
澳大利亚鞋仓库:Shoe Warehouse
2019/07/25 全球购物
信息管理专业学生自荐信格式
2013/09/22 职场文书
信息技术培训感言
2014/03/06 职场文书
环境工程专业自荐信范文
2014/03/18 职场文书
大学学生会竞选演讲稿
2014/04/25 职场文书
学校教师安全责任书
2014/07/23 职场文书
2015年教学副校长工作总结
2015/07/22 职场文书
标会主持词应该怎么写?
2019/08/15 职场文书