Cache Class

Cache and deliver your content with lightning speed, and save your server the overhead of repeating itself over and over. This class takes care of all of the file management and header controls for you. Some excellent resources that I used on the subject are at:

bool Cache ( [ string $url [, mixed $expire= 'false' ]] )

You should call this method just as soon as you know what page you want to load, and before you open your database. This is not a piecemeal caching machine that saves a database call or two, this class is designed to get your content to your visitor as soon and fast as possible. If you are delivering custom content, or tell Bill hi on every page to make it personal, then this class will not work for you (unless all of your visitors happen to be named Bill).

$url The relative url you would like this page to be cached as. eg. 'folder/page.html' The only pages that can be cached with this class are 'html', 'xml', 'rss', 'atom', 'css', 'js', and 'txt' files.
$expire How often you would like this page to be re-cached in the form of "{$integer} {$interval}". eg. ('4 seconds', '8 minutes', '15 hours', '16 days', '23 months', or '42 years', etc.) It can also be a timestamp of the last mod date. This will make sure that the cached page is current. If you leave it empty, you can always delete the cache later and everything will be updated then.
Returns True or false, but it's pretty predictable. If you include a $url, and it's of the correct type then it will return true. If this method returns false then a cached version will neither be created nor delivered.
$code = md5($_SERVER['REQUEST_URI']); // a great way to include any url parameters 
$cache = new Cache ("folder/{$code}.html", '15 min');

enforce ( [ string $url ] )

If you only want your page to be accessible via one url (a good idea), then this method is here to save the day. This method may seem a little late in the game as the constructor should have already delivered the page and cancelled the script, but that is only if the cached page exists. If your url can be accessed from 'page.php?id=1', or 'seo-seeded-url.html' (preferred), then redirect the user to the preferred url using this method, and then cache the page.

$url The relative url you would like to enforce. The default is whatever you are naming the cached page.
$cache->enforce ('seo-seeded-url.html');

page ( string $html )

This method caches the $html to the $url specified in the constructor, delivers the content, and ends the script. If the class constructor returned false then this method will do nothing for you.

$html Add everything in the page to your $html var, and put it here.
$html = $page->display ($html);  
echo $html; // just in case this page is not being cached  

clear ( )

This method will clear your entire cache so that you can start afresh. This class also offers a protected 'remove' method that can be more specific for more advanced applications, but this clear method will get you by just fine.

Click to Download the PHP Cache Class

 Subscribe to our feed


 *    author:		Kyle Gadd 
 *    documentation: 
 *    This program is free software: you can redistribute it and/or modify 
 *    it under the terms of the GNU General Public License as published by 
 *    the Free Software Foundation, either version 3 of the License, or 
 *    (at your option) any later version. 
 *    This program is distributed in the hope that it will be useful, 
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of 
 *    GNU General Public License for more details. 
 *    You should have received a copy of the GNU General Public License 
 *    along with this program.  If not, see <>. 
class Cache { 
  protected $cache_uri; 
  private $file = false; 
  private $type; 
  private $encoding; 
  private $expires; 
  private $modified; 
  public function __construct ($url='', $expire=false) { 
    $this->cache_uri = BASE_URI . 'uploads/cache/'; 
    if (!empty($url)) { 
      $url = trim($url); 
      if ($url[0] == '/') $url = substr($url, 1); 
      $type = explode('.', $url); 
      $type = array_pop($type); 
      if (!in_array($type, array('html', 'xml', 'rss', 'atom', 'css', 'js', 'txt'))) return false; 
      $encoding = $this->encoding(); 
      $this->file = $this->cache_uri . $url . ($encoding != 'none' ? '.' . $encoding : ''); 
      if ($type == 'xml' && strpos($this->file, 'rss') !== false) $type = 'rss'; 
      if ($type == 'xml' && strpos($this->file, 'atom') !== false) $type = 'atom'; 
      $this->type = $type; 
      $this->encoding = $encoding; 
      $this->expires = $this->expires($expire); 
      return true; 
    return false; 
  public function enforce ($url='') { 
    if (empty($url)) $url = str_replace(array($this->cache_uri, '.gzip', '.deflate'), '', $this->file); 
    if (substr($_SERVER['REQUEST_URI'], 1, strlen($url)) != $url) { 
      header('Location: ' . BASE_URL . $url, true, 301); 
  public function page ($html) { 
    if ($this->file) { 
      if ($this->encoding != 'none') $html = gzencode($html, 9, ($this->encoding == 'gzip') ? FORCE_GZIP : FORCE_DEFLATE); 
      $this->save ($this->file, $html); 
  public function clear () { 
    $this->remove ($this->cache_uri); 
  protected function remove ($dir) { 
    if (strpos($dir, BASE_URI) === false) return false; // get out of here before we do some major damage 
    if (is_file($dir)) return unlink($dir); 
    if (!is_dir($dir) || !$handle = opendir($dir)) return false; 
    while (false !== ($file = readdir($handle))) { 
      if ($file == '.' || $file == '..') continue; 
      $fullpath = $dir . $file; 
      if (is_dir($fullpath)) { 
        $this->remove ($fullpath.'/'); 
      } else { 
    return rmdir($dir); 
  private function save ($file, $content) { 
    if (!is_dir(dirname($file))) mkdir(dirname($file), 0755, true); 
    if ($fp = fopen($file, 'wb')) { 
      fwrite($fp, $content); 
      return true; 
    return false; 
  private function encoding () { 
    $supported = (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) ? $_SERVER['HTTP_ACCEPT_ENCODING'] : ''; 
    $gzip = strstr($supported, 'gzip'); 
    $deflate = strstr($supported, 'deflate'); 
    $encoding = $gzip ? 'gzip' : ($deflate ? 'deflate' : 'none'); 
    if (isset($_SERVER['HTTP_USER_AGENT']) && !strstr($_SERVER['HTTP_USER_AGENT'], 'Opera') && preg_match('/^Mozilla\/4\.0 \(compatible; MSIE ([0-9]\.[0-9])/i', $_SERVER['HTTP_USER_AGENT'], $matches)) { 
      $version = floatval($matches[1]);			 
      if ($version < 6) $encoding = 'none';				 
      if ($version == 6 && !strstr($_SERVER['HTTP_USER_AGENT'], 'EV1')) $encoding = 'none'; 
    return $encoding; // either 'gzip', 'deflate', or 'none' 
  private function expires ($time) { 
    if (empty($time) || is_numeric($time)) { 
      $this->modified = $time; 
      return false; 
    if ($time === true) return -3600; // an hour ago 
    $time = explode(' ', $time); 
    if (!is_numeric($time[0])) return false; 
    if (!isset($time[1])) return $time[0]; // assumed to be in seconds 
    $interval = substr(strtolower($time[1]), 0, 3); 
    $time = $time[0]; 
    switch ($interval) { 
      case 'sec': $time = $time; break; // no further processing needed 
      case 'min': $time = $time * 60; break; // 60 seconds 
      case 'hou': $time = $time * 3600; break; // 60 minutes 
      case 'day': $time = $time * 86400; break; // 24 hours 
      case 'mon': $time = $time * 2592000; break; // 30 days 
      case 'yea': $time = $time * 31536000; break; // 365 days 
    return $time; // in seconds 
  private function conditional_get () { 
    $lastmod = ($this->file && file_exists($this->file)) ? filemtime($this->file) : false; 
    if (!$lastmod) return false; 
    if ($this->expires !== false && ($lastmod + $this->expires) < time()) { 
      return false; 
    } elseif (!empty($this->modified) && $lastmod < $this->modified) { 
      return false; 
    while (ob_get_level()) ob_end_clean(); 
    $etag = '"' . $lastmod . '-' . md5($this->file) . '"'; 
    $lastmod = gmdate('D, d M Y H:i:s \G\M\T', $lastmod); // A GMT RFC 2822 Formated Date 
    $ifmod = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] == $lastmod : null; 
    $iftag = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) == $etag : null; 
    $match = (($ifmod || $iftag) && ($ifmod !== false && $iftag !== false)) ? true : false; 
    header("ETag: {$etag}"); // ETag is sent even with 304 header 
    if ($match) { 
      // header('HTTP/1.0 304 Not Modified'); 
      header('Content-Type:', true, 304); 
    if ($fp = fopen($this->file, 'rb')) { 
      header("Last-Modified: {$lastmod}"); // Last-Modified doesn't need to be sent with 304 response 
      if ($this->encoding != 'none') header("Content-Encoding: " . $this->encoding); 
      switch ($this->type) { 
        case 'html': header("Content-Type: text/html"); break; 
        case 'atom': header("Content-Type: application/atom+xml"); break; 
        case 'rss': header("Content-Type: application/rss+xml"); break; 
        case 'xml': header("Content-Type: text/xml"); break; 
        case 'css': header("Content-Type: text/css"); break; 
        case 'txt': header("Content-Type: text/plain"); break; 
        case 'js': header("Content-Type: text/javascript"); break; 
      header("Content-Length: " . filesize($this->file)); 

comments powered by Disqus
Copyright © 2011 - PHP Made Easy