<?php
/*
** The settings in Settings.php should look like this:
// Select a system. Probably want to leave this up to the installer or admin panel.
$cache = array(
// memcache, eaccelerator, mmcache, output_cache, xcache, apc, file, or false.
'system' => '',
// The level of caching. The higher the number, the more gets cached. An integer between 0 and 3 (0 turns caching off).
'level' => 0,
// This is generally only used for memcache. Should be a string of 'server:port, server:port'.
'servers' => '',
// Persistent connection (only of use to memcache).
'persist' => false,
// The directory where you want to store your cache files (if you are using the 'file' system).
'dir' => '',
// Boolean value. If true, it logs how many hits/misses and how long they took.
'debug' => false,
);
*/
$cache = new cache($cache);
class cache
{
public $hits = 0;
public $count = 0;
protected
$settings =
array();
// Check all of the variables and setup what functions to use.
function __construct ($settings = false)
{
$this->settings = $settings;
if ($this->cache == false)
return;
$this->
settings['level'] = !
empty($this->
settings['level']) ?
(int
) $this->
settings['level'] :
false;
$this->
settings['system'] = !
empty($this->
settings['system']) ?
$this->
settings['system'] :
false;
$this->
settings['dir'] = !
empty($this->
settings['dir']) &&
file_exists($this->
settings['dir']) ?
$this->
settings['dir'] :
false;
if (!$this->settings['system'] || !$this->settings['level'])
$this->settings = false;
// Define the Get, Set, and sometimes Remove functions.
// Just added: remove, increment, decrement, status.
switch ($this->settings['system'])
{
case 'apc':
$this->get = 'apc_fetch';
$this->set = 'apc_store';
$this->rm = 'apc_delete';
$this->clear = 'apc_clear_cache';
break;
case 'memcache':
$this->get = 'memcache_get';
$this->set = 'memcache_set';
$this->rm = 'memcache_delete';
$this->clear = 'memcache_flush';
break;
case 'eaccelerator':
$this->get = 'eaccelerator_get';
$this->set = 'eaccelerator_put';
$this->rm = 'eaccelerator_rm';
$this->gc = 'eaccelerator_gc';
// Might have to use eaccelerator_list_keys() and rm all of them.
//$this->clear = 'eaccelerator_gc';
break;
case 'mmcache':
$this->get = 'mmcache_get';
$this->set = 'mmcache_put';
$this->rm = 'mmcache_rm';
$this->gc = 'mmcache_gc';
break;
case 'output_cache':
$this->get = 'output_cache_get';
$this->set = 'output_cache_put';
// Always one that has to be different. We must find those that check for TTL in get.
$this->settings['req_ttl'] = true;
$this->rm = 'output_cache_remove_key';
// No idea how to implement clear() in this one.
break;
case 'xcache':
$this->get = 'xcache_get';
$this->set = 'xcache_set';
$this->rm = 'xcache_unset';
$this->clear = 'xcache_clear_cache';
break;
case: 'file':
$this->
get =
array($this,
'get');
$this->
set =
array($this,
'set');
$this->
rm =
array($this,
'rm');
$this->
clear =
array($this,
'clear');
//$this->version = array($this, 'version'),
default:
$this->settings = false;
}
// Do we need to connect to the memcached server?
if ($this->settings['system'] == 'memcache')
{
if (!
empty($this->
settings['servers'])) {
// Not connected yet?
{
$this->
settings['servers'] =
explode(',',
$this->
settings['servers']);
get_memcached_server();
}
if (!$this->res)
$this->settings = false;
}
else
$this->settings = false;
}
// Do we need to do garbage collection?
// Let someone know that caching isn't working.
if ($this->settings === false)
trigger_error('Invalid cache type: ' .
$this->
settings['system'],
E_USER_NOTICE);
trigger_error($this->
get .
' or ' .
$this->
set .
' functions not found',
E_USER_NOTICE);
}
// Connect to a memcached server.
private function get_memcached_server($level = 3)
{
// Don't try more times than we have servers!
$level =
min(count($this->
settings['servers']),
$level);
// Don't wait too long. It might be faster to just run without caching.
if ($this->settings['persist'])
$this->
res = memcache_connect
($server[0],
empty($server[1]) ?
11211 :
$server[1]);
else
$this->
res = memcache_pconnect
($server[0],
empty($server[1]) ?
11211 :
$server[1]);
if (!$this->res && $level > 0)
get_memcached_server($level - 1);
}
// Put an item in the cache.
function put($key, $value, $ttl = 120, $level = 0)
{
if (!$this->settings)
return;
if ($this->settings['debug'])
{
$this->count++;
$this->
hits[$this->
count] =
array('k' =>
$key,
'd' =>
'put',
's' =>
$value ===
null ?
0 :
strlen(serialize($value)));
}
$key = $this->get_key($key);
$value =
$value ===
null ?
null :
serialize($value);
if ($value === null)
$this->rm($key);
if ($this->settings['system'] == 'file')
{
// Create a PHP file with the info.
if ($value !== null)
file_put_contents
($this->
settings['dir'] .
'/data_' .
$key .
'.php',
'<?php if (' .
(time() +
$ttl) .
' < time()) $expired = true; else{$expired = false; $value = \'' .
addcslashes($value,
'\\\'') .
'\';}?>', LOCK_EX
);
else
$this->rm($this->settings['dir'] . '/data_' . $key . '.php');
}
// Some caches are accessed through a resource.
elseif (isset($this->
res)) $cache['put']($this->res, $key, $value, 0, $ttl);
else
$cache['put']($key, $value, $ttl);
if ($this->settings['debug'])
}
// Return a cached item.
function get($key, $ttl = 120, $level = 0)
{
if (!$this->settings)
return;
if ($this->settings['debug'])
{
$this->count++;
$this->
hits[$this->
count] =
array('k' =>
$key,
'd' =>
'get');
}
$key = $this->get_key($key);
if ($this->
settings['system'] ==
'file' &&
file_exists($this->
settings['dir'] .
'/data_' .
$key .
'.php') &&
filesize($this->
settings['dir'] .
'/data_' .
$key .
'.php') >
10) {
require($this->settings['dir'] . '/data_' . $key . '.php');
{
$this->rm($this->settings['dir'] . '/data_' . $key . '.php');
}
}
elseif (isset($this->
res)) $value = $this->get($this->res, $key);
elseif ($this->settings['req_ttl'])
$value = $this->get($key, $ttl);
else
$value = $this->get($key);
if ($this->settings['debug'])
{
$this->
hits[$this->
count]['s'] =
isset($value) ?
strlen($value) :
0;
}
}
// Increment.
function increment($key, $value = 1, $ttl = 120)
{
if (!$this->settings)
return;
{
{
return $cached_value + $value;
}
}
elseif ($this->settings['req_ttl'])
else
}
// Decrement.
function decrement($key, $value = 1, $ttl = 120)
{
if (!$this->settings)
return;
{
{
return $cached_value + $value;
}
}
elseif ($this->settings['req_ttl'])
else
}
// Remove a cached item.
function rm($key)
{
if (!$this->settings)
return;
if ($this->
settings['system'] ==
'file' &&
file_exists($this->
settings['dir'] .
'/data_' .
$key .
'.php')) unlink($this->
settings['dir'] .
'/data_' .
$key .
'.php');
else
}
// Clears the entire cache. Nothing will be left!
function clear()
{
if (!$this->settings)
return;
if ($this->settings['system'] == 'file')
{
$files = scandir($this->settings['dir']);
foreach ($files as $file)
unlink($this->
settings .
'/' .
$file);
}
// If there is no clear function defined, try to find all of the keys and delete those?
// If we can't delete all of them, try dropping everything and then restarting?
}
// Return the version of the current cache.
function version()
{
if (!$this->settings)
return;
switch($this->settings['system'])
{
case 'apc':
$version =
array('title' =>
'Alternative PHP Cache',
'version' =>
phpversion('apc'));
break;
case 'memcache':
$version =
array('title' =>
'Memcached',
'version' => memcache_get_version
($this->
resource));
break;
case 'eaccelerator':
$version =
array('title' =>
'eAccelerator',
'version' => EACCELERATOR_VERSION
);
break;
case 'mmcache':
$version =
array('title' =>
'Turck MMCache',
'version' => MMCACHE_VERSION
);
break;
case 'output_cache':
$version =
array('title' =>
'Zend',
'version' =>
$_PHPA['VERSION']);
break;
case 'xcache':
$version =
array('title' =>
'XCache',
'version' => XCACHE_VERSION
);
break;
default:
$version =
array('title' =>
'file',
'version' =>
1);
}
return $version;
}
// One function to get the key so we can change how the keys are formatted easily.
private function get_key($key)
{
//return md5(__DIR__ . filemtime(__FILE__) . __FILE__ . strtr($key, ':', '-'));
return strtr($key,
':',
'-');
}
}
/* Development Notes:
* I removed the @ operator from unlink because if we can't remove a file, we don't want to fill up our filesystem with cached files.
I'd rather have it fill up an error log and let someone know about it.
* Webpages for support:
MMCache: http://turck-mmcache.sourceforge.net/index_old.html#api
Xcache: http://xcache.lighttpd.net/wiki/XcacheApi
memcache: http://www.php.net/memcache
APC: http://www.php.net/apc
eAccelerator: http://bart.eaccelerator.net/doc/phpdoc/
Zend: http://files.zend.com/help/Zend-Platform/partial_and_preemptive_page_caching.htm
*/
?>