蓝冰大侠 发表于 2011-12-21 08:44

为Table类增加缓存功能

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