为Table类增加缓存功能
<P><?php<BR>/**<BR> * 在STable类上扩展缓存功能<BR> * @author ice<BR> *<BR> */<BR>class SCachedTable extends STable {</P><P> public function __construct($tableName) {<BR> parent::__construct($tableName);<BR> }</P>
<P> //是否临时禁止缓存<BR> private $temporaryDisableCache = false;</P>
<P> /**<BR> * 临时禁止缓存,只禁止一次<BR> * @return SCachedTable<BR> */<BR> public function disCache() {<BR> $this->temporaryDisableCache = true;<BR> return $this;<BR> }</P>
<P> /**<BR> * 检查是否允许使用Memcache<BR> *<BR> * @return unknown<BR> */<BR> private function _enable() {<BR> //如果已经临时禁止了缓存<BR> if($this->temporaryDisableCache){<BR> //下次不再临时禁止<BR> $this->temporaryDisableCache = false;<BR> return false;<BR> }</P>
<P> //检查缓存是否开启<BR> return SMemcache::enable();<BR> }</P>
<P> /**<BR> * 具体执行数据库查询,如果缓存里没有,则查询并保存到缓存中<BR> *<BR> * @param string $sql<BR> * @return 查询结果<BR> */<BR> protected function _query($sql) {<BR> //如果缓存未开启,则直接查询数据库<BR> if(!$this->_enable()){<BR> return parent::_query($sql);<BR> }</P>
<P> //从缓存中取<BR> $data = SMemcache::get($sql);</P>
<P> //如果缓存未中<BR> if(!$data){<BR> //从数据库查询<BR> $data = parent::_query($sql);</P>
<P> //保存到缓存中<BR> SMemcache::set($this->tableName,$sql,$data);<BR> }</P>
<P> return $data;<BR> }</P>
<P> /**<BR> * 具体执行数据库语句,先清除本表的缓存,再执行<BR> *<BR> * @param string $sql<BR> * @return boolean 执行是否成功<BR> */<BR> protected function _execute($sql) {<BR> //检查配置开关<BR> if($this->_enable()){<BR> SMemcache::clear($this->tableName);<BR> }<BR> return parent::_execute($sql);<BR> }</P>
<P> /**<BR> * 获取查询语句中的涉及表名<BR> *<BR> * @param string $sql<BR> * @return array 表名列表<BR> */<BR> private function _getNameFromQuery($sql) {<BR> $matches = null;</P>
<P> //获取命令字<BR> if(!preg_match('/^\s*(\w+)\b/i',$sql,$matches)){<BR> throw new Exception("SQL query statement invalid:" . $sql);<BR> }</P>
<P> //查询语句必须是Select开头,否则不承认<BR> if(strtolower($matches) != 'select'){<BR> throw new Exception("Can't use '" . $matches . "' in query statement.");<BR> }</P>
<P> //匹配 表名部分 ,包括 From之后和Join之后<BR> if(!preg_match_all('/from\s+([\w|`]+)(\s*,\s*([\w|`]+))*|join\s+([\w|`]+)/i',$sql,$matches)){<BR> return array();<BR> }</P>
<P> //收集匹配的表名<BR> $names = array();<BR> $range = array(<BR> 1,3,4<BR> );<BR> foreach($range as $i){<BR> foreach($matches[$i] as $name){<BR> if($name){<BR> array_push($names,trim($name,'`'));<BR> }<BR> }<BR> }<BR> return $names;<BR> }</P>
<P> /**<BR> * 获取执行语句中的涉及表名<BR> *<BR> * @param string $sql SQL语句<BR> * @return array 表名列表<BR> */<BR> private function _getNameFromExecute($sql) {<BR> $matches = null;</P>
<P> //获取命令字<BR> if(!preg_match('/^(\w+)\b/i',$sql,$matches)){<BR> throw new Exception("SQL query statement invalid:" . $sql);<BR> }</P>
<P> //只支持Insert,Update,Delete,Replace语句,其它不支持<BR> $command = strtolower($matches);<BR> if(!in_array($command,array(<BR> 'insert','update','delete','replace'<BR> ))){<BR> throw new Exception("Don't support '" . $matches . "' in execute statement.");<BR> }</P>
<P> //匹配表名部分,包括 insert $tbl , replace $tbl, update $tbl, delete from $tbl<BR> 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)){<BR> return array();<BR> }</P>
<P> //收集匹配的表名<BR> $names = array();<BR> $range = array(<BR> 2,4,5,6<BR> );<BR> foreach($range as $i){<BR> foreach($matches[$i] as $name){<BR> if($name){<BR> array_push($names,trim($name,'`'));<BR> }<BR> }<BR> }<BR> return $names;<BR> }</P>
<P> /**<BR> * 重载query方法,允许指定是否使用缓存,默认是使用缓存,<BR> *<BR> * @param string $sql SQL语句<BR> * @param bool/string/array $cached 默认True,使用缓存,False:不使用缓存,表名列表:此查询与这些表相关.<BR> * @return mixed 查询数据<BR> */<BR> public function query($sql) {<BR> //检查配置开关<BR> if(!$this->_enable()){<BR> return parent::_query($sql);<BR> }</P>
<P> //重构表名列表<BR> $tables = $this->_getNameFromQuery($sql);<BR> <BR> if(!SConfig::system('multi_table') and count($tables)>1){<BR> return $this->_err(__METHOD__,__LINE__,'disable multi table query:'.$sql);<BR> }</P>
<P> //如果无法解析表名,不缓存,直接返回查询结果<BR> if(!$tables or !count($tables)){<BR> return parent::query($sql);<BR> }</P>
<P> //从缓存中取<BR> $data = SMemcache::get($sql);<BR> if($data){<BR> return $data;<BR> }</P>
<P> //从数据库中取<BR> $data = parent::query($sql);</P>
<P> //设置到缓存中,并与每一个表关联<BR> foreach($tables as $name){<BR> SMemcache::set($name,$sql,$data);<BR> }</P>
<P> return $data;<BR> }</P>
<P> /**<BR> * 重载execute方法,允许指定是否使用缓存,默认是使用缓存<BR> *<BR> * @param string $sql 要执行的SQL语句<BR> * @param bool/string/array $cached 默认True,使用缓存,False:不使用缓存,表名列表:此执行语句与这些表相关<BR> * @return mixed 执行结果<BR> */<BR> public function execute($sql) {<BR> //检查配置开关<BR> if(!$this->_enable()){<BR> return parent::_execute($sql);<BR> }</P>
<P> //重构相关表名<BR> $tables = $this->_getNameFromExecute($sql);<BR> <BR> if(!SConfig::system('multi_table') and count($tables)>1){<BR> return $this->_err(__METHOD__,__LINE__,'disable multi table execute:'.$sql);<BR> }</P>
<P> //如果可以解析表名 清除相关缓存<BR> if($tables and count($tables)){<BR> //清除相关表中的所有缓存.因为可能影响到<BR> foreach($tables as $name){<BR> SMemcache::clear($name);<BR> }<BR> }</P>
<P> return parent::execute($sql);<BR> }<BR>}</P>
<DIV> </DIV>
<DIV> </DIV>
<DIV><?php<BR>/**<BR> * Memcache缓存类<BR> * 增强域功能<BR> */<BR>final class SMemcache {<BR> //具体缓存服务器的句柄<BR> private static $handles=array();</DIV>
<DIV> //缓存配置<BR> private static $config;</DIV>
<DIV> //获取缓存配置,由于配置类使用缓存功能,所以缓存类不可以使用配置类<BR> private static function config() {<BR> if(!self::$config){<BR> self::$config = require (DIR_CONFIG . 'memcache.config.php');<BR> }<BR> return self::$config;<BR> }</DIV>
<DIV> /**<BR> * 判断是否允许缓存<BR> *<BR> * @return bool<BR> */<BR> public static function enable() {<BR> $config = self::config();<BR> return $config['enable'];<BR> }</DIV>
<DIV> /**<BR> * 建立与Memcache的长连接<BR> *<BR> * @return 连接句柄<BR> */<BR> private static function connect($n) {<BR> if(!isset(self::$handles[$n]) or !self::$handles[$n]){<BR> self::$handles[$n] = new Memcache();<BR> $config = self::config();<BR> if(!self::$handles[$n]->pconnect($config[$n]['host'],$config[$n]['port'])){<BR> throw new Exception('Memcache connect fail');<BR> }<BR> }</DIV>
<DIV> return self::$handles[$n];<BR> }<BR> <BR> /**<BR> * 对指定的缓存键进行散列,返回服务器编号<BR> * @param string $key 要缓存的内容的键<BR> * @return int 服务器编号(1->m)<BR> */<BR> private static function hash($key){<BR> return abs(crc32($key)) % self::$config['servers'] +1;<BR> }</DIV>
<DIV> /**<BR> * 根据键获取Memcache的句柄,以便直接使用.<BR> *<BR> * @param string $key 缓存内容的键<BR> * @return Memcache 缓存类实例<BR> */<BR> public static function getHandle($key) {<BR> return self::connect(self::hash($key));<BR> }</DIV>
<DIV> /**<BR> * 清除指定表的所有缓存数据<BR> *<BR> * @param string $field 域<BR> */<BR> public static function clear($field) {<BR> for($i=1;$i<=self::$config['servers'];$i++){<BR> $handle = self::connect($i);<BR> <BR> $keys = $handle->get('Field_' . $field);<BR> if($keys){<BR> foreach($keys as $key){<BR> $handle->delete($key);<BR> }<BR> $handle->delete('Field_' . $field);<BR> }<BR> }<BR> }</DIV>
<DIV> /**<BR> * 缓存一条数据<BR> *<BR> * @param string $table 表名<BR> * @param string $key 键(实际是查询语句)<BR> * @param fixed $data 数据<BR> */<BR> public static function set($field , $key , $data) {<BR> $handle = self::connect(self::hash($key));</DIV>
<DIV> //缓存数据<BR> $key = md5($key);<BR> $handle->set($key,$data,0,0);</DIV>
<DIV> //取出此表已经缓存的所有键<BR> $keys = $handle->get('Field_' . $field);<BR> if(!$keys){<BR> $keys = array();<BR> }</DIV>
<DIV> //增加一个键,并保存回去,以便以后统一清除<BR> $keys[] = $key;<BR> $handle->set('Field_' . $field,$keys,0,0);<BR> }</DIV>
<DIV> /**<BR> * 从缓存中取一条数据<BR> *<BR> * @param string $table 表名<BR> * @param string $key 键名<BR> * @return fixed 数据<BR> */<BR> public static function get($key) {<BR> $handle = self::connect(self::hash($key));</DIV>
<DIV> return $handle->get(md5($key));<BR> }<BR>}</DIV>
页:
[1]