executed_ = false;
$this->_cache = array();
$this->_cached = false;
// this is the minimum runtime a query has to run to be
// eligible for caching in seconds
$this->_minruntime = 0.1;
// maximum size of a cached result set (512kB)
$this->_maxcachesize = 524288;
$this->d = true;
}
function checkCache()
{
// only cache selects
// we don't use select ... into so there is no problem
if (strtolower(substr($this->_sql, 0, 6)) != 'select' && strtolower(substr($this->_sql, 0, 4)) != 'show')
{
// this is no select, update the table
$this->markAffectedTables();
return false;
}
if (file_exists(KB_CACHEDIR.'/qcache_qry_'.$this->_hash))
{
$this->_mtime = filemtime(KB_CACHEDIR.'/qcache_qry_'.$this->_hash);
if ($this->isCacheValid())
{
return true;
}
}
return false;
}
function parseSQL()
{
// gets all involved tables for a select statement
$text = strtolower($this->_sql).' ';
// we try to get the text from 'from' to 'where' because all involved
// tables are declared in that part
$from = strpos($text, 'from')+5;
if (!$to = strpos($text, 'where'))
{
$to = strlen($text);
}
$parse = trim(substr($text, $from, $to-$from));
$tables = array();
if (strpos($parse, ',') !== false)
{
// , is a synonym for join so we'll replace them
$parse = str_replace(',', ' join ', $parse);
}
if (strpos($parse, 'join'))
{
// if this query is a join we parse it with regexp to get all tables
preg_match_all('/join (.*?) /', $parse, $match);
$tables = $match[1];
}
else
{
// no join so it is hopefully a simple table select
$tables[] = $parse;
}
$this->_usedtables = $tables;
}
function isCacheValid()
{
// check if cachefiles are stil valid
// first, we need to get all involved tables
$this->parseSQL();
foreach ($this->_usedtables as $table)
{
$file = KB_CACHEDIR.'/qcache_tbl_'.trim($table);
if (file_exists($file))
{
// if one of the tables is outdated, the query is outdated
if ($this->_mtime < filemtime($file))
{
return false;
}
}
}
return true;
}
function markAffectedTables()
{
// this function invalidates cache files for touched tables
$text = trim(strtolower($this->_sql));
$text = str_replace(array('ignore','`', "\r\n", "\n"), '', $text);
$ta = preg_split('/ /', $text, 0, PREG_SPLIT_NO_EMPTY);
// check for sql keywords and get the table from the appropriate position
$tables = array();
if ($ta[0] == 'update')
{
$tables[] = $ta[1];
}
elseif ($ta[0] == 'insert')
{
$tables[] = $ta[2];
}
elseif ($ta[0] == 'replace')
{
$tables[] = $ta[2];
}
elseif ($ta[0] == 'delete')
{
$tables[] = $ta[2];
}
elseif ($ta[0] == 'alter')
{
return false;
}
elseif ($ta[0] == 'create')
{
return false;
}
else
{
var_dump($ta);
trigger_error('No suitable handler for query found.',E_USER_WARNING);
return false;
}
foreach ($tables as $table)
{
$file = KB_CACHEDIR.'/qcache_tbl_'.$table;
touch($file);
}
// refresh php's filestatcache so we dont get wrong timestamps on changed files
clearstatcache();
}
function genCache()
{
// this function fetches all rows and writes the data into a textfile
// don't attemp to cache updates!
if (strtolower(substr($this->_sql, 0, 6)) != 'select' && strtolower(substr($this->_sql, 0, 4)) != 'show')
{
return false;
}
$bsize = 0;
while ($row = $this->getRow())
{
$this->_cache[] = $row;
// if the bytesize of the table exceeds the limit we'll abort
// the cache generation and leave this query unbuffered
$bsize += join('', $row);
if ($bsize > $this->_maxcachesize)
{
$this->_cache[] = array();
$this->_cached = false;
$this->rewind();
return false;
}
}
// write data into textfile
file_put_contents(KB_CACHEDIR.'/qcache_qry_'.$this->_hash, serialize($this->_cache));
$this->_cached = true;
$this->_currrow = 0;
$this->executed_ = true;
}
function loadCache()
{
// loads the cachefile into the memory
$this->_cache = unserialize(file_get_contents(KB_CACHEDIR.'/qcache_qry_'.$this->_hash));
$this->_cached = true;
$this->_currrow = 0;
$this->executed_ = true;
}
function execute($sql)
{
$this->_sql = trim($sql);
$this->_hash = md5($this->_sql);
$this->_cache = array();
$this->_cached = false;
if ($this->checkCache())
{
$this->loadCache();
$this->queryCachedCount(true);
return true;
}
// we got no or no valid cache so open the connection and run the query
$this->dbconn_ = new DBConnection;
$t1 = strtok(microtime(), ' ') + strtok('');
$this->resid_ = mysql_query($sql, $this->dbconn_->id());
if ($this->resid_ == false)
{
if (DB_HALTONERROR === true)
{
echo "Database error: ".mysql_error($this->dbconn_->id())."
";
echo "SQL: ".$this->_sql."
";
exit;
}
else
{
return false;
}
}
$this->exectime_ = strtok(microtime(), ' ') + strtok('') - $t1;
$this->executed_ = true;
if (KB_PROFILE == 2)
{
file_put_contents('/tmp/profile.lst', $sql."\nExecution time: ".$this->exectime_."\n", FILE_APPEND);
}
// if the query was too slow we'll fetch all rows and run it cached
if ($this->exectime_ > $this->_minruntime)
{
$this->genCache();
}
$this->queryCount(true);
return true;
}
function queryCount($increase = false)
{
static $count;
if ($increase)
{
$count++;
}
return $count;
}
function queryCachedCount($increase = false)
{
static $count;
if ($increase)
{
$count++;
}
return $count;
}
function recordCount()
{
if ($this->_cached)
{
return count($this->_cache);
}
return mysql_num_rows($this->resid_);
}
function getRow()
{
if ($this->_cached)
{
if (!isset($this->_cache[$this->_currrow]))
{
return false;
}
// return the current row and increase the pointer by one
return $this->_cache[$this->_currrow++];
}
if (is_resource($this->resid_))
{
return mysql_fetch_assoc($this->resid_);
}
return false;
}
function rewind()
{
if ($this->_cached)
{
$this->_currrow = 0;
}
@mysql_data_seek($this->resid_, 0);
}
function getInsertID()
{
return mysql_insert_id();
}
function execTime()
{
return $this->exectime_;
}
function executed()
{
return $this->executed_;
}
function getErrorMsg()
{
$msg = $this->sql_."
";
$msg .= "Query failed. ".mysql_error($this->dbconn_->id());
return $msg;
}
}
?>