欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-02-24 23:35:11
我们往往在服务器上对缓存设置进行各种优化方案,但是我们却很少注意到客户端缓存,准确的说是浏览器的缓存机制。其实每种浏览器都有缓存策略,会暂时将每一个浏览过的文件缓存在一个特...
我们往往在服务器上对缓存设置进行各种优化方案,但是我们却很少注意到客户端缓存,准确的说是浏览器的缓存机制。
其实每种浏览器都有缓存策略,会暂时将每一个浏览过的文件缓存在一个特殊的文件夹里。我们就可以在用户重复提交页面请求的时候,告诉用户这个页 面没有改变,可以调用缓存。 那我们怎么知道用户有没有这个页面的缓存数据呢? 其实浏览器在发送请求的时候会先发送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');//返回标识,用于标识上次的确访问过(浏览器中存在缓存)