[Patch] Cache handler (diff)
Trevor Burnham <[email protected]> Sun, 15 Oct 2006 20:11:38 -0600
| Newsgroups | gmane.comp.php.smarty.devel |
|---|---|
| Message-ID | <[email protected]> |
Sorry, the .diff didn't go thorugh; I've given it a .txt extension. Trevor Burnham -- Smarty Development Mailing List (http://smarty.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php
smarty.diff.txt
(text/plain, 26.9 KB)
? .DS_Store
? smarty.diff
? libs/.DS_Store
? libs/internals/.DS_Store
? libs/plugins/.DS_Store
? plugins/.DS_Store
Index: libs/Smarty.class.php
===================================================================
RCS file: /repository/smarty/libs/Smarty.class.php,v
retrieving revision 1.525
diff -u -r1.525 Smarty.class.php
--- libs/Smarty.class.php 28 May 2006 17:35:05 -0000 1.525
+++ libs/Smarty.class.php 15 Oct 2006 23:51:29 -0000
@@ -324,11 +324,21 @@
var $default_resource_type = 'file';
/**
- * The function used for cache file handling. If not set, built-in caching is used.
+ * The function used for cache handling. If not set, built-in caching is
+ * used. As of v2.6.15, this may be an array of cache handlers. In the
+ * event of a read, each is tried in sequence. In the event of a write or
+ * a clear, all of the handlers in the array are called. Smarty's built-in
+ * cache handler may be included in the array as
+ * 'Smarty::file_cache_handler'.
+ *
+ * Using an array for this method allows for performance improvement by
+ * using a volatile (memory-based) cache in conjunction with a reliable
+ * (disk-based) cache. Obviously, the cache handlers should be listed in
+ * the array in order from fastest to slowest.
*
- * @var null|string function name
+ * @var null|string|array function name(s)
*/
- var $cache_handler_func = null;
+ var $cache_handler_func = 'Smarty::file_cache_handler';
/**
* This indicates which filters are automatically loaded into Smarty.
@@ -946,18 +956,8 @@
$_auto_id = $this->_get_auto_id($cache_id, $compile_id);
- if (!empty($this->cache_handler_func)) {
- return call_user_func_array($this->cache_handler_func,
- array('clear', &$this, &$dummy, $tpl_file, $cache_id, $compile_id, $exp_time));
- } else {
- $_params = array('auto_base' => $this->cache_dir,
- 'auto_source' => $tpl_file,
- 'auto_id' => $_auto_id,
- 'exp_time' => $exp_time);
- require_once(SMARTY_CORE_DIR . 'core.rm_auto.php');
- return smarty_core_rm_auto($_params, $this);
- }
-
+ return $this->_cache_clear($tpl_file, $cache_id, $compile_id,
+ $exp_time);
}
@@ -986,16 +986,7 @@
if (!$this->caching)
return false;
- if (!isset($compile_id))
- $compile_id = $this->compile_id;
-
- $_params = array(
- 'tpl_file' => $tpl_file,
- 'cache_id' => $cache_id,
- 'compile_id' => $compile_id
- );
- require_once(SMARTY_CORE_DIR . 'core.read_cache_file.php');
- return smarty_core_read_cache_file($_params, $this);
+ return $this->_cache_exists($tpl_file, $cache_id, $compile_id);
}
@@ -1165,15 +1156,14 @@
// save old cache_info, initialize cache_info
array_push($_cache_info, $this->_cache_info);
$this->_cache_info = array();
- $_params = array(
- 'tpl_file' => $resource_name,
- 'cache_id' => $cache_id,
- 'compile_id' => $compile_id,
- 'results' => null
- );
- require_once(SMARTY_CORE_DIR . 'core.read_cache_file.php');
- if (smarty_core_read_cache_file($_params, $this)) {
- $_smarty_results = $_params['results'];
+
+ /* _cache_read() fills the $_smarty_results var and, if the cache
+ * handler supports it, additional info in $this->_cache_info.
+ */
+ $_smarty_results = null;
+ if ($this->_cache_read($_smarty_results, $resource_name,
+ $cache_id, $compile_id))
+ {
if (!empty($this->_cache_info['insert_tags'])) {
$_params = array('plugins' => $this->_cache_info['insert_tags']);
require_once(SMARTY_CORE_DIR . 'core.load_plugins.php');
@@ -1188,7 +1178,6 @@
$_smarty_results = smarty_core_process_compiled_include($_params, $this);
}
-
if ($display) {
if ($this->debugging)
{
@@ -1229,6 +1218,7 @@
return $_smarty_results;
}
} else {
+ // data not found in the cache
$this->_cache_info['template'][$resource_name] = true;
if ($this->cache_modified_check && $display) {
header('Last-Modified: '.gmdate('D, d M Y H:i:s', time()).' GMT');
@@ -1273,12 +1263,8 @@
}
if ($this->caching) {
- $_params = array('tpl_file' => $resource_name,
- 'cache_id' => $cache_id,
- 'compile_id' => $compile_id,
- 'results' => $_smarty_results);
- require_once(SMARTY_CORE_DIR . 'core.write_cache_file.php');
- smarty_core_write_cache_file($_params, $this);
+ $this->_cache_write($_smarty_results, $resource_name, $cache_id,
+ $compile_id);
require_once(SMARTY_CORE_DIR . 'core.process_cached_inserts.php');
$_smarty_results = smarty_core_process_cached_inserts($_params, $this);
@@ -1914,6 +1900,310 @@
/**
+ * call the cache handler function(s) to see if a cache exists
+ *
+ * If there are multiple cache handlers, they are tried in order until one
+ * gives a true or false response. A handler may return a value of 'defer'
+ * in order to indicate that the next handler should be tried. The last
+ * cache handler tried is given the action 'check_final', which disallows
+ * a defer response.
+ *
+ * Note that older function handlers may raise an error when called with a
+ * 'check' or 'check_final' action, or may simply return false without
+ * performing any action.
+ *
+ * @param string $tpl_file name of template file
+ * @param string $cache_id name of cache_id
+ * @param string $compile_id name of compile_id
+ * @return boolean true if the cache exists (see above), false otherwise
+ */
+ function _cache_exists($tpl_file, $cache_id, $compile_id)
+ {
+ if (!isset($compile_id))
+ $compile_id = $this->compile_id;
+
+ $cache_handler = $this->cache_handler_func;
+ if (!is_array($cache_handler))
+ $cache_handler = array($cache_handler);
+
+ /* Test the caches until one says true or false. */
+ $last_func = array_pop($cache_handler);
+ foreach($cache_handler as $func) {
+ $exists = $this->_call_function($func, array('check', $this,
+ null, $tpl_file, $cache_id,
+ $compile_id, $exp_time));
+ if ($exists == true || $exists == false)
+ return $exists;
+ }
+ return $this->_call_function($last_func, array('check_final', $this,
+ null, $tpl_file, $cache_id, $compile_id,
+ $exp_time));
+ }
+
+ /**
+ * call the cache handler function(s) for a clear operation
+ *
+ * If there are multiple cache handlers, they are all called sequentially
+ * in reverse order.
+ *
+ * @param string $tpl_file name of template file
+ * @param string $cache_id name of cache_id
+ * @param string $compile_id name of compile_id
+ * @param numeric $exp_time
+ * @return boolean true if all caches were cleared, false otherwise.
+ */
+ function _cache_clear($tpl_file, $cache_id, $compile_id, $exp_time)
+ {
+ $cache_handler = $this->cache_handler_func;
+ if (!is_array($cache_handler))
+ $cache_handler = array($cache_handler);
+ else
+ $cache_handler = array_reverse($cache_handler);
+
+ /* We attempt to clear all caches. */
+ $success = true;
+ foreach($cache_handler as $func) {
+ $cleared = $this->_call_function($func, array('clear', $this,
+ null, $tpl_file, $cache_id,
+ $compile_id, $exp_time));
+ if (! $cleared)
+ $success = false;
+ }
+
+ // True is returned only if all caches reported that they cleared.
+ return $success;
+ }
+
+
+ /**
+ * call the cache handler function(s) for a read operation
+ *
+ * If there are multiple cache handlers, they are attempted sequentially
+ * until ones gives a positive result.
+ *
+ * @param string results data from the cache
+ * @param string $tpl_file name of template file
+ * @param string $cache_id name of cache_id
+ * @param string $compile_id name of compile_id
+ * @return boolean true if the cache reported success, false otherwise.
+ */
+ function _cache_read(&$results, $tpl_file, $cache_id, $compile_id,
+ $exp_time = null)
+ {
+ if ($this->force_compile) {
+ // force compile enabled, always regenerate
+ return false;
+ }
+
+ $cache_handler = $this->cache_handler_func;
+ if (!is_array($cache_handler))
+ $cache_handler = array($cache_handler);
+
+ /* We try all caches in sequence until we get a result. */
+ foreach($cache_handler as $func) {
+ $success = $this->_call_function($func, array('read', $this, &$results,
+ $tpl_file, $cache_id, $compile_id,
+ $exp_time));
+ if ($success)
+ break;
+ }
+
+ if (! $success)
+ return false;
+
+ // process inserts and other metadata
+ $_info_start = strpos($results, "\n") + 1;
+ $_info_len = (int)substr($results, 0, $_info_start - 1);
+ $_cache_info = unserialize(substr($results, $_info_start, $_info_len));
+ $results = substr($results, $_info_start + $_info_len);
+
+ if ($this->caching == 2 && isset ($_cache_info['expires'])){
+ // caching by expiration time
+ if ($_cache_info['expires'] > -1 && (time() > $_cache_info['expires'])) {
+ // cache expired, regenerate
+ return false;
+ }
+ } else {
+ // caching by lifetime
+ if ($this->cache_lifetime > -1 && (time() - $_cache_info['timestamp'] > $this->cache_lifetime)) {
+ // cache expired, regenerate
+ return false;
+ }
+ }
+
+ if ($this->compile_check) {
+ $_params = array('get_source' => false, 'quiet'=>true);
+ foreach (array_keys($_cache_info['template']) as $_template_dep) {
+ $_params['resource_name'] = $_template_dep;
+ if (!$this->_fetch_resource_info($_params) || $_cache_info['timestamp'] < $_params['resource_timestamp']) {
+ // template file has changed, regenerate cache
+ return false;
+ }
+ }
+
+ if (isset($_cache_info['config'])) {
+ $_params = array('resource_base_path' => $this->config_dir, 'get_source' => false, 'quiet'=>true);
+ foreach (array_keys($_cache_info['config']) as $_config_dep) {
+ $_params['resource_name'] = $_config_dep;
+ if (!$this->_fetch_resource_info($_params) || $_cache_info['timestamp'] < $_params['resource_timestamp']) {
+ // config file has changed, regenerate cache
+ return false;
+ }
+ }
+ }
+ }
+
+ $content_cache[$tpl_file.','.$cache_id.','.$compile_id] = array($results, $_cache_info);
+ $this->_cache_info = $_cache_info;
+
+ return true;
+ }
+
+
+ /**
+ * call the cache handler function(s) for a write operation
+
+ * If there are multiple cache handlers, they are all called sequentially
+ * in reverse order.
+ *
+ * @param string $cache_content data to put in the cache
+ * @param string $tpl_file name of template file
+ * @param string $cache_id name of cache_id
+ * @param string $compile_id name of compile_id
+ * @return boolean true if all caches reported success, false otherwise.
+ */
+ function _cache_write($cache_content, $tpl_file, $cache_id, $compile_id)
+ {
+ $cache_handler = $this->cache_handler_func;
+ if (!is_array($cache_handler))
+ $cache_handler = array($cache_handler);
+ else
+ $cache_handler = array_reverse($cache_handler);
+
+ // prepend metadata to $cache_content
+
+ $this->_cache_info['timestamp'] = time();
+ if ($this->cache_lifetime > -1){
+ // expiration set
+ $this->_cache_info['expires'] = $this->_cache_info['timestamp'] + $this->cache_lifetime;
+ } else {
+ // cache will never expire
+ $this->_cache_info['expires'] = -1;
+ }
+
+ // collapse nocache.../nocache-tags
+ if (preg_match_all('!\{(/?)nocache\:[0-9a-f]{32}#\d+\}!', $cache_content, $match, PREG_PATTERN_ORDER)) {
+ // remove everything between every pair of outermost noache.../nocache-tags
+ // and replace it by a single nocache-tag
+ // this new nocache-tag will be replaced by dynamic contents in
+ // smarty_core_process_compiled_includes() on a cache-read
+
+ $match_count = count($match[0]);
+ $results = preg_split('!(\{/?nocache\:[0-9a-f]{32}#\d+\})!', $cache_content, -1, PREG_SPLIT_DELIM_CAPTURE);
+
+ $level = 0;
+ $j = 0;
+ for ($i=0, $results_count = count($results); $i < $results_count && $j < $match_count; $i++) {
+ if ($results[$i] == $match[0][$j]) {
+ // nocache tag
+ if ($match[1][$j]) { // closing tag
+ $level--;
+ unset($results[$i]);
+ } else { // opening tag
+ if ($level++ > 0) unset($results[$i]);
+ }
+ $j++;
+ } elseif ($level > 0) {
+ unset($results[$i]);
+ }
+ }
+ $cache_content = implode('', $results);
+ }
+ $this->_cache_info['cache_serials'] = $this->_cache_serials;
+
+ // prepend the cache header info into cache file
+ $_cache_info = serialize($this->_cache_info);
+ $cache_content = strlen($_cache_info) . "\n" . $_cache_info . $cache_content;
+
+ /* We attempt to write to all caches. */
+ $success = true;
+ foreach($cache_handler as $func) {
+ $written = $this->_call_function($func, array('write', $this,
+ $cache_content, $tpl_file,
+ $cache_id, $compile_id, null));
+ if (! $written)
+ $success = false;
+ }
+
+ // True is returned only if all caches reported success.
+ return $success;
+ }
+
+ /**
+ * static function that serves as the default cache handler
+ * @param string action 'read', 'write', 'check', 'check_final' or 'clear'.
+ * @param Smarty smarty_obj
+ * @param string cache_content
+ * @param string tpl_file
+ * @param string cache_id
+ * @param string compile_id
+ * @param null exp_time
+ * @return boolean true on success, false otherwise.
+ */
+ static function file_cache_handler($action, &$smarty_obj, &$cache_content,
+ $tpl_file=null, $cache_id=null, $compile_id=null, $exp_time=null)
+ {
+ $auto_id = $smarty_obj->_get_auto_id($cache_id, $compile_id);
+
+ switch ($action) {
+ case 'check':
+ case 'check_final':
+ $_params = array('tpl_file' => $tpl_file,
+ 'cache_id' => $cache_id,
+ 'compile_id' => $compile_id);
+ require_once(SMARTY_CORE_DIR . 'core.read_cache_file.php');
+ return smarty_core_read_cache_file($_params, $smarty_obj);
+ case 'clear':
+ $_params = array('auto_base' => $smarty_obj->cache_dir,
+ 'auto_source' => $tpl_file,
+ 'auto_id' => $auto_id,
+ 'exp_time' => $exp_time);
+ require_once(SMARTY_CORE_DIR . 'core.rm_auto.php');
+ return smarty_core_rm_auto($_params, $smarty_obj);
+ case 'read':
+ $_params = array('results' => &$cache_content,
+ 'tpl_file' => $tpl_file,
+ 'cache_id' => $cache_id,
+ 'compile_id' => $compile_id);
+ require_once(SMARTY_CORE_DIR . 'core.read_cache_file.php');
+ return smarty_core_read_cache_file($_params, $smarty_obj);
+ case 'write':
+ $_params = array('tpl_file' => $tpl_file,
+ 'cache_id' => $cache_id,
+ 'compile_id' => $compile_id,
+ 'results' => $cache_content);
+ require_once(SMARTY_CORE_DIR . 'core.write_cache_file.php');
+ return smarty_core_write_cache_file($_params, $smarty_obj);
+ }
+ }
+
+ /**
+ * wrapper for call_user_func_array() that parses class function and
+ * object method calls
+ * @param string function
+ * @param array params
+ * @return mixed
+ */
+ function _call_function(&$function, $params) {
+ if (is_array($function)) {
+ // already an array, perform no action
+ } elseif (strstr($function, '::') != false) {
+ $function = explode('::', $function); // class static function call
+ }
+ return call_user_func_array($function, $params);
+ }
+
+ /**
* wrapper for include() retaining $this
* @return mixed
*/
@@ -1936,7 +2226,6 @@
return eval($code);
}
/**#@-*/
-
}
/* vim: set expandtab: */
Index: libs/internals/core.read_cache_file.php
===================================================================
RCS file: /repository/smarty/libs/internals/core.read_cache_file.php,v
retrieving revision 1.3
diff -u -r1.3 core.read_cache_file.php
--- libs/internals/core.read_cache_file.php 8 Jul 2005 18:11:22 -0000 1.3
+++ libs/internals/core.read_cache_file.php 15 Oct 2006 23:51:29 -0000
@@ -22,77 +22,22 @@
{
static $content_cache = array();
- if ($smarty->force_compile) {
- // force compile enabled, always regenerate
- return false;
- }
-
+ // if this cache file has already been read during this script, don't read it again
if (isset($content_cache[$params['tpl_file'].','.$params['cache_id'].','.$params['compile_id']])) {
list($params['results'], $smarty->_cache_info) = $content_cache[$params['tpl_file'].','.$params['cache_id'].','.$params['compile_id']];
return true;
}
- if (!empty($smarty->cache_handler_func)) {
- // use cache_handler function
- call_user_func_array($smarty->cache_handler_func,
- array('read', &$smarty, &$params['results'], $params['tpl_file'], $params['cache_id'], $params['compile_id'], null));
- } else {
- // use local cache file
- $_auto_id = $smarty->_get_auto_id($params['cache_id'], $params['compile_id']);
- $_cache_file = $smarty->_get_auto_filename($smarty->cache_dir, $params['tpl_file'], $_auto_id);
- $params['results'] = $smarty->_read_file($_cache_file);
- }
+ // read from the cache file
+ $_auto_id = $smarty->_get_auto_id($params['cache_id'], $params['compile_id']);
+ $_cache_file = $smarty->_get_auto_filename($smarty->cache_dir, $params['tpl_file'], $_auto_id);
+ $params['results'] = $smarty->_read_file($_cache_file);
if (empty($params['results'])) {
// nothing to parse (error?), regenerate cache
return false;
}
- $_contents = $params['results'];
- $_info_start = strpos($_contents, "\n") + 1;
- $_info_len = (int)substr($_contents, 0, $_info_start - 1);
- $_cache_info = unserialize(substr($_contents, $_info_start, $_info_len));
- $params['results'] = substr($_contents, $_info_start + $_info_len);
-
- if ($smarty->caching == 2 && isset ($_cache_info['expires'])){
- // caching by expiration time
- if ($_cache_info['expires'] > -1 && (time() > $_cache_info['expires'])) {
- // cache expired, regenerate
- return false;
- }
- } else {
- // caching by lifetime
- if ($smarty->cache_lifetime > -1 && (time() - $_cache_info['timestamp'] > $smarty->cache_lifetime)) {
- // cache expired, regenerate
- return false;
- }
- }
-
- if ($smarty->compile_check) {
- $_params = array('get_source' => false, 'quiet'=>true);
- foreach (array_keys($_cache_info['template']) as $_template_dep) {
- $_params['resource_name'] = $_template_dep;
- if (!$smarty->_fetch_resource_info($_params) || $_cache_info['timestamp'] < $_params['resource_timestamp']) {
- // template file has changed, regenerate cache
- return false;
- }
- }
-
- if (isset($_cache_info['config'])) {
- $_params = array('resource_base_path' => $smarty->config_dir, 'get_source' => false, 'quiet'=>true);
- foreach (array_keys($_cache_info['config']) as $_config_dep) {
- $_params['resource_name'] = $_config_dep;
- if (!$smarty->_fetch_resource_info($_params) || $_cache_info['timestamp'] < $_params['resource_timestamp']) {
- // config file has changed, regenerate cache
- return false;
- }
- }
- }
- }
-
- $content_cache[$params['tpl_file'].','.$params['cache_id'].','.$params['compile_id']] = array($params['results'], $_cache_info);
-
- $smarty->_cache_info = $_cache_info;
return true;
}
Index: libs/internals/core.write_cache_file.php
===================================================================
RCS file: /repository/smarty/libs/internals/core.write_cache_file.php,v
retrieving revision 1.4
diff -u -r1.4 core.write_cache_file.php
--- libs/internals/core.write_cache_file.php 1 Feb 2005 10:19:08 -0000 1.4
+++ libs/internals/core.write_cache_file.php 15 Oct 2006 23:51:29 -0000
@@ -20,75 +20,24 @@
function smarty_core_write_cache_file($params, &$smarty)
{
+ //write to the cache file
- // put timestamp in cache header
- $smarty->_cache_info['timestamp'] = time();
- if ($smarty->cache_lifetime > -1){
- // expiration set
- $smarty->_cache_info['expires'] = $smarty->_cache_info['timestamp'] + $smarty->cache_lifetime;
- } else {
- // cache will never expire
- $smarty->_cache_info['expires'] = -1;
- }
-
- // collapse nocache.../nocache-tags
- if (preg_match_all('!\{(/?)nocache\:[0-9a-f]{32}#\d+\}!', $params['results'], $match, PREG_PATTERN_ORDER)) {
- // remove everything between every pair of outermost noache.../nocache-tags
- // and replace it by a single nocache-tag
- // this new nocache-tag will be replaced by dynamic contents in
- // smarty_core_process_compiled_includes() on a cache-read
-
- $match_count = count($match[0]);
- $results = preg_split('!(\{/?nocache\:[0-9a-f]{32}#\d+\})!', $params['results'], -1, PREG_SPLIT_DELIM_CAPTURE);
-
- $level = 0;
- $j = 0;
- for ($i=0, $results_count = count($results); $i < $results_count && $j < $match_count; $i++) {
- if ($results[$i] == $match[0][$j]) {
- // nocache tag
- if ($match[1][$j]) { // closing tag
- $level--;
- unset($results[$i]);
- } else { // opening tag
- if ($level++ > 0) unset($results[$i]);
- }
- $j++;
- } elseif ($level > 0) {
- unset($results[$i]);
- }
- }
- $params['results'] = implode('', $results);
- }
- $smarty->_cache_info['cache_serials'] = $smarty->_cache_serials;
-
- // prepend the cache header info into cache file
- $_cache_info = serialize($smarty->_cache_info);
- $params['results'] = strlen($_cache_info) . "\n" . $_cache_info . $params['results'];
-
- if (!empty($smarty->cache_handler_func)) {
- // use cache_handler function
- call_user_func_array($smarty->cache_handler_func,
- array('write', &$smarty, &$params['results'], $params['tpl_file'], $params['cache_id'], $params['compile_id'], null));
- } else {
- // use local cache file
-
- if(!@is_writable($smarty->cache_dir)) {
- // cache_dir not writable, see if it exists
- if(!@is_dir($smarty->cache_dir)) {
- $smarty->trigger_error('the $cache_dir \'' . $smarty->cache_dir . '\' does not exist, or is not a directory.', E_USER_ERROR);
- return false;
- }
- $smarty->trigger_error('unable to write to $cache_dir \'' . realpath($smarty->cache_dir) . '\'. Be sure $cache_dir is writable by the web server user.', E_USER_ERROR);
+ if(!@is_writable($smarty->cache_dir)) {
+ // cache_dir not writable, see if it exists
+ if(!@is_dir($smarty->cache_dir)) {
+ $smarty->trigger_error('the $cache_dir \'' . $smarty->cache_dir . '\' does not exist, or is not a directory.', E_USER_ERROR);
return false;
}
-
- $_auto_id = $smarty->_get_auto_id($params['cache_id'], $params['compile_id']);
- $_cache_file = $smarty->_get_auto_filename($smarty->cache_dir, $params['tpl_file'], $_auto_id);
- $_params = array('filename' => $_cache_file, 'contents' => $params['results'], 'create_dirs' => true);
- require_once(SMARTY_CORE_DIR . 'core.write_file.php');
- smarty_core_write_file($_params, $smarty);
- return true;
+ $smarty->trigger_error('unable to write to $cache_dir \'' . realpath($smarty->cache_dir) . '\'. Be sure $cache_dir is writable by the web server user.', E_USER_ERROR);
+ return false;
}
+
+ $_auto_id = $smarty->_get_auto_id($params['cache_id'], $params['compile_id']);
+ $_cache_file = $smarty->_get_auto_filename($smarty->cache_dir, $params['tpl_file'], $_auto_id);
+ $_params = array('filename' => $_cache_file, 'contents' => $params['results'], 'create_dirs' => true);
+ require_once(SMARTY_CORE_DIR . 'core.write_file.php');
+ smarty_core_write_file($_params, $smarty);
+ return true;
}
/* vim: set expandtab: */