控制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 相关文章推荐
文件上传的实现
Oct 09 PHP
PHP安装攻略:常见问题解答(二)
Oct 09 PHP
php模拟socket一次连接,多次发送数据的实现代码
Jul 26 PHP
如何使用php输出时间格式
Aug 31 PHP
PHP实现获取并生成数据库字典的方法
May 04 PHP
mysql desc(DESCRIBE)命令实例讲解
Sep 24 PHP
Laravel框架中VerifyCsrfToken报错问题的解决
Aug 30 PHP
PHP 计算两个特别大的整数实例代码
May 07 PHP
PHP+MySQL实现消息队列的方法分析
May 09 PHP
php 中htmlentities导致中文无法查询问题
Sep 10 PHP
PHP实现简单登录界面
Oct 23 PHP
PHP7 其他修改
Mar 09 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网页游戏学习之Xnova(ogame)源码解读(二)
2014/06/23 PHP
Javascript 对象的解释
2008/11/24 Javascript
JavaScript 基础知识 被自己遗忘的
2009/10/15 Javascript
javascript与asp.net(c#)互相调用方法
2009/12/13 Javascript
web的各种前端打印方法之jquery打印插件jqprint实现网页打印
2013/01/09 Javascript
jquery iframe操作详细解析
2013/11/20 Javascript
下拉列表select 由左边框移动到右边示例
2013/12/04 Javascript
JS实现从表格中动态删除指定行的方法
2015/03/31 Javascript
JavaScript的模块化开发框架Sea.js上手指南
2016/05/12 Javascript
Bootstrap轮播插件简单使用方法介绍
2016/06/21 Javascript
JavaScript获取css行间样式,内连样式和外链样式的简单方法
2016/07/18 Javascript
jQuery购物网页经典制作案例
2016/08/19 Javascript
js获取指定字符前/后的字符串简单实例
2016/10/27 Javascript
react-router JS 控制路由跳转实例
2017/06/15 Javascript
JavaScript之promise_动力节点Java学院整理
2017/07/03 Javascript
浅谈关于iview表单验证的问题
2018/09/29 Javascript
巧妙运用v-model实现父子组件传值的方法示例
2019/04/07 Javascript
详解Vue3中对VDOM的改进
2020/04/23 Javascript
VSCode搭建Vue项目的方法
2020/04/30 Javascript
vue3.0中setup使用(两种用法)
2020/12/02 Vue.js
JavaScript实现页面高亮操作提示和蒙板
2021/01/04 Javascript
Python基于hashlib模块的文件MD5一致性加密验证示例
2018/02/10 Python
flask应用部署到服务器的方法
2019/07/12 Python
numpy.linalg.eig() 计算矩阵特征向量方式
2019/11/29 Python
Python中对象的比较操作==和is区别详析
2021/02/12 Python
SOA面试题:如何在SOA中实现松耦合
2013/07/21 面试题
公司司机岗位职责范本
2014/03/03 职场文书
青春寄语大全
2014/04/09 职场文书
学习雷锋活动总结
2014/04/29 职场文书
经销商年会策划方案
2014/05/29 职场文书
经济管理专业求职信
2014/06/09 职场文书
文明礼仪标语
2014/06/13 职场文书
发言稿之优秀教师篇
2019/09/26 职场文书
python numpy中setdiff1d的用法说明
2021/04/22 Python
Oracle11g r2 卸载干净重装的详细教程(亲测有效已重装过)
2021/06/04 Oracle
JS中如何优雅的使用async await详解
2021/10/05 Javascript