Memcache.php 8.23 KB
<?php
class Q_Cache_Memcache extends Q_Cache_Abstract {
	
	private $_prefix;
	public function __construct(array $servers, $persistentID = '')
	{
		if (empty($servers)) {
			throw new Q_Cache_Exception(__CLASS__ . ' :  Servers Is Null');
		}
		$this->_unset();
		$prefix = $this->_prefix();
		$this->servers = $servers;
		#初始化memcache
		$this->initCache($persistentID);
		$this->setPrefix($prefix);
	}
	
	public function setPrefix($prefix)
	{
		$this->_prefix .= $prefix;
		return $this;
	}
	
	/**
	 * 获取系统前缀
	 * @return string
	 */
	public function _prefix()
	{
		return $this->domain . '.' . $this->class;
	}
	
	private function initCache($persistentID = '')
	{
		if (empty($this->_cache)) {
			$this->_cache = new Memcache ( );
			foreach ( $this->servers as $server )
				$this->_cache->addServer ( $server ['host'], $server ['port'] );
		}
		return $this->_cache;
	}
	
	/**
	 * 析构函数,释放内存
	 *
	 */
	public function __destruct() {
		if(is_object($this->_cache)) {
			$this->_cache->close();
		}
		$this->_cache = null;
		$this->servers = null;
	}
	/**
	 * 增加值
	 *
	 * @param string $key
	 * @param mixed $value
	 * @param integer $expire
	 * @return bool
	 */
	public function set($key, $value, $expire = null) {
		if (($return = $this->setTagSig ( isset ( $expire ) ? $expire : 0 ))) {
			$key = $this->realKey ( $key );
			if ($this->_testCached ( $key )) {
				if (isset ( $expire ))
					$return = $this->_cache->replace ( $key, $value, $this->_checkCompress ( $key, $value ), $expire );
				else
					$return = $this->_cache->replace ( $key, $value, $this->_checkCompress ( $key, $value ) );
			} else
				$return = $this->_cache->set ( $key, $value, $this->_checkCompress ( $key, $value ), isset ( $expire ) ? $expire : 0 );
			
		}
		$this->_resetTagName ();
		return $return;
	}
	/**
	 * 得到一个Key下面的Value
	 *
	 * @param string | array $key
	 * @return mixed| false | array 针对memcache是反序列化后的内容
	 */
	public function get($key) {
		if (! isset ( $key )) {
			return false;
		}
		$isArray = gettype ( $key ) == 'array';
		if ($this->_tagContent () === false) {
			return false;
		}
		if (is_scalar ( $key )) {
			$key = array ($key );
		}
		$realKey = array_map ( array ($this, 'realKey' ), $key );
		$realReturn = ($realReturn = $this->_cache->get ( $realKey )) === false ? array () : $realReturn;
		$realKey = array_combine ( $realKey, $key );
		$realReturn = $this->_realReturns ( $realReturn, $realKey );
		$this->_resetTagName ();
		return ! $isArray ? (isset ( $realReturn [$key [0]] ) ? $realReturn [$key [0]] : false) : (empty ( $realReturn ) ? false : $realReturn);
	}
	
	/**
	 * 键不存在,设置值
	 *
	 * @param string $key
	 * @param mixed $value
	 * @param $integer $expire
	 * @return integer | false
	 */
	public function add($key, $value,$expire = null) {
		if (($return = $this->setTagSig ( isset ( $expire ) ? $expire : 0 ))) {
			$key = $this->realKey ( $key );
			$return = $this->_cache->add($key, $value, false, isset ( $expire ) ? $expire : 0 );
		}
		$this->_resetTagName ();
		return $return;
	}
	
	private function _realReturns($data, $keyMap) {
		$return = array ();
		foreach ( $data as $k => $v ) {
			$return [$keyMap [$k]] = $v;
		}
		return $return;
	}
	/**
	 * 删除一个Key
	 *
	 * @param string | array $key
	 * @param integer $delay 延时时间,单位秒
	 * @return bool | array
	 */
	public function delete($key, $delay = 0) {
		$isArray = gettype ( $key ) == 'array';
		if ($this->_tagContent () === false) {
			return false;
		}
		if (is_scalar ( $key )) {
			$key = array ($key );
		}
		$return = array ();
		foreach ( $key as $v ) {
			$return [$v] = $this->_cache->delete ( $this->realKey ( $v ), $delay );
		}
		$this->_resetTagName ();
		return ! $isArray ? $return [$key [0]] : $return;
	}
	/**
	 * 将一个Key减1
	 *
	 * @param string $key
	 * @return integer | false
	 */
	public function decrement($key, $num = 1) {
		if ($this->_tagContent () === false)
			return false;
		$this->_resetTagName ();
		return $this->_cache->decrement ( $this->realKey ( $key ), $num );
	}
	/**
	 * 将一个Key增1
	 *
	 * @param string $key
	 * @return integer | false
	 */
	public function increment($key, $num = 1) {
		if ($this->_tagContent () === false)
			return false;
		$this->_resetTagName ();
		return $this->_cache->increment ( $this->realKey ( $key ), $num );
	}
	
	/**
	 * 删除TAG以及下面的所有ITEM,不传入$tag的话删除默认值下所有内容TAG
	 *
	 * @param string | array $tag
	 * @return bool | array
	 */
	public function deleteTag($tag = 'TAG', $delay = 0) {
	    if(!$this->_checkServer()) {
	        return false;
	    }
		$isArray = gettype ( $tag ) == 'array';
		if (is_scalar ( $tag )) {
			$tag = array ($tag );
		}
		$return = array ();
		foreach ( $tag as $v ) {
			$return [$v] = $this->_cache->delete ( md5 ( $this->_prefix . '_' . $v ), $delay );
		}
		return ! $isArray ? $return [$tag [0]] : $return;
	}
	/**
	 * 判定是否使用压缩
	 * 抽象出来是为了确定在实际应用时的需求,可以将是否压缩逻辑写在此处
	 *
	 * @param string $key
	 * @param mixed $value
	 * @return 0|1
	 */
	private function _checkCompress($key, $value) {
		return false;
	}
	/**
	 * 得到存储的实际Key
	 *
	 * @param string $key
	 * @return string
	 */
	protected function realKey($key) {
		if (($prefix = $this->_tagPrefix ())) {
			return md5 ( $this->_prefix . '_' . $prefix . '_' . $key );
		}
		else { //获取不到真实值,始终为空
			$prefix =  uniqid ();
			return md5($this->_prefix . '_' . $prefix . '_' . $key);
		}
	}
	/**
	 * 测试缓存是否命中
	 *
	 * @param string $key
	 * @return boolean
	 */
	private function _testCached($key) {
	    if($this->_checkServer())  {
		    return $this->_cache->get ( $this->realKey ( $key ) ) !== false;
	    } else{
	        return false;
	    }
		   
	}
	/**
	 * 使用TAG
	 *
	 * @param string $tag
	 * @return object $this
	 */
	public function tag($tag) {
		if ($tag == 'TAG')
			throw new Util_Cache_Exception ( '该tag名称:TAG被禁止直接使用!' );
		$this->_tag = $tag;
		return $this;
	}
	/**
	 * 设定TAG组标志
	 *
	 * @param integer $expire
	 * @return boolean
	 */
	protected function setTagSig($expire = 0) {
	    if(!$this->_checkServer()) {
	        return false;
	    }
		//FIXME 如果不传入expire(replace)情况下的处理
		$return = true;
		$iTime = time ();
		//下面主要为计算TAG过期时间以保证TAG过期时间<=所包含的Key的过期时间
		if (($aContent = $this->_tagContent ()) !== false) {
			if (($tmp = $aContent ['expire']) && $tmp > 0 && ($expire == 0 || $iTime + $expire > $tmp)) {
				$aContent ['expire'] = $expire == 0 ? 0 : $expire + $iTime;
				$return = $this->_cache->replace ( $this->_tagSigName (), $aContent, 0, $expire );
			}
		} else {
			$aContent = array ('prefix' => uniqid (), 'expire' => ($expire == 0 ? 0 : $iTime + $expire) );
			$return = $this->_cache->set ( $this->_tagSigName (), $aContent, 0, $expire );
		}
		return $return;
	}
	
	/**
	 * 得到TAG标志名称
	 *
	 * @return string
	 */
	private function _tagSigName() {
		$tag = isset ( $this->_tag ) ? $this->_tag : 'TAG';
		return md5 ( $this->_prefix . '_' . $tag );
	}
	/**
	 * 释放掉tag设置的tagname
	 *
	 */
	private function _resetTagName() {
		if (isset ( $this->_tag ))
			$this->_tag = null;
	}
	
	/**
	 * 得到TAG标志的资料
	 *
	 * @return array | false 内容
	 */
	private function _tagContent() {
	    if($this->_checkServer())  {
            return $this->_cache->get ( $this->_tagSigName () );
	    } else {
	        return false;
	    }
	}
	/**
	 * 得到tag过期时间,从cache存储中
	 *
	 * @param string $tag
	 * @return string | false
	 */
	private function _tagExpire() {
		return ($tag = $this->_tagContent ()) && isset ( $tag ['prefix'] ) ? $tag ['prefix'] : false;
	}
	/**
	 * 得到tag唯一前缀,从cache存储中
	 *
	 * @param string $tag
	 * @return string | false
	 */
	private function _tagPrefix() {
		return ($tag = $this->_tagContent ()) && isset ( $tag ['prefix'] ) ? $tag ['prefix'] : false;
	}
	
	private function _checkServer() {
	    if(count($this->servers)) {
       		foreach($this->servers as $server) {
       			if(Q_Server_CheckServer::check($server['host'], $server['port'], 'memcache')) {
       				return true;
       			}
       		}
       	}
       	return true;
	}
}