The PEAR repository is a well-used and well-loved library of reusable Open Source PHP code. Perhaps in recent years it has fallen a bit out of favour and has become somewhat neglected; but there are some useful scripts to be found on there, with one of those being Cache Lite.
This article describes how I implemented Cache Lite with Drupal CMS.
One aspect of Drupal that can give users grief is its cache. The inbuilt Drupal cache is, although better than nothing, dependent on database storage and isn't the best solution. Easily the best solution for top notch Drupal performance is using Alternative PHP Cache (APC), Varnish HTTP accelerator and Memcache for the database. Most major Drupal sites use all or some of these three.
It is unthinkable to run Drupal without a server-side cache as slow load times are detrimental to the user experience, especially for an eCommerce cart where there is a well-proven correlation between fast page load times and happy customers.
However, if you are running Drupal on a shared server then these solutions are not going to be possible to use. There are a number of credible Drupal third-party cache modules such as Boost but I wanted to try Drupal with PEAR Cache Lite.
If I was to build a PHP site from scratch I would consider using PEAR Cache Lite which uses PHP output buffering with OOP.
It's certainly one of the easiest solutions to implement.
Lets take a look at Cache Lite implemented for caching various blocks on the standard Drupal page.tpl.php page:
<?php
require_once (drupal_get_path('module', 'cachelite') . '/Cache/Lite/Output.php');
$id = md5('http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
$options = array(
'cacheDir' => './sites/default/files/cache/',
'lifeTime' => 3600,
'caching' => TRUE
);
/* Set a key for this cache item */
$cache = new Cache_Lite_Output($options);
if ( !($cache->start($id . 'block-one')) ) {
echo '<!-- Served From Cache block one: '. date('l jS of F Y h:i:s A').' -->';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php print $language->language ?>" lang="<?php print $language->language ?>" dir="<?php print $language->dir ?>">
<head>
<?php print $head; ?>
<title><?php print $head_title; ?></title>
<?php print $styles; ?>
<?php print $scripts; ?>
<script type="text/javascript"><?php /* Needed to avoid Flash of Unstyled Content in IE */ ?> </script>
</head>
<body class="<?php print $body_classes; ?>">
<div id="page">
<div id="header">
<div id="logo-title">
<?php if (!empty($logo)): ?>
<a href="<?php print $front_page; ?>" title="<?php print t('Home'); ?>" rel="home" id="logo">
<img src="<?php print $logo; ?>" alt="<?php print t('Home'); ?>" />
</a>
<?php endif; ?>
<?php
$cache->end();
}
?>
<p>This will not be cached</p>
<?php
if ( !($cache->start($id . 'block-two')) ) {
echo '<!-- Served From Cache block two: '. date('l jS of F Y h:i:s A').' -->';
?>
<div id="name-and-slogan">
<?php if (!empty($site_name)): ?>
<h1 id="site-name">
<a href="<?php print $front_page ?>" title="<?php print t('Home'); ?>" rel="home"><span><?php print $site_name; ?></span></a>
</h1>
<?php endif; ?>
<?php if (!empty($site_slogan)): ?>
<div id="site-slogan"><?php print $site_slogan; ?></div>
<?php endif; ?>
</div> <!-- /name-and-slogan -->
</div> <!-- /logo-title -->
<?php if (!empty($search_box)): ?>
<div id="search-box"><?php print $search_box; ?></div>
<?php endif; ?>
<?php if (!empty($header)): ?>
<div id="header-region">
<?php print $header; ?>
</div>
<?php endif; ?>
</div> <!-- /header -->
<?php
$cache->end();
}
?>
<p>This will not be cached</p>
<?php
if ( !($cache->start($id . 'block-three')) ) {
echo '<!-- Served From Cache block three: '. date('l jS of F Y h:i:s A').' -->';
?>
<div id="container" class="clear-block">
<div id="navigation" class="menu <?php if (!empty($primary_links)) { print "withprimary"; } if (!empty($secondary_links)) { print " withsecondary"; } ?> ">
<?php if (!empty($primary_links)): ?>
<div id="primary" class="clear-block">
<?php print theme('links', $primary_links, array('class' => 'links primary-links')); ?>
</div>
<?php endif; ?>
<?php if (!empty($secondary_links)): ?>
<div id="secondary" class="clear-block">
<?php print theme('links', $secondary_links, array('class' => 'links secondary-links')); ?>
</div>
<?php endif; ?>
</div> <!-- /navigation -->
<?php if (!empty($left)): ?>
<div id="sidebar-left" class="column sidebar">
<?php print $left; ?>
</div> <!-- /sidebar-left -->
<?php endif; ?>
<div id="main" class="column"><div id="main-squeeze">
<?php if (!empty($breadcrumb)): ?><div id="breadcrumb"><?php print $breadcrumb; ?></div><?php endif; ?>
<?php if (!empty($mission)): ?><div id="mission"><?php print $mission; ?></div><?php endif; ?>
<?php
$cache->end();
}
?>
<p>This will not be cached</p>
<?php
if ( !($cache->start($id . 'block-four')) ) {
echo '<!-- Served From Cache block four: '. date('l jS of F Y h:i:s A').' -->';
?>
<div id="content">
<?php if (!empty($title)): ?><h1 class="title" id="page-title"><?php print $title; ?></h1><?php endif; ?>
<?php if (!empty($tabs)): ?><div class="tabs"><?php print $tabs; ?></div><?php endif; ?>
<?php if (!empty($messages)): print $messages; endif; ?>
<?php if (!empty($help)): print $help; endif; ?>
<div id="content-content" class="clear-block">
<?php print $content; ?>
</div> <!-- /content-content -->
<?php print $feed_icons; ?>
</div> <!-- /content -->
</div></div> <!-- /main-squeeze /main -->
<?php if (!empty($right)): ?>
<div id="sidebar-right" class="column sidebar">
<?php print $right; ?>
</div> <!-- /sidebar-right -->
<?php endif; ?>
</div> <!-- /container -->
<div id="footer-wrapper">
<div id="footer">
<?php print $footer_message; ?>
<?php if (!empty($footer)): print $footer; endif; ?>
</div> <!-- /footer -->
</div> <!-- /footer-wrapper -->
<?php print $closure; ?>
</div> <!-- /page -->
</body>
</html>
<?php
$cache->end();
}
?>
At the top of the page the require_once is used to link to the Cache Lite PHP file that is in the module; cacheDir is the folder that needs to be manually created and where the cache files will be stored; lifeTime is the amount of seconds that the cache will be kept and caching can either be TRUE or FALSE.
For a full list of parameters read the manual: http://pear.php.net/manual/en/package.caching.cache-lite.cache-lite.cache-lite.php
From there the first cache block is built. The $id I used was a uniquely generated number for every individual page plus the block number for every section. I also added a time stamp so that it can be determined whether the section has been cached or not. In the example above the areas not being cached are clearly labelled.
There is no mechanism for flushing the cache and that needs to be integrated with the administration section. The code below will do that:
<?php
// $ID$
// Andy Walpole andy@suburban-glory.com January 23 2010
// Sets permissions
function cachelite_perm()
{
return array(
'cachelite module'
);
}
// Implementation of hook_menu()
function cachelite_menu()
{
$items = array();
$items['admin/settings/cachelite'] = array(
// Title of module as it appears on the <title> tags
'title' => t('Cache Lite Settings'),
// Appears at the top of the screen
'description' => t('Configuration options for the Cache Lite module'),
// Function that is called when the page is loaded
'page callback' => 'cachelite_admin_form',
// find more about access callback
'access callback' => 'user_access',
'access arguments' => array(
'administer site configuration'
)
);
return $items;
}
function cachelite_admin_form()
{
// call the function below
return drupal_get_form('cachelite_form');
}
function cachelite_form()
{
$form = array();
// This below creates a fieldset for the submit button
$form['fieldset-one'] = array(
'#type' => 'fieldset',
'#title' => t('Flush the cache here')
);
$form['fieldset-one']['submit'] = array(
// creates the submit button
'#type' => 'submit',
'#value' => 'Submit'
);
return $form;
}
// Presents message on successful completion of email
function cachelite_form_submit($form_id, &$form_state)
{
$result = "";
require_once(drupal_get_path('module', 'cachelite') . '/Cache/Lite/Output.php');
$options = array(
'cacheDir' => './sites/default/files/cache/'
);
$cache = new Cache_Lite($options);
$cache->clean();
$result = true;
if ($result == true) {
drupal_set_message(t('The cache has been flushed'));
} else {
drupal_set_message(t('There have been problems with the cache'));
}
} // end cachelite_form_submit
The code above is standard Drupal module creation code and uses hook_perm() and hook_menu() as well as the Form API. In the admin section it is now possible to flush the cache with just a quick form submission.
But this still leaves the problem of old pages from the cache being called despite user interaction. For instance, if you want to show a message after the user has submitted the form this will not be possible with the current set up because it will pull an old page from the cache.
So I added a bit more code to the module file:
function cachelite_form_alter(&$form, $form_state, $form_id)
{
$key = $form_id;
// Flushes the cahce on submitting a button - clumsy
$form['#submit'][] = 'cache_flush';
switch ($key) {
case 'uc_cart_view_form':
$form['checkout']['#submit'][] = 'cache_flush';
$form['update']['#submit'][] = 'cache_flush';
break;
}
} // end cachelit_form_alter
// Presents message on successful completion of email
function cache_flush()
{
$result = "";
require_once(drupal_get_path('module', 'cachelite') . '/Cache/Lite/Output.php');
$options = array(
'cacheDir' => './sites/default/files/cache/'
);
$cache = new Cache_Lite($options);
$cache->clean();
} // end cache_flush
Whenever a user submits a form the cache is now flushed. It's a clumsy, primitive way of regenerating the cache but it is okay for now.
So above is an implementation of PEAR Cache Lite for Drupal.
BUT testing the page rendering times with Apache benchmark it is apparent that there is hardly any gain by using Cache Lite with Drupal – at least, not the class I have used.
So in conclusion Cache Lite is... useless with Drupal. LOL! Oh well, it doesn't hurt to try these things out.
The full code including that of Cache Lite is in a module which you can download here. Be my guest if you want to persist with this experiment.
For more info on Cache Lite then read: Speedup your website with Cache_Lite and Cache it! Solve PHP Performance Problems.