First stab at cache management backend. Everything seems to have been tested and working so far, but a number of things require a more specialized cache and can't go through the framework (e.g. user ranks which use references to map usernames to user IDs)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/includes/cache.php Mon Jul 07 18:12:30 2008 -0400
@@ -0,0 +1,131 @@
+<?php
+
+/*
+ * Enano - an open-source CMS capable of wiki functions, Drupal-like sidebar blocks, and everything in between
+ * Version 1.1.4 (Caoineag alpha 4)
+ * Copyright (C) 2006-2008 Dan Fuhry
+ *
+ * This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2 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 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
+ */
+
+/**
+ * Framework for caching arbitrary data around the site.
+ * @package Enano
+ * @subpackage Core
+ * @author Dan Fuhry <dan@enanocms.org>
+ * @license GNU General Public License
+ */
+
+class CacheManager
+{
+ /**
+ * Fetch a cached piece of data.
+ * @param string Cache ID. The timestamp is checked automatically.
+ */
+
+ public function fetch($cache_id)
+ {
+ if ( !preg_match('/^[a-z0-9_]+$/', $cache_id) )
+ {
+ throw new Exception('Cache ID must be letters, numbers, and underscores.');
+ }
+ $cache_file = ENANO_ROOT . "/cache/cache_$cache_id.php";
+ if ( file_exists($cache_file) )
+ {
+ require($cache_file);
+ if ( isset($_cache_data) && isset($_cache_ttl) && isset($_cache_timestamp) )
+ {
+ $cache_expire = $_cache_timestamp + ( 60 * $_cache_ttl);
+ if ( $cache_expire >= time() || $_cache_ttl === -1 )
+ {
+ return $_cache_data;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Stores an array by var_export()ing it and saving it to disk.
+ * @param string Cache ID - human readable name for this store (letters, numbers, underscores)
+ * @param array Data to store
+ * @param int TTL for the cached array, in minutes. Defaults to 20. If set to -1, caches indefinitely.
+ * @return bool True on success, false on failure
+ */
+
+ public function store($cache_id, $data, $ttl = 20)
+ {
+ if ( getConfig('cache_thumbs') != '1' )
+ {
+ // caching disabled
+ return false;
+ }
+ if ( !preg_match('/^[a-z0-9_]+$/', $cache_id) )
+ {
+ throw new Exception('Cache ID must be letters, numbers, and underscores.');
+ }
+ if ( !is_int($ttl) )
+ {
+ throw new Exception('TTL must be an integer');
+ }
+
+ $cache_file = ENANO_ROOT . "/cache/cache_$cache_id.php";
+ if ( file_exists($cache_file) )
+ {
+ @unlink($cache_file);
+ }
+ $fh = @fopen($cache_file, 'w');
+ if ( !$fh )
+ {
+ throw new Exception('Failed to open file for writing.');
+ }
+ $exported = Language::var_export_string($data);
+ $now = time();
+ $content = <<<EOF
+<?php
+/**
+ * Automatically generated cache file.
+ * Do not edit this file as it will expire and be overwritten $ttl minutes from its creation.
+ */
+
+\$_cache_ttl = $ttl;
+\$_cache_timestamp = $now;
+\$_cache_data = $exported;
+
+EOF;
+ fwrite($fh, $content);
+ fclose($fh);
+
+ return true;
+ }
+
+ /**
+ * Deletes a cached item.
+ * @param string Cache ID.
+ * @return bool true on success or if item doesn't exist, false on failure
+ */
+
+ public function purge($cache_id)
+ {
+ if ( !preg_match('/^[a-z0-9_]+$/', $cache_id) )
+ {
+ throw new Exception('Cache ID must be letters, numbers, and underscores.');
+ }
+
+ $cache_file = ENANO_ROOT . "/cache/cache_$cache_id.php";
+ if ( file_exists($cache_file) )
+ {
+ if ( unlink($cache_file) )
+ {
+ return true;
+ }
+ return false;
+ }
+ return true;
+ }
+}
+
--- a/includes/common.php Mon Jul 07 02:50:17 2008 -0400
+++ b/includes/common.php Mon Jul 07 18:12:30 2008 -0400
@@ -137,6 +137,7 @@
require_once(ENANO_ROOT.'/includes/sessions.php');
require_once(ENANO_ROOT.'/includes/template.php');
require_once(ENANO_ROOT.'/includes/plugins.php');
+require_once(ENANO_ROOT.'/includes/cache.php');
require_once(ENANO_ROOT.'/includes/lang.php');
require_once(ENANO_ROOT.'/includes/render.php');
require_once(ENANO_ROOT.'/includes/rijndael.php');
@@ -309,6 +310,9 @@
profiler_log('Ran checks');
+// Init cache
+$cache = new CacheManager();
+
// Load plugin manager
$plugins = new pluginLoader();
--- a/includes/lang.php Mon Jul 07 02:50:17 2008 -0400
+++ b/includes/lang.php Mon Jul 07 18:12:30 2008 -0400
@@ -122,14 +122,21 @@
{
global $db, $session, $paths, $template, $plugins; // Common objects
- $lang_file = ENANO_ROOT . "/cache/lang_{$this->lang_id}.php";
// Attempt to load the strings from a cache file
- if ( file_exists($lang_file) && $allow_cache )
+ $loaded = false;
+
+ if ( $allow_cache )
{
- // Yay! found it
- $this->load_cache_file($lang_file);
+ // Load the cache manager
+ global $cache;
+
+ if ( $cached = $cache->fetch("lang_{$this->lang_id}") )
+ {
+ $this->merge($cached);
+ $loaded = true;
+ }
}
- else
+ if ( !$loaded )
{
// No cache file - select and retrieve from the database
$q = $db->sql_unbuffered_query("SELECT string_category, string_name, string_content FROM " . table_prefix . "language_strings WHERE lang_id = {$this->lang_id};");
@@ -150,6 +157,7 @@
while ( $row = $db->fetchrow() );
// all done fetching
$this->merge($strings);
+ $this->regen_caches(false);
}
else
{
@@ -528,41 +536,24 @@
* Refetches the strings and writes out the cache file.
*/
- function regen_caches()
+ function regen_caches($refetch = true)
{
global $db, $session, $paths, $template, $plugins; // Common objects
- $lang_file = ENANO_ROOT . "/cache/lang_{$this->lang_id}.php";
-
// Refresh the strings in RAM to the latest copies in the DB
- $this->fetch(false);
+ if ( $refetch )
+ $this->fetch(false);
- $handle = @fopen($lang_file, 'w');
- if ( !$handle )
- // Couldn't open the file. Silently fail and let the strings come from the database.
- return false;
-
- // The file's open, that means we should be good.
- fwrite($handle, '<?php
-// This file was generated automatically by Enano. You should not edit this file because any changes you make
-// to it will not be visible in the ACP and all changes will be lost upon any changes to strings in the admin panel.
-
-$lang_cache = ');
+ // Load the cache manager
+ global $cache;
- $exported = $this->var_export_string($this->strings);
- if ( empty($exported) )
- // Ehh, that's not good
- $db->_die('lang.php - var_export_string() failed');
-
- fwrite($handle, $exported . '; ?>');
+ // Store it
+ $cache->store("lang_{$this->lang_id}", $this->strings, -1);
// Update timestamp in database
$q = $db->sql_query('UPDATE ' . table_prefix . 'language SET last_changed = ' . time() . ' WHERE lang_id = ' . $this->lang_id . ';');
if ( !$q )
$db->_die('lang.php - updating timestamp on language');
-
- // Done =)
- fclose($handle);
}
/**
--- a/includes/paths.php Mon Jul 07 02:50:17 2008 -0400
+++ b/includes/paths.php Mon Jul 07 18:12:30 2008 -0400
@@ -157,6 +157,7 @@
{
global $db, $session, $paths, $template, $plugins; // Common objects
global $lang;
+ global $cache;
$code = $plugins->setHook('paths_init_before');
foreach ( $code as $cmd )
@@ -164,16 +165,9 @@
eval($cmd);
}
- $cache_enable = ( getConfig('cache_thumbs') == '1' );
- $cache_file = ENANO_ROOT . '/cache/cache_page_meta.php';
- $cache_fresh = ( file_exists($cache_file) ) ? filemtime($cache_file) + 1800 >= time() : false;
- if ( $cache_enable && $cache_fresh )
+ if ( $page_cache = $cache->fetch('page_meta') )
{
- require($cache_file);
- if ( isset($page_cache) && is_array($page_cache) )
- {
- $this->pages = array_merge($this->pages, $page_cache);
- }
+ $this->pages = array_merge($this->pages, $page_cache);
}
else
{
@@ -192,10 +186,7 @@
$this->pages[] =& $this->pages[$r['urlname']];
}
- if ( $cache_enable )
- {
- $this->update_metadata_cache();
- }
+ $this->update_metadata_cache();
}
$db->free_result();
if ( defined('ENANO_INTERFACE_INDEX') || defined('ENANO_INTERFACE_AJAX') || defined('IN_ENANO_UPGRADE') )
@@ -585,36 +576,27 @@
{
global $db, $session, $paths, $template, $plugins; // Common objects
- $cache_output = <<<EOF
-<?php
-
-/**
- * Automatically generated cache of page metadata. Do not edit this file as it is updated every 20 minutes.
- */
-
-\$page_cache = array(
-EOF;
+ if ( getConfig('cache_thumbs') != '1' )
+ return false;
+
$e = $db->sql_unbuffered_query('SELECT name,urlname,namespace,special,visible,comments_on,protected,delvotes,' . "\n"
. ' delvote_ips,wiki_mode,password FROM '.table_prefix.'pages ORDER BY name;');
if ( !$e )
$db->_die();
+ $md_array = array();
+
while ( $row = $db->fetchrow() )
{
$row = $this->calculate_metadata_from_row($row);
- $key = addslashes($row['urlname']);
- $row = substr(preg_replace('/^/m', ' ', Language::var_export_string($row)), 2);
- $cache_output .= "\n '$key' => $row,";
+ $md_array[$row['urlname']] = $row;
}
- $cache_output .= "\n);\n";
- $cache_file = ENANO_ROOT . '/cache/cache_page_meta.php';
+ // import cache functions
+ global $cache;
- $fh = @fopen($cache_file, 'w');
- if ( !$fh )
- return false;
- fwrite($fh, $cache_output);
- fclose($fh);
+ // store data (TTL 20 minutes)
+ $cache->store('page_meta', $md_array, 20);
return true;
}
--- a/includes/plugins.php Mon Jul 07 02:50:17 2008 -0400
+++ b/includes/plugins.php Mon Jul 07 18:12:30 2008 -0400
@@ -446,7 +446,6 @@
$this->update_plugins_cache($plugin_info);
$GLOBALS['plugins_cache'] = $plugin_info;
- @define('ENANO_PLUGINS_CACHE_LOADED', true);
}
/**
@@ -457,26 +456,8 @@
function update_plugins_cache($plugin_info)
{
- $plugin_info = Language::var_export_string($plugin_info);
-
- $file = ENANO_ROOT . '/cache/cache_plugins.php';
- $fh = @fopen($file, 'w');
- if ( !$fh )
- // can't open for writing
- return false;
-
- $contents = <<<EOF
-<?php
-/**
- * Cache of plugin files. Automatically generated, any modifications will soon be lost
- */
-
-@define('ENANO_PLUGINS_CACHE_LOADED', true);
-\$GLOBALS['plugins_cache'] = $plugin_info;
-
-EOF;
- fwrite($fh, $contents);
- fclose($fh);
+ global $cache;
+ return $cache->store('plugins', $plugin_info, -1);
}
/**
@@ -485,9 +466,10 @@
function load_plugins_cache()
{
- if ( file_exists(ENANO_ROOT . '/cache/cache_plugins.php') && !defined('ENANO_PLUGINS_CACHE_LOADED') )
+ global $cache;
+ if ( $data = $cache->fetch('plugins') )
{
- require(ENANO_ROOT . '/cache/cache_plugins.php');
+ $GLOBALS['plugins_cache'] = $data;
}
}
--- a/includes/template.php Mon Jul 07 02:50:17 2008 -0400
+++ b/includes/template.php Mon Jul 07 18:12:30 2008 -0400
@@ -2014,26 +2014,18 @@
function fetch_sidebar()
{
global $db, $session, $paths, $template, $plugins; // Common objects
+ global $cache;
$left = '';
$right = '';
// check the cache
- $cache_enable = getConfig('cache_thumbs') == '1' && !$session->user_logged_in;
- $cache_file = ENANO_ROOT . "/cache/cache_anon_sidebar.php";
- $cache_fresh = intval(getConfig('sidebar_anon_cache_time') + 600) >= time();
- if ( $cache_enable && $cache_fresh )
+ if ( !$session->user_logged_in && $data = $cache->fetch('anon_sidebar') )
{
- @include($cache_file);
- if ( isset($sidebar_cache) )
+ if ( @$data['_theme_'] === $this->theme )
{
- // we loaded the cache!
- foreach ( $sidebar_cache as $i => $_ )
- {
- $block =& $sidebar_cache[$i];
- $block = str_replace('$USERNAME$', $session->username, $block);
- }
- return $sidebar_cache;
+ unset($data['_theme_']);
+ return $data;
}
}
@@ -2079,7 +2071,7 @@
break;
case BLOCK_PLUGIN:
$parser = $this->makeParserText('{CONTENT}');
- $c = (gettype($this->fetch_block($row['block_content'])) == 'string') ? $this->fetch_block($row['block_content']) : /* This used to say "can't find plugin block" but I think it's more friendly to just silently hide it. */ '';
+ $c = '<!-- PLUGIN -->' . (gettype($this->fetch_block($row['block_content'])) == 'string') ? $this->fetch_block($row['block_content']) : /* This used to say "can't find plugin block" but I think it's more friendly to just silently hide it. */ '';
break;
}
// is there a {restrict} or {hideif} block?
@@ -2125,26 +2117,14 @@
$min .= $bottom;
}
$return = Array($left, $right, $min);
- if ( $cache_enable )
+ if ( getConfig('cache_thumbs') == '1' && !$session->user_logged_in )
{
- $cachestore = Language::var_export_string($return);
+ $cachestore = enano_json_encode($return);
$cachestore = str_replace($session->username, '$USERNAME$', $cachestore);
- $cachestore = <<<EOF
-<?php
-/**
- * Automatically generated cache of the sidebar for guests.
- * Do not modify this, it is refreshed every 15 minutes.
- */
-
-\$sidebar_cache = $cachestore;
-EOF;
- $fh = @fopen($cache_file, 'w');
- if ( $fh )
- {
- fwrite($fh, $cachestore);
- fclose($fh);
- }
- setConfig('sidebar_anon_cache_time', time());
+ $cachestore = str_replace($paths->page, '$PAGEID$', $cachestore);
+ $cachestore = enano_json_decode($cachestore);
+ $cachestore['_theme_'] = $this->theme;
+ $cache->store('anon_sidebar', $cachestore, 10);
}
return $return;
}
--- a/plugins/SpecialAdmin.php Mon Jul 07 02:50:17 2008 -0400
+++ b/plugins/SpecialAdmin.php Mon Jul 07 18:12:30 2008 -0400
@@ -2166,6 +2166,7 @@
{
global $db, $session, $paths, $template, $plugins; // Common objects
global $lang;
+ global $cache;
if($session->auth_level < USER_LEVEL_ADMIN)
{
@@ -2227,6 +2228,7 @@
exit;
}
}
+ $cache->purge('anon_sidebar');
echo '<div class="info-box" style="margin: 10px 0;">' . $lang->get('sbedit_msg_order_update_success') . '</div>';
}
elseif(isset($_POST['create']))
@@ -2279,7 +2281,8 @@
$template->footer();
exit;
}
-
+
+ $cache->purge('anon_sidebar');
echo '<div class="info-box" style="margin: 10px 0;">' . $lang->get('sbedit_msg_item_added') . '</div>';
}
@@ -2385,7 +2388,7 @@
<?php
foreach($template->plugin_blocks as $k => $c)
{
- echo '<option value="'.$k.'">'.$k.'</option>';
+ echo '<option value="'.$k.'">'.$lang->get($k).'</option>';
}
?>
</select>
@@ -2414,6 +2417,7 @@
return;
break;
case 'move':
+ $cache->purge('anon_sidebar');
if( !isset($_GET['side']) || ( isset($_GET['side']) && !preg_match('#^([0-9]+)$#', $_GET['side']) ) )
{
echo '<div class="warning-box" style="margin: 10px 0;">$_GET[\'side\'] contained an SQL injection attempt</div>';
@@ -2436,6 +2440,7 @@
$template->footer();
exit;
}
+ $cache->purge('anon_sidebar');
if(isset($_GET['ajax']))
{
ob_end_clean();
@@ -2493,6 +2498,8 @@
ob_end_clean();
$r = $db->fetchrow();
$db->free_result();
+ $cache->purge('anon_sidebar');
+
if($r['block_type'] == BLOCK_PLUGIN) die('HOUSTON_WE_HAVE_A_PLUGIN');
die($r['block_content']);
break;
@@ -2555,6 +2562,7 @@
break;
}
$c = preg_replace('/\{(restrict|hideif) ([a-z0-9_\(\)\|&! ]+)\}/', '', $c);
+ $cache->purge('anon_sidebar');
die('var status = \'GOOD\'; var content = unescape(\''.hexencode($c).'\');');
break;
}
--- a/plugins/SpecialPageFuncs.php Mon Jul 07 02:50:17 2008 -0400
+++ b/plugins/SpecialPageFuncs.php Mon Jul 07 18:12:30 2008 -0400
@@ -836,7 +836,7 @@
}
$sb_html = $cloud->make_html('small', 'justify') . '<br /><a style="text-align: center;" href="' . makeUrlNS('Special', 'TagCloud') . '">' . $lang->get('pagetools_tagcloud_sidebar_btn_larger') . '</a>';
}
- $template->sidebar_widget($lang->get('pagetools_tagcloud_sidebar_title'), "<div style='padding: 5px;'>$sb_html</div>");
+ $template->sidebar_widget('pagetools_tagcloud_sidebar_title', "<div style='padding: 5px;'>$sb_html</div>");
}
$plugins->attachHook('compile_template', 'sidebar_add_tag_cloud();');