jump to navigation

Http Cache Component (updated) June 16, 2006

Posted by rossoft in CakePHP.
trackback

Uses the HTTP 1.1 caching.
Example:

You've the 'modified' date from a record in your DB, then you must convert it to timestamp. In your action you can do
$this->httpCache->cache($modified_timestamp);
From now, the browser will cache that response, in the next request the browser will send that timestamp, and it will be compared with server's one. If the browser's one is not older than $modified_timestamp, then a status code of not modified is sent and the action exits.

Other example: expiration date
$this->httpCache->expires('+1 Day'); 
Then the page will be cached 1 day. You must use only one of those in every different url.

Copy to /app/controllers/components/http_cache.php
<?php
/**
 * Http Cache Component
 * Uses built in HTTP 1.1 caching mechanism
 * (304 Not Modified)
 * 
 * @author RosSoft
 * @license MIT
 * @version 0.2
 *
 * @link http://blog.codahale.com/2006/05/23/rails-plugin-http_caching/
 *
 *
 * Example 1:
 * $this->httpCache->cache($timestamp_modified_record);
 * $timestamp_modified_record is the timestamp of the content (like 'modified' field)
 *
 * Example 2:
 * $this->httpCache->expires('+1 Day');
 * The page will be cached until expire time.
 *
 * (cache and expires are incompatible, use one of them for each different url)
 */

class HttpCacheComponent extends Object
{
    var $enabled=true;
   
    /**
     * If the browser cache has a timestamp newer than the
     * modification date of the content, then a response of
     * not-modified is sent (and exits).
     *
     * @param integer $timestamp The modification date of the content (Unix timestamp)
     * @param boolean $no_cache If true, then it stores cache to user always
     *
     */   
    function cache($timestamp,$no_cache=false)
    {       
        if ($this->enabled)
        {
            $this->_cache_control();           
            if (env('HTTP_IF_MODIFIED_SINCE' ;)
            && $timestamp <= strtotime(env('HTTP_IF_MODIFIED_SINCE'))
            && ! $no_cache)
            {
                header('Last-Modified: '.$this->_timestamp_to_gmt, true, 304);               
                exit();
            }
            else
            {
                header('Last-Modified: '.$this->_timestamp_to_gmt($timestamp), true, 200);
            }
        }
    }

    /**
     * Sets an expiration time for the page.
     *
     * @param mixed $expires A strtotime string / timestamp integer: expiration date.
     * @param boolean $no_cache If true, then it stores cache to user always
     */     
    function expires($expires='+1 Day',$no_cache=false)
    {
        if ($this->enabled)
        {
            $this->_cache_control();
            if (env('HTTP_IF_MODIFIED_SINCE' ;)
            && time()<= strtotime(env('HTTP_IF_MODIFIED_SINCE'))
            && ! $no_cache)
            {
                header('Last-Modified: '.env('HTTP_IF_MODIFIED_SINCE'), true, 304);
                header("HTTP/1.1 304 Not changed");
                exit();
            }   
            else
            {
                header('Last-Modified: '.$this->_timestamp_to_gmt($expires), true, 200);
            }
        }                   
    }

    /**
     * Timestamp to GMT string   
     *
     * @param mixed $timestamp . A strtotime string / timestamp integer
     * @return string GMT time
     */
    function _timestamp_to_gmt($timestamp)
    {
        if (! is_numeric($timestamp))
        {
            $timestamp=strtotime($timestamp);                       
        }       
        return gmdate('D, d M Y H:i:s', $timestamp).' GMT';
    }   

    /**
     * Sets header for cache control
     */   
    function _cache_control()
    {
        header("Cache-Control: Public",true);
        header("Pragma: Public",true);   
    }   
}
?>

Comments»

1. truster - June 17, 2006

Just one little thing what I think about it: imagine situation when You will use this component in more then one controller and You will call other controller by Object::requestAction(). In both of them will be HttpCacheComponent::cache() executed (in case cache is out of date). I think it is not wanted behaviour, so I propose to You use var $enabled = false; in definition of this component, implement method startup() with test of value $this->enabled and in Your controller You will be able manage this component in beforeFilter() method. default value of ‘enabled’ shall be true, if You think - it is not point here.

Anyway: nice work, man!

2. rossoft - June 17, 2006

thanks, updated