Sources for file plugins/cache/file.php in version 4.0 Beta 1
Click on a comment to hide it. Click here to show all comments.
/**
* Project:
* File:
*
* Version:
* SVN Id: $Id: file.php 5 2007-05-18 03:49:07Z bok $
* SVN URL: $HeadURL: http://svn.syd.wholesalebroadband.com.au/xnyo/trunk/plugins/cache/file.php
$
* Authors: Robert Amos <bok[at]odynia.org>
*
* Copyright (c) 2001-2007 Robert Amos <bok[at]odynia.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**/
class XnyoCacheFile implements XnyoClassPlugin
{
// Plugin Information
public $_plugin_info = array
(
'name' => 'Xnyo File Cache',
'description' => 'Stores cached copies of data for quick retrieval',
'action' => XNYO_PLUGIN_ACTION_RETURN
);
/**
* Information Storage
**/
private $fp;
private $headers;
private $xnyo;
/**
* Constructor
**/
public function __construct ()
{
$this->xnyo = $GLOBALS['xnyo_parent'];
}
/**
* Get Filename
**/
private function get_file ()
{
$file = '';
// and the cache id if one is specified
if (!empty($this->xnyo->cache->id))
$file .= $this->xnyo->cache->id;
// otherwise, use the filename
else
$file .= dirname($_SERVER['SCRIPT_NAME']);
return $file;
}
private function get_subdirs ()
{
$dirs = array();
// store it inside the language folder
if (!empty($this->xnyo->session->language))
$dirs[] = $this->xnyo->session->language.DIRSEP;
// and if we're logged in
if (!empty($this->xnyo->user->username))
$dirs[] = $this->xnyo->user->username.DIRSEP;
// do we have a cache id with "groups"?
if (!empty($this->xnyo->cache->id))
{
$d = explode(':', $this->xnyo->cache->id);
if (count($d))
$dirs += $d;
} else
{
$d = explode('/', substr($_SERVER['SCRIPT_NAME'], 0, 1));
if (count($d))
$dirs += $d;
}
return $dirs;
}
/**
* Write a buffer to the cache file.
**/
public function write ($buf)
{
// before we go any further, we cannot cache something with a query string
if (empty($this->xnyo->cache->id) && !empty($_SERVER['QUERY_STRING']))
{
if (XNYO_DEBUG) $this->xnyo->d('Unable to cache page with a query string.');
return false;
}
// check to make sure we can write to our cache directory
if (!is_dir($this->xnyo->cache->location) || is_writable($this->xnyo->cache->location))
throw new XnyoError('Cache directory <b>%s</b> does not exist or is not writable.',
$this->xnyo->cache->location);
// get where we're going to put it
$dirs = $this->get_subdirs();
$file = $this->get_file();
// start our location
$loc = substr($this->xnyo->cache->location, -1) == DIRSEP ? $this->xnyo->cache->location :
$this->xnyo->cache->location.DIRSEP;
// check that we can write to each
foreach ($dirs as $d)
{
if (is_dir($loc.$d))
{
$loc .= $d.DIRSEP;
if (!is_writable($loc))
throw new XnyoError('Cache Subdirectory <b>%s</b> already exists but we cannot write to it.',
$loc);
continue;
}
if (!mkdir($loc.$d, 0700))
throw new XnyoError('Unable to create Cache Subdirectory <b>%s</b>.', $loc.$d);
$loc .= $d.DIRSEP;
}
// check to see if our filename already exists..
if (file_exists($loc.$file) && !is_writable($loc.$file))
throw new XnyoError('Cache file <b>%s</b> already exists but we cannot write to it.',
$loc.$file);
// open a pointer and output the cache file
$fp = fopen($loc.$file, 'w');
if (!$fp)
throw new XnyoError('Unable to open file pointer on cache file <b>%s</b>.', $loc.$file);
// calculate expiration/idle times
if (!empty($this->xnyo->cache->expiration))
fputs($fp, 'expiration: '.$this->xnyo->cache->expiration."\n");
elseif ($this->xnyo->cache->lifetime != 0)
fputs($fp, 'expiration: '.(time() + $this->xnyo->cache->lifetime)."\n");
if (!empty($this->xnyo->cache->idletime))
fputs($fp, 'idletime: '.$this->xnyo->cache->idletime."\n");
fputs($fp, 'created: '.time()."\n\n");
// output the page
fputs($fp, $buf);
fclose($fp);
// make sure the access time is right
touch($loc.$file);
// generate Last-Modified headers to make caching nice
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
//done
return true;
}
/**
* Is Page Cached?
**/
public function is_cached ()
{
// check the cache dirs
if (!is_dir($this->xnyo->cache->location) || !is_readable($this->xnyo->cache->location))
throw new XnyoError('Cache directory <b>%s</b> does not exist or is not readable.',
$this->xnyo->cache->location);
// get our subdirs and file
$loc = substr($this->xnyo->cache->location, -1) == DIRSEP ? $this->xnyo->cache->location :
$this->xnyo->cache->location.DIRSEP;
$loc .= join(DIRSEP, $this->get_subdirs()).DIRSEP.$this->get_file();
// check to see if our cache file exists
if (!file_exists($loc))
return false;
// make sure we can read it
if (!is_readable($loc) || is_dir($loc))
throw new XnyoError('Cache file <b>%s</b> exists but we cannot read it (or its a directory).',
$loc);
// store the file access time before we go stuffing around with it
$fileatime = fileatime($loc);
// are we caching a page? if so has the page changed?
if (empty($this->xnyo->cache->id) && filemtime($_SERVER['SCRIPT_FILENAME']) > filemtime($loc))
{
if (XNYO_DEBUG) $this->xnyo->d('Script newer than page, removing cache file <b>%s</b>', $loc);
unlink($loc);
return false;
}
// open file pointer
$fp = fopen($loc, 'r');
if (!$fp)
throw new XnyoError('Unable to open file pointer on cache file <b>%s</b>', $loc);
// read in headers
$h = array();
while ($l = fgets($fp))
{
// end of headers
if ($l == "\n" || $l == "\r\n")
break;
// store header
$header = explode(': ', chop($l));
$h[$header[0]] = $header[1];
}
// have we passed the expiration date?
if (isset($h['expiration']) && time() > $h['expiration'])
{
// yep, close the pointer and prepare to regen the file
fclose($fp);
unlink($loc);
return false;
}
// have we past the idle timeout?
if (isset($h['idletime']) && $fileatime > ($h['idletime'] + $h['created']))
{
// yep regen
fclose($fp);
unlink($loc);
return false;
}
// store the fp and headers for later use
$this->fp = $fp;
$this->headers = $h;
// yep, we're cached!
return true;
}
/**
* Display the cache
**/
public function display_cache ()
{
// haven't run is_cache yet?
if ((!is_resource($this->fp) || !is_array($this->headers)) && !$this->is_cached())
throw new XnyoError('Trying to call display cache when page not cached.');
// support for the If-Modified-Since header
if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']))
{
$mod = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
if ($mod != -1 && $mod >= $h['created'])
{
header('HTTP/1.0 304 Not Modified');
exit();
}
}
// send the Last-Modified header
header('Last-Modified: '.gmdate('D, d M Y H:i:s', $h['created']).' GMT');
// output the rest of the file
fpassthru($fp);
// close it and done
fclose($fp);
return true;
}
/**
* Clear the cache
**/
public function clear_cache ()
{
// check the cache dirs
if (!is_dir($this->xnyo->cache->location) || !is_readable($this->xnyo->cache->location))
throw new XnyoError('Cache directory <b>%s</b> does not exist or is not readable.',
$this->xnyo->cache->location);
// get our subdirs and file
$loc = substr($this->xnyo->cache->location, -1) == DIRSEP ? $this->xnyo->cache->location :
$this->xnyo->cache->location.DIRSEP;
$loc .= join(DIRSEP, $this->get_subdirs()).DIRSEP.$this->get_file();
// check to see if our cache file exists
if (!file_exists($loc))
return false;
return unlink($loc);
}
}
