from the PHPUG-Hamburg
// to help me here. :-)
preg_match_all(sprintf('/(?:FROM|JOIN|DESC|,)[\s](%s[\d\w]+)/i', $table_prefix), $query, $this->currTables);
// Take the 2nd array, so now we have all tables together :-)
$this->currTables = $this->currTables[1];
// We need $this->hashList for deleting cache files when UPDATE, INSERT et cetera is queried
if (count($this->currTables) == 0) {
// Always read from database if no table was found... :-)
$flushCache = true;
} else {
// Search for the current tables in hash list
foreach ($this->currTables as $table) {
if (!isset($this->hashList[$table])) {
// The table was not found in the hash list so we add it + the query
$this->hashList[$table] = array($this->currQueryHash);
// Not yet cached!
$flushCache = true;
} elseif (!in_array($this->currQueryHash, $this->hashList[$table])) {
// The table was been added to the hash list before but not this query string
$this->hashList[$table][] = $this->currQueryHash;
// Not yet cached!
$flushCache = true;
} else {
// Count the cache hit
$this->cache_hits++;
}
}
}
// Do we need to create the cache file?
if ($flushCache) {
// Write logfile
/*
$fp = @fopen(ABSPATH."wp-content/cache/db-cache.log", 'a') or $this->cache_die("Cannot write logfile!");
fwrite($fp, $query."\n");
fclose($fp);
*/
// So let's read data from database and put it in memory
$this->currNums[$this->currQueryHash] = parent::query($query);
if ($this->currNums[$this->currQueryHash] > 0) {
// Read all data from the last_result array in wpdb
for ($idx = 0; $idx < $this->currNums[$this->currQueryHash]; $idx++) {
$this->cachedData[$this->currQueryHash][$idx] = parent::getLastResultElement($idx);
}
// Remeber this table for flushing
$this->waitingTables[] = $this->currQueryHash;
}
} else {
// Read from cache
$content = $this->readCacheFile($this->currQueryHash);
// Decompress it
if (!empty($content)) {
$content = $this->decode($content);
// Split it up
if (strstr($content, "---") !== false) {
$array = explode("---", $content);
$this->currNums[$this->currQueryHash] = $array[0];
$this->cachedData[$this->currQueryHash] = unserialize($array[1]);
// Transfer data to wpdb
//* DEBUG: */ echo "CACHED=".$this->currQueryHash."
";
//* DEBUG: */ echo "".$array[1]."
";
parent::setLastResultArray($this->cachedData[$this->currQueryHash]);
} else {
// Cache is invalid!
$this->cache_die(sprintf("SQL-CACHER BUG: Cache with query %s is invalid!", $query));
//$this->waitingTables[] = $this->currQueryHash;
}
}
// Number of rows
$this->query_nums = $this->currNums[$this->currQueryHash];
}
} else {
// Check for tables
preg_match_all(sprintf('/[\s](%s[\d\w]+)/i', $table_prefix), $query, $this->currTables);
// Take the 2nd array, so now we have all tables together :-)
$this->currTables = $this->currTables[1];
// Check for existing cahes by analyzing the query string
$this->removeAssignedCacheFiles();
// Run the SQL command (don't comment this out, or the roof will fall on your head! ;-))
// Nope, this would be the ultimate spam-killer when there will be the problem that *no*
// comment/post/link/whatever get inserted... Sorry, dude but spam-fighting is not so simple...
$this->query_nums = parent::query($query);
}
// Return number of rows
return $this->query_nums;
}
// Remove all assigned tables
function removeAssignedCacheFiles () {
foreach ($this->currTables as $table) {
if (is_array($this->hashList[$table])) {
foreach ($this->hashList[$table] as $hash) {
// Remove from many arrays
unset($this->currNums[$hash]);
unset($this->cachedData[$hash]);
// Delete cache file itself
$this->removeCacheFile($table);
}
}
// Remove the final hashList entry
unset($this->hashList[$table]);
}
// Remove $this->currTables as well
$this->currTables = array();
}
// Remove the cache file from filesystem
function removeCacheFile ($name) {
$filename = sprintf("%s%s.cache", $this->cache_dir, $name);
if (file_exists($filename) && is_writeable($filename)) {
// Remove it
@unlink($filename);
// Has it been removed?
if (file_exists($filename)) {
// No! Die here
$this->cache_die(sprintf("SQL-CACHER BUG: Cannot remove cache file %s from filesystem!", $name));
}
}
}
// Shuts the caching system down
function close () {
// Shall we output debugging info? (useful for development)
if (defined('DEBUG_SQL_CACHE')) {
// Output cache debug informations
$this->cache_debug_output();
}
// Flush all newly read tables
$this->flushWaitingTables();
// Flush the hashlist to disc
$this->flushHashList();
// Close the database connection
parent::close();
}
// Flush all waiting tables to disc
function flushWaitingTables () {
if (count($this->waitingTables) > 0) {
// Begin with flush procecure
foreach ($this->waitingTables as $idx=>$table) {
// Serialze the cached data
$data = serialize($this->cachedData[$table]);
if ($data != "N;") {
// Serialize all output (--- is the default seperator)
$content = $this->compileData($table, $data);
// Compress/encode it
$content = $this->encode($content);
// Now we hash the serialized object/array for looking it up in the allocation table
$hash = md5(WP_SECRET.":".$content);
if ($this->lookupDataInAllocTable($hash, $table) == "new") {
// Write it to disc
$this->writeCacheFile($table, $content);
} else {
// Do not flush the same data twice...
$this->destroyCachedData($idx, $table);
}
// Add it to the allocation table
$this->addDataAllocTable($hash, $table);
} else {
// Remove handler from queue
$this->destroyCachedData($idx, $table);
}
}
}
}
// The central method which binds the SQL hash with the serialized data
function compileData ($table, $data) {
return $this->currNums[$table] . "---" . $data;
}
// Adds an allocation between the table and the hashed serialization output
function addDataAllocTable ($hash, $table) {
// Do we have created the table?
if ($this->lookupDataInAllocTable($hash, $table) == "hash") {
// Is the table already allocated with a hash?
if (!in_array($table, $this->allocTable[$hash])) {
$this->allocTable[$hash][] = $table;
}
} else {
// Create new entry
$this->allocTable[$hash] = array($table);
}
}
// Search for the hashed data in the allocation table
function lookupDataInAllocTable ($hash, $table) {
// Simple, he? ;-)
if (isset($this->allocTable[$hash])) {
if (in_array($table, $this->allocTable[$hash])) {
// The table is already hashed
return "already";
} else {
// Only the hash was found
return "hash";
}
}
// Completly new hash and table
return "new";
}
// Destroys cached data
function destroyCachedData ($idx, $table) {
unset($this->waitingTables[$idx]);
unset($this->currNums[$table]);
unset($this->cachedData[$table]);
}
// Encodes and compresses the hashlist
function flushHashList () {
// Serialize and compress/encode it
$output = $this->encode(serialize($this->hashList));
// Write it to disc
$this->writeCachefile("hashlist", $output);
}
// Loads a cached hashList from disc
function loadHashList () {
$content = $this->readCacheFile("hashlist");
if (!empty($content)) {
$content = unserialize($this->decode($content));
$this->hashList = $content;
}
}
// Write a cache file to disc, you shall better
// serialize $content before sending it to this method
// (private)
function writeCacheFile ($name, $content) {
// Shall not be defined in productive area
if (defined('DONT_FLUSH_SQL')) return;
$filename = sprintf("%s%s.cache", $this->cache_dir, $name);
if ($this->useZlib == 'on') {
// Use the gzopen, bla functions
// You may need to experiment with the compression level (the number behind "w")
// to find the best value for your PHP/OS combination.
$fp = @gzopen($filename, 'w4') or $this->cache_die(sprintf("Cannot write %s.cache!", $name));
@gzputs($fp, $content);
@gzclose($fp);
} else {
// No ZLIB compression
if (function_exists('file_put_contents')) {
// One function
@file_put_contents($filename, $content) or $this->cache_die(sprintf("Cannot write %s.cache!", $name));
} else {
// Fall-back method
$fp = @fopen($filename, 'w') or $this->cache_die(sprintf("Cannot write %s.cache!", $name));
@fwrite($fp, sprintf("%s\n", $content));
@fclose($fp);
}
}
}
// Read a cache file from disc
// (private)
function readCacheFile ($name) {
$content = "";
$filename = sprintf("%s%s.cache", $this->cache_dir, $name);
if (file_exists($filename) && is_readable($filename)) {
// Use gzopen?
if ($this->useZlib == 'on') {
// Read compressed file
$fp = @gzopen($filename, 'r') or $this->cache_die(sprintf("Cannot read %s.cache!", $name));
while (!gzeof($fp)) {
$content .= gzgets($fp, 4096);
}
gzclose($fp);
} else {
// Use maybe uncompresed files
$content = implode("", file($filename));
}
}
return $content;
}
// Encode method
function encode ($str) {
if ($this->compress && $this->useZlib == 'off') {
return base64_encode(gzcompress($str));
} else {
return $str;
}
}
// Decode method
function decode ($str) {
if ($this->compress && $this->useZlib == 'off') {
return gzuncompress(base64_decode($str));
} else {
return $str;
}
}
// Setter to turn compression on/off
function setCompression ($status) {
$this->compress = $status;
}
// Debug informations of the cache
function cache_debug_output () {
echo "Cache hits: ".$this->cache_hits."
Database queries: ".parent::getNumQueries()."
Cached queries:
".print_r($this->hashList, true)."
Cached table data
".print_r($this->cachedData, true)."
Cached number of rows:
".print_r($this->currNums, true)."
";
}
// Wrapper for wp_die()
function cache_die ($msg) {
printf("%s
\n", $msg);
}
// Detects ZLIB support
function detectZlibSupport () {
// Do only detect when in auto-detection mode
if ($this->useZlib == 'auto') {
// Automatic detection can slowdown your blog
// So when you know that your PHP has ZLIB enabled
// change the setting in header to 'on'
if (function_exists('gzopen')) {
$this->useZlib = 'on';
$this->compression = false;
} else {
$this->useZlib = 'off';
}
}
}
}
//
?>