Simple PHP Cacheing With Mixed Static and Dynamic Content

One way to speed up your development time and website is to use cache in PHP. The method I use is made to respect Rapid CAAR Development. It doesn’t require any external libraries and will work on the vast majority of web hosts (even the really cheap ones).

Download PHP Caching

PHP cache can greatly reduce server load and let you get those few thousand extra page views out of your web host, with only adding few a few lines of code to your source.

Useing The PHP Cache

<?php
 
// these two files are in the store zip file
require("./includes/conf.inc.php");
require("./includes/debug.inc.php");
 
if(CACHE_ENABLE == 1){
  include("./includes/cache.inc.php");
}else{  
  function cache_start(){return True;};
  function cache_end(){};
}
 
?>
<html>
  <head>
    <title>Zoooom!</title>
   <head>
   <body>
<?php
 
echo "Hello: {$_SESSION['user_name']} <br>";
 
if(cache_start()){
  echo "Here is the results of a long running Problem";
  solve_riemann_hypothesis_numerically();
}cache_end();
 
echo "Here is some not cached info {$_SESSION['user_name']}";
 
if(cache_start()){
 echo "But here is something else that is cached";
 poincare_conjecture(new Geometry('Narwhal Tooth'));
}cache_end();
?>
   </body>
</html>

For this to work you need to have a file called cache/ which is in the same directory as the includes/ directory. The cache/ folder must be read and writable by use that Apache is running as.

The directory

% cd /path/to/yoursite.faux/
% whoami
apache 
% ls
-r-------- apache apache index.php
dr-x------ apache apache includes/
drwx------ apache apache cache/

Its pretty straight forward to use really. Just include the file and make sure cache/ is readable.

To clear cache you visit http://yoursite.faux/?clear_cache=clear you can set the “password” in the includes/conf.inc.php file. In this example the password is clear.

There are other configuration options in there as well that you can use if you need them.

Now all thats left is to download the code. There are some other dirs in there as well because this is taken directly from my foundation sites creating svn repository.

Download PHP Caching

Now on to the Code

The work horse for the cache is the output buffering ob_* built in PHP functions. Namely ob_start(),ob_get_contents() and ob_get_contents() and ob_end_flush().

If you are not familiar with those functions you should check out the documentation after reading over this code.

Before we get into the main code it think it will help to look at the relevant configuration file options.

DFHU Conf for Cache (includes/conf.inc.php)

 
<?php
// Cache Variables
define("CACHE_ENABLE",1);
// http://yoursite.faux/?clear_cache=clear will delete the cache 
define("CACHE_CLEAR_PASSWORD","clear");
// set this to 0 if your site doesn't use conical urls (bad idea)
define("CACHE_CONICAL_URLS", 1);
// higher means call cache open, low means call cache rarely
define("CACHE_CALL_PROBABILITY",90);
define("CACHE_NAME_SALT","Put Random String Here");
?>

And now onto the main caching functions for PHP.

DFHU PHP Cache Functions (includes/cache.inc.php)

 
<?php
/*
 
@author: Victory
@site: http://dfhu.org
@copyright: dfhu.org
@report_bugs: bugs(at)dfhu.org
@feature_request: features(at)dfhu.org
@file: dfhufoundation/includes/cache.inc.php
@license: BSD
 
@description:
 
  This is a function to accomplish caching. PHP5
 
  The cache dir should be set to ../cache/ and should be read and
  writable by Apache.
 
 
 
$Date:: 2009-08-05 12:01:59 #$:
$Rev:: 4                     $:
 
*/
 
// The cache number is incremented, everytime you call cache_start();
global $CACHE_NUMBER;
$CACHE_NUMBER=0; 
 
global $CACHE_COLLECTING;
$CACHE_COLLECTING=False;
 
// I know where you live cache dir, and realpath doesn't do what you
// want if the cache dir doesn't exists in PHP<5.3.
global $CACHE_DIR;
$CACHE_DIR=preg_replace(";/includes$;","/cache/",dirname(__FILE__));
 
 
// We need a variable to see if we should try to pull the cache or
// should we recalculate the contents of the cache. We call this
// variable $CACHE_CALL_THIS_TIME and we ...
global $CACHE_CALL_THIS_TIME;
// ... set it to true the probablity a random number is less than the
// probablity expressed as percentage with three sig figs.
$cache_call_probability = 
  min(995,CACHE_CALL_PROBABILITY*10);
if(rand(0,1000) < $cache_call_probability){
  $CACHE_CALL_THIS_TIME=True;
}else{
  $CACHE_CALL_THIS_TIME=False;
}
 
 
 
// Make sure the directory is writable
if(!is_dir($CACHE_DIR) or
   !is_writable($CACHE_DIR)){
 
  // This is my here() function it is defined in debug.inc.php. It
  // prints the message and a backtrace
  here("ERROR: cache dir won't let me write: $CACHE_DIR");
  exit;
}
 
// if we were sent something like
// http://mysite.faux/?clear_cache=thepassword_set_in_conf ...
if(isset($_GET['clear_cache']) and 
   $_GET['clear_cache'] == CACHE_CLEAR_PASSWORD){
  // ... then we delete ALL the '.cache' files in cache_dir
 
  // To do that we open up the dir (which we know is valid because we
  // did a is_dir/is_writable above) and ...
  $d = dir($CACHE_DIR);
  // ... iterate over all the files in the dir
  while (false !== ($entry = $d->read())) {
    // ... and if they are cache files ...
    if(!preg_match("/\.cache$/",$entry))
      continue;
    // ... then we can delete them (either using a loud and obnoxious
    // version or a soft and sneaky version)
    if(defined(DEBUG_LEVEL) and DEBUG_LEVEL > 2){
      echo "<br>deleting: " . $d->path . $entry . "<br>";
      unlink($d->path . $entry);
    }else{
      // The @ means i am sneaky i can fail an no one will ever know!
      @unlink($d->path . $entry);
    }
  }
  // Time to close up shop senior Dir.
  $d->close();
}// The end of delete all .cache files.
 
 
function get_cache_name(){
  // Create a filename for the cache files, using the REQUEST_URI, It
  // turns out to be something like return
  // md5($request.$salt).$extension;
 
  // We use the cache number to see which section of the page this
  // cache block is associated with. If you start adding random
  // cache_start blocks without clearing cache you will know why all
  // the WTF Bunnies are chewing you to bits.
  global $CACHE_NUMBER;
 
  // We create the extension for cache file.
  $cache_extension="-$CACHE_NUMBER.cache";
  // And shake out the salt.
  $salt=CACHE_NAME_SALT;
 
  // Now if you are enlightened you will use, conical urls and thus
  // will want to ...
  if(CACHE_CONICAL_URLS == 1){
    // ... parse the REQUEST_URI so that you can ...
    $url_bits=parse_url($_SERVER['REQUEST_URI']);
    // ... cosider just the path when ...
    $path=$url_bits['path'];
    // ... creating and returning the cache name.
    return md5($path.$salt) . $cache_extension;
  }
 
  // Apparently you have some good reason for not having conical
  // urls. I am guessing you are trying some SEO reverse psychology on
  // Google, let me know how that works out for you. /snideRemark
  return md5($_SERVER['REQUEST_URI'].$salt) . $cache_extension;  
}
 
function cache_start(){
  // This function, cache_start(), either starts output buffering or
  // reads outputs the cache by reading from the .cache file
 
 
  // This is set to true if we have started output buffering and false
  // otherwise.
  global $CACHE_COLLECTING;
  // The place on the page for this section of cache.
  global $CACHE_NUMBER;
  // This is the directory where the cache is stored.
  global $CACHE_DIR;
  // This is True if we should try to pull cache from the file or it
  // is false if we should re-populate the cache.
  global $CACHE_CALL_THIS_TIME;
 
 
  // Ok lets update the cache index for the page.
  $CACHE_NUMBER+=1;
 
  // construct the absolute path to the appropriate cache file.
  $cache_name=$CACHE_DIR . get_cache_name();
 
 
  // Should we try to pull the cache from the file this time and is it
  // writable if so than ...
  if($CACHE_CALL_THIS_TIME and
     is_readable($cache_name)){
    // ... we just dump the results of the file to the screen, its
    // very tempting to use include but that would be a security
    // nightmear, file_get_contents isn't super meaga ultra fast so
    // maybe someone could load the file another way.
    echo file_get_contents($cache_name);
    return False;
  }
 
  /* NOTE: You could have done something like the following if you
           wanted have the cache file update according to its age, but
           i am not really into that kind of crazyness.
 
     $five_minutes_old=time() - 60*5;
     if($five_minutes_old > filemtime($cache_name) and
        is_readable($cache_name)){
 
     }
  */
 
  // Set the global cache to know that we should be collecting text
  // and be ready to cache it when call cache_end();
  $CACHE_COLLECTING=True;  
  ob_start();
  return True;
}
 
function cache_end(){
  global $CACHE_COLLECTING;
 
  // If we were not collecting cache before, than just return.
  if(!$CACHE_COLLECTING){
    return;
  }
 
  // We are going to process the cache so turn off the cache
  // collecting flag.
  $CACHE_COLLECTING=False;
 
  // Now we need to get all the contents of buffer and store it in a
  // text file.
  cache_store(ob_get_contents());
 
  // finally we want to output the cache or else we will have some
  // confused surfers.
  ob_end_flush();
}
 
function cache_store($cache_contents){
  global $CACHE_DIR;
 
  // open up the cache dir and and store the contents of the output
  // buffer in it.
  $cache_name=$CACHE_DIR . get_cache_name();
  $fp=fopen($cache_name,'w');
  fwrite($fp,$cache_contents);
  fclose($fp);
}
 
?>

You should use cache whenever it will reduce the amount of time you need to think about optimizations for your code, or when your code is slow or when it will save you bandwidth (for instance its perfect for sites that use ccenter).

Share
This entry was posted in intermediate.programming and tagged , , , , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.
1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">

If you are going to post code please use:
<pre lang="php" escaped="true"> YOUR_CODE_HERE </pre>

Change the lang to mysql, python, lisp, whatever. This will escape your code.