Welcome Guest, Not a member yet? Register   Sign In
More Robust View Cache
#1

[eluser]BayssMekanique[/eluser]
I was looking for a way to cache images that I'm loading from my 'asset' controller (performing watermarking, and creating sprites), and noticed the core Output file had a note for 'set_headers' stating that there was still a need to implement a method for caching headers. This class extender does just that.

It stores the entire header as a JSON object, just like it stores the time stamp. It also adds Expires headers, and returns 403s for content with eTags. As written, it will still serve existing cache files, but adds the ability to cache content other than html files, like css, js, and sprite files. The best part, the changes to the two existing methods are very slight, and the added method is externally available, and quite handy for setting the cache header on content that you don't want cached on the system, but do want cached on the client's computer.

Code:
<?php
class MY_Output extends CI_Output {

    function __construct()
    {
        parent::__construct();
    }

// --------------------------------------------------------------------

/**
  * Write a Cache File
  * -Modified by BayssMekanique 9-12-2012
  * ++ Added JSON encoded header to cache file
  * @access public
  * @param  string
  * @return void
  */
function _write_cache($output)
{
  $CI =& get_instance();
  $path = $CI->config->item('cache_path');

  $cache_path = ($path == '') ? APPPATH.'cache/' : $path;

  if ( ! is_dir($cache_path) OR ! is_really_writable($cache_path))
  {
   log_message('error', "Unable to write cache file: ".$cache_path);
   return;
  }

  $uri = $CI->config->item('base_url').
    $CI->config->item('index_page').
    $CI->uri->uri_string();

  $cache_path .= md5($uri);

  if ( ! $fp = @fopen($cache_path, FOPEN_WRITE_CREATE_DESTRUCTIVE))
  {
   log_message('error', "Unable to write cache file: ".$cache_path);
   return;
  }

  //++ BAYSS --------------------------------------- START
  $expire = time() + ($this->cache_expiration * 60);
  $this->set_header('Expires: '.gmdate ("D, d M Y H:i:s", $expire) . ' GMT');
  $this->set_header('Cache-Control: max-age='.($this->cache_expiration * 60));
  $this->set_header('Cache-Control: private');
  

  if (flock($fp, LOCK_EX))
  {
   fwrite($fp, $expire.'TS--->'.json_encode($this->headers).'HD--->'.$output);
   flock($fp, LOCK_UN);
  }
  else
  {
   log_message('error', "Unable to secure a file lock for file at: ".$cache_path);
   return;
  }
  //++ BAYSS --------------------------------------- END
  fclose($fp);
  @chmod($cache_path, FILE_WRITE_MODE);

  log_message('debug', "Cache file written: ".$cache_path);
}

// --------------------------------------------------------------------

/**
  * Update/serve a cached file
  * -Modified by BayssMekanique 9-12-2012
  * ++ Reads JSON encoded header from cache file and outputs using stored header
  * ++ Checks for changes and returns 403 if no modifications
  * @access public
  * @param  object config class
  * @param  object uri class
  * @return void
  */
function _display_cache(&$CFG, &$URI)
{
  $cache_path = ($CFG->item('cache_path') == '') ? APPPATH.'cache/' : $CFG->item('cache_path');

  // Build the file path.  The file name is an MD5 hash of the full URI
  $uri = $CFG->item('base_url').
    $CFG->item('index_page').
    $URI->uri_string;

  $filepath = $cache_path.md5($uri);

  if ( ! @file_exists($filepath))
  {
   return FALSE;
  }

  if ( ! $fp = @fopen($filepath, FOPEN_READ))
  {
   return FALSE;
  }

  flock($fp, LOCK_SH);

  $cache = '';
  if (filesize($filepath) > 0)
  {
   $cache = fread($fp, filesize($filepath));
  }

  flock($fp, LOCK_UN);
  fclose($fp);

  // Strip out the embedded timestamp
  if ( ! preg_match("/(\d+TS--->)/", $cache, $match))
  {
   return FALSE;
  }

  // Has the file expired? If so we'll delete it.
  $expires = trim(str_replace('TS--->', '', $match['1']));
  if (time() >= $expires)
  {
   if (is_really_writable($cache_path))
   {
    @unlink($filepath);
    log_message('debug', "Cache file has expired. File deleted");
    return FALSE;
   }
  }
  
  
  //++ BAYSS --------------------------------------- START
  // Strip and Read JSON header
  $cache = str_replace($match['0'], '', $cache);
  
  if (preg_match("/(.*?)HD--->/", $cache, $match))
  {
   $header = json_decode($match['1']);
   $this->headers = $header;
   $cache = str_replace($match['0'], '', $cache);
  }
  
  // Calls check_cache method
  $this->check_cache($uri.$expires);
  
  //++ BAYSS --------------------------------------- END
  
  // Display the cache
  $this->_display($cache);
  log_message('debug', "Cache file is current. Sending it to browser.");
  return TRUE;
}
//++ BAYSS --------------------------------------- START
function check_cache($buffer)
{
  $etagFile = md5($buffer);
  $etagHeader=(isset($_SERVER['HTTP_IF_NONE_MATCH']) ? trim($_SERVER['HTTP_IF_NONE_MATCH']) : false);
  if ($etagHeader == $etagFile)
  {
      $this->set_status_header('304');
      exit;
  }
  else
  {
   $this->set_header("Etag: $etagFile");
   $this->set_header("Cache-Control: no-cache");
   $this->set_header('Cache-Control: private');
  }
}
//++ BAYSS --------------------------------------- END
}




Theme © iAndrew 2016 - Forum software by © MyBB