深入PHP与浏览器缓存的分析


Posted in PHP onJune 03, 2013

我们往往在服务器上对缓存设置进行各种优化方案,但是我们却很少注意到客户端缓存,准确的说是浏览器的缓存机制。
其实每种浏览器都有缓存策略,会暂时将每一个浏览过的文件缓存在一个特殊的文件夹里。我们就可以在用户重复提交页面请求的时候,告诉用户这个页 面没有改变,可以调用缓存。 那我们怎么知道用户有没有这个页面的缓存数据呢? 其实浏览器在发送请求的时候会先发送http头,一般象这样:
Date: Sun, 30 Jul 2006 09:18:11 GMT
Content-Type: image/gif
Last-Modified: Wed, 19 Jul 2006 07:40:06 GMT
ETag: "8c55da8d6abc61:2327"
Content-Length: 14757
其中
Last-Modified: Wed, 19 Jul 2006 07:40:06 GMT
ETag: "8c55da8d6abc61:2327"
就是有关页面的缓存信息的。然后如果服务器返回的响应代码不是HTTP 200 (OK),而是 304的话,浏览器就会从缓存中读取数据。
//告诉客户端浏览器不使用缓存,HTTP 1.1 协议 
header("Cache-Control: no-cache, must-revalidate"); 

//告诉客户端浏览器不使用缓存,兼容HTTP 1.0 协议 
header("Pragma: no-cache"); 
根据这个原理,可以用在不经常更新或者需要经常刷新的页面,可以大大减轻服务器的负担,因为它如果发现客户端有缓存,就向客户端发送一个304响应,然后停止程序的执行。

浏览器发出的请求中包含If-Modified-Since和If-None-Match 两个参数,第一个表示询问数据的最后修改时间是否是Thu,19 Jun 2008 16:24:01 GMT 然后服务器就会检查数据的最后修改时间,如果是该时间则返回状态码304(表示没有修改),此时当浏览器收到状态码是304时就不会下载数据而是从本地缓 存中调用。然而只有本地缓存中存在着该请求资源的数据时浏览器才会发送If-Modified-Since参数并且其值为上一次服务器所返回的Last- Modified的值(并不是所有的服务器都支持If-Modified-Since和If-None-Match );If-None-Match的功能也类似,它是由服务器返回的Etag的值生成的,可以是任意值,因为其作用仅仅是使服务器检查数据的修改时间然后返 回而已,只要不为none(默认值)或不为空其它的都可以。

所以我们可以在代码的最前部分设置返回给浏览的Etag为某个值,然后在这个资源被第二次请求的时候就会附带着一个If-None-Match 参 数,通过核实其值确实为所发出的Etag值时就可以指定服务器返回为304然后强行退出程序就行了,If-Modified-Since也是一样的做法这 里就只给出etag方法的php版(Last-Modified版的太常见了如设置缓存超时等等):
PHP 代码复制到剪贴板

    if ($_SERVER["HTTP_IF_NONE_MATCH"] == "claymorephp.com")
    {
        header('Etag:'.'zhaiyun.com',true,304);
        exit();
    }
    else {
        header('Etag:'."claymorephp.com");
    }
    你还可以稍微改一下:
    $expires=date("Ymd"); //一天后缓存过期
    if ($_SERVER["HTTP_IF_NONE_MATCH"] == $expires)
    {
        header('Etag:'.$expires,true,304);
        exit();
    }
    else {
        header('Etag:'.$expires);
    }
if ($_SERVER["HTTP_IF_NONE_MATCH"] == "claymorephp.com") { header('Etag:'.'zhaiyun.com',true,304); exit(); } else { header('Etag:'."claymorephp.com"); } 你还可以稍微改一下: $expires=date("Ymd"); //一天后缓存过期 if ($_SERVER["HTTP_IF_NONE_MATCH"] == $expires) { header('Etag:'.$expires,true,304); exit(); } else { header('Etag:'.$expires); }

另外,当GZIP和ETAG同时使用时有时会出问题,就是ETAG没有值,这个问题是普遍存在的,我暂时没有找到相关的原因,网上搜了一会,普遍的人称之为BUG。
基于以上原因,关于PHPBLOG的客户端缓存是以下来处理的(同时对HTTP_IF_NONE_MATCH和HTTP_IF_MODIFIED_SINCE进行判断):
PHP 代码复制到剪贴板
      if($_SERVER['HTTP_IF_NONE_MATCH'])
        {
            if($_SERVER['HTTP_IF_NONE_MATCH'] == 'phpblog')
            {
                header('Etag:phpblog',true,304);//控制浏览器缓存
                $_SESSION['time_end']=microtime(true);
                exit();
            }
        }
        else if($_SERVER['HTTP_IF_MODIFIED_SINCE'])//eg:Sun, 02 Nov 2008 07:08:25 GMT; length=35849
        {
            $array=explode(' ',$_SERVER['HTTP_IF_MODIFIED_SINCE']);
            $gmday=$array[1];
            $month_array=array(
            "Jan"=>"01",
            "Feb"=>"02",
            "Mar"=>"03",
            "Apr"=>"04",
            "May"=>"05",
            "Jun"=>"06",
            "Jul"=>"07",
            "Aug"=>"08",
            "Sep"=>"09",
            "Oct"=>"10",
            "Nov"=>"11",
            "Dec"=>"12");
            $gmmonth=$month_array[$array[2]];
            $gmyear=$array[3];
            $array=explode(':',$array[4]);
            $gmtimestamp=gmmktime($array[0],$array[1],$array[2],$gmmonth,$gmday,$gmyear);
            if(gmmktime()-$gmtimestamp<$config_client_cache_time*60*60)
            {
                header('Etag:phpblog',true,304);//控制浏览器缓存
                $_SESSION['time_end']=microtime(true);
                exit();
            }
        }
if($_SERVER['HTTP_IF_NONE_MATCH']) { if($_SERVER['HTTP_IF_NONE_MATCH'] == 'phpblog') { header('Etag:phpblog',true,304);//控制浏览器缓存 $_SESSION['time_end']=microtime(true); exit(); } } else if($_SERVER['HTTP_IF_MODIFIED_SINCE'])//eg:Sun, 02 Nov 2008 07:08:25 GMT; length=35849 { $array=explode(' ',$_SERVER['HTTP_IF_MODIFIED_SINCE']); $gmday=$array[1]; $month_array=array( "Jan"=>"01", "Feb"=>"02", "Mar"=>"03", "Apr"=>"04", "May"=>"05", "Jun"=>"06", "Jul"=>"07", "Aug"=>"08", "Sep"=>"09", "Oct"=>"10", "Nov"=>"11", "Dec"=>"12"); $gmmonth=$month_array[$array[2]]; $gmyear=$array[3]; $array=explode(':',$array[4]); $gmtimestamp=gmmktime($array[0],$array[1],$array[2],$gmmonth,$gmday,$gmyear); if(gmmktime()-$gmtimestamp<$config_client_cache_time*60*60) { header('Etag:phpblog',true,304);//控制浏览器缓存 $_SESSION['time_end']=microtime(true); exit(); } }

缓存的HEADER是这样来发送的:
PHP 代码复制到剪贴板
     $client_cache_time=$config_client_cache_time*60*60;//单位 - 秒
            header('Cache-Control: public, max-age='.$client_cache_time);
            header('Expires: '.gmdate('D, d M Y H:i:s',time()+$client_cache_time).' GMT');//设置页面缓存时间
            header('Last-Modified: '.gmdate('D, d M Y H:i:s',time()).' GMT');//返回最后修改时间
            header('Pragma: public');
            header('Etag:phpblog');//返回标识,用于标识上次的确访问过(浏览器中存在缓存)
$client_cache_time=$config_client_cache_time*60*60;//单位 - 秒 header('Cache-Control: public, max-age='.$client_cache_time); header('Expires: '.gmdate('D, d M Y H:i:s',time()+$client_cache_time).' GMT');//设置页面缓存时间 header('Last-Modified: '.gmdate('D, d M Y H:i:s',time()).' GMT');//返回最后修改时间 header('Pragma: public'); header('Etag:phpblog');//返回标识,用于标识上次的确访问过(浏览器中存在缓存)

PHP 相关文章推荐
附件名前加网站名
Mar 23 PHP
php curl模拟post请求小实例
Nov 13 PHP
回帖脱衣服的图片实现代码
Feb 15 PHP
php的declare控制符和ticks教程(附示例)
Mar 21 PHP
php的ZipArchive类用法实例
Oct 20 PHP
php制作的简单验证码识别代码
Jan 26 PHP
PHP编写RESTful接口
Feb 23 PHP
php实现简单爬虫的开发
Mar 28 PHP
PHP入门教程之表单与验证实例详解
Sep 11 PHP
phpinfo()中Loaded Configuration File(none)的解决方法
Jan 16 PHP
Ubuntu中支持PHP5与PHP7双版本的简单实现
Aug 19 PHP
Yii框架使用PHPExcel导出Excel文件的方法分析【改进版】
Jul 24 PHP
PHP判断图片格式的七种方法小结
Jun 03 #PHP
基于session_unset与session_destroy的区别详解
Jun 03 #PHP
PHP批量采集下载美女图片的实现代码
Jun 03 #PHP
基于PHP CURL获取邮箱地址的详解
Jun 03 #PHP
解析CI即CodeIgniter框架在Nginx下的重写规则
Jun 03 #PHP
深入php函数file_get_contents超时处理的方法详解
Jun 03 #PHP
详解PHP内置访问资源的超时时间 time_out file_get_contents read_file
Jun 03 #PHP
You might like
PHP实现文件安全下载
2006/10/09 PHP
动态新闻发布的实现及其技巧
2006/10/09 PHP
PHP结合JQueryJcrop实现图片裁切实例详解
2014/07/24 PHP
php读取目录及子目录下所有文件名的方法
2014/10/20 PHP
PHP过滤器 filter_has_var() 函数用法实例分析
2020/04/23 PHP
Aster vs KG BO3 第一场2.18
2021/03/10 DOTA
js对象之JS入门之Array对象操作小结
2011/01/09 Javascript
Json对象与Json字符串互转(4种转换方式)
2013/03/27 Javascript
jquery动态添加删除一行数据示例
2014/06/12 Javascript
jQuery中[attribute=value]选择器用法实例
2014/12/31 Javascript
jQuery插件pagewalkthrough实现引导页效果
2015/07/05 Javascript
JS+CSS实现大气清新的滑动菜单效果代码
2015/10/22 Javascript
jquery自定义右键菜单、全选、不连续选择
2016/03/01 Javascript
jQuery获取file控件中图片的宽高与大小
2016/08/04 Javascript
详解jquery easyui之datagrid使用参考
2016/12/05 Javascript
JavaScript奇技淫巧44招【实用】
2016/12/11 Javascript
微信小程序-小说阅读小程序实例(demo)
2017/01/12 Javascript
兼容浏览器的js事件绑定函数(详解)
2017/05/09 Javascript
nodejs实现的简单web服务器功能示例
2018/03/15 NodeJs
vue项目中api接口管理总结
2018/04/20 Javascript
javascript中floor使用方法总结
2019/02/02 Javascript
jQuery轮播图功能制作方法详解
2019/12/03 jQuery
用Python写冒泡排序代码
2016/04/12 Python
pyinstaller打包多个py文件和去除cmd黑框的方法
2019/06/21 Python
推荐值得学习的12款python-web开发框架
2020/08/10 Python
CSS实现定位元素居中的方法
2015/06/23 HTML / CSS
html5新增的定时器requestAnimationFrame实现进度条功能
2018/12/13 HTML / CSS
详解canvas.toDataURL()报错的解决方案全都在这了
2020/03/31 HTML / CSS
介绍一下HDLC(High-Level Data Link Control)高层数据链路协议
2012/01/21 面试题
播音主持女孩的自我评价分享
2013/11/20 职场文书
学生会宣传部部长竞选演讲稿
2014/04/25 职场文书
关于学习的演讲稿
2014/05/10 职场文书
庆祝儿童节标语
2014/10/09 职场文书
2014年组织委员工作总结
2014/12/01 职场文书
2016年度员工工作表现评语
2015/12/02 职场文书
Vue OpenLayer 为地图绘制风场效果
2022/04/24 Vue.js