分类: 系统运维
2011-06-17 17:07:00
/**
* 在STable类上扩展缓存功能
* @author ice
*
*/
class SCachedTable extends STable {
public function __construct($tableName) {
parent::__construct($tableName);
}
//是否临时禁止缓存
private $temporaryDisableCache = false;
/**
* 临时禁止缓存,只禁止一次
* @return SCachedTable
*/
public function disCache() {
$this->temporaryDisableCache = true;
return $this;
}
/**
* 检查是否允许使用Memcache
*
* @return unknown
*/
private function _enable() {
//如果已经临时禁止了缓存
if($this->temporaryDisableCache){
//下次不再临时禁止
$this->temporaryDisableCache = false;
return false;
}
//检查缓存是否开启
return SMemcache::enable();
}
/**
* 具体执行数据库查询,如果缓存里没有,则查询并保存到缓存中
*
* @param string $sql
* @return 查询结果
*/
protected function _query($sql) {
//如果缓存未开启,则直接查询数据库
if(!$this->_enable()){
return parent::_query($sql);
}
//从缓存中取
$data = SMemcache::get($sql);
//如果缓存未中
if(!$data){
//从数据库查询
$data = parent::_query($sql);
//保存到缓存中
SMemcache::set($this->tableName,$sql,$data);
}
return $data;
}
/**
* 具体执行数据库语句,先清除本表的缓存,再执行
*
* @param string $sql
* @return boolean 执行是否成功
*/
protected function _execute($sql) {
//检查配置开关
if($this->_enable()){
SMemcache::clear($this->tableName);
}
return parent::_execute($sql);
}
/**
* 获取查询语句中的涉及表名
*
* @param string $sql
* @return array 表名列表
*/
private function _getNameFromQuery($sql) {
$matches = null;
//获取命令字
if(!preg_match('/^\s*(\w+)\b/i',$sql,$matches)){
throw new Exception("SQL query statement invalid:" . $sql);
}
//查询语句必须是Select开头,否则不承认
if(strtolower($matches[0]) != 'select'){
throw new Exception("Can't use '" . $matches[0] . "' in query statement.");
}
//匹配 表名部分 ,包括 From之后和Join之后
if(!preg_match_all('/from\s+([\w|`]+)(\s*,\s*([\w|`]+))*|join\s+([\w|`]+)/i',$sql,$matches)){
return array();
}
//收集匹配的表名
$names = array();
$range = array(
1,3,4
);
foreach($range as $i){
foreach($matches[$i] as $name){
if($name){
array_push($names,trim($name,'`'));
}
}
}
return $names;
}
/**
* 获取执行语句中的涉及表名
*
* @param string $sql SQL语句
* @return array 表名列表
*/
private function _getNameFromExecute($sql) {
$matches = null;
//获取命令字
if(!preg_match('/^(\w+)\b/i',$sql,$matches)){
throw new Exception("SQL query statement invalid:" . $sql);
}
//只支持Insert,Update,Delete,Replace语句,其它不支持
$command = strtolower($matches[0]);
if(!in_array($command,array(
'insert','update','delete','replace'
))){
throw new Exception("Don't support '" . $matches[0] . "' in execute statement.");
}
//匹配表名部分,包括 insert [into] $tbl , replace [into] $tbl, update $tbl, delete from $tbl
if(!preg_match_all('/insert\s+(into\s+)?([\w|`]+)|replace\s+(into\s+)?([\w|`]+)|delete\s+from\s+([\w|`]+)|update\s+([\w|`]+)/i',$sql,$matches)){
return array();
}
//收集匹配的表名
$names = array();
$range = array(
2,4,5,6
);
foreach($range as $i){
foreach($matches[$i] as $name){
if($name){
array_push($names,trim($name,'`'));
}
}
}
return $names;
}
/**
* 重载query方法,允许指定是否使用缓存,默认是使用缓存,
*
* @param string $sql SQL语句
* @param bool/string/array $cached 默认True,使用缓存,False:不使用缓存,表名列表:此查询与这些表相关.
* @return mixed 查询数据
*/
public function query($sql) {
//检查配置开关
if(!$this->_enable()){
return parent::_query($sql);
}
//重构表名列表
$tables = $this->_getNameFromQuery($sql);
if(!SConfig::system('multi_table') and count($tables)>1){
return $this->_err(__METHOD__,__LINE__,'disable multi table query:'.$sql);
}
//如果无法解析表名,不缓存,直接返回查询结果
if(!$tables or !count($tables)){
return parent::query($sql);
}
//从缓存中取
$data = SMemcache::get($sql);
if($data){
return $data;
}
//从数据库中取
$data = parent::query($sql);
//设置到缓存中,并与每一个表关联
foreach($tables as $name){
SMemcache::set($name,$sql,$data);
}
return $data;
}
/**
* 重载execute方法,允许指定是否使用缓存,默认是使用缓存
*
* @param string $sql 要执行的SQL语句
* @param bool/string/array $cached 默认True,使用缓存,False:不使用缓存,表名列表:此执行语句与这些表相关
* @return mixed 执行结果
*/
public function execute($sql) {
//检查配置开关
if(!$this->_enable()){
return parent::_execute($sql);
}
//重构相关表名
$tables = $this->_getNameFromExecute($sql);
if(!SConfig::system('multi_table') and count($tables)>1){
return $this->_err(__METHOD__,__LINE__,'disable multi table execute:'.$sql);
}
//如果可以解析表名 清除相关缓存
if($tables and count($tables)){
//清除相关表中的所有缓存.因为可能影响到
foreach($tables as $name){
SMemcache::clear($name);
}
}
return parent::execute($sql);
}
}