Memcache.class.php 8.37 KB
<?php
class Util_Cache_Adapter_Memcache implements Util_Cache_Adapter_Interface {
	private $_domain = 'www.yoho.cn'; //域名
	private $_class = 'default'; //分类
	private $_prefix = ''; //前缀
	

	private $_mem = null; //Memcache.class对象
	private $_servers = null; //服务器列表
	
	private $_tag = null;
	
	/**
	 * 结构函数,建立必要参数
	 *
	 * @param string $domain 域名
	 * @param string $class 子分类
	 */
	public function __construct($domain, $class = 'default') {
	    $isCache = defined('UTIL_MEMCACHE_CACHE') ? UTIL_MEMCACHE_CACHE : true;
	    if($isCache) {
	       $this->_servers = Util_Server::factory ( 'cache', 'memcache' )->loadBalanceServer ();
	    } else {
	       return;
	    }
		$this->_mem = new Memcache ( );
		if (count ( $this->_servers ) == 0 || !is_array($this->_servers)) {
		    //throw new Util_Cache_Exception ( get_class ( $this ) . ':没有备选服务器!' );
		    $log = sprintf("all memcache down at time: %s\n", date('Y-m-d H:i:s',time()));
//		    file_put_contents ( dirname ( __FILE__ ) . '/memcache.down.log', $log);
          //  @file_put_contents ( '/tmp/logs/memcache.down.log', $log);
		    $this->_servers = array();
		    return;
		}
		foreach ( $this->_servers as $server )
			$this->_mem->addServer ( $server ['host'], $server ['port'] );
		$this->_domain = $domain;
		$this->_class = $class;
		$this->_prefix = $domain . '_' . $class;
	}
	/**
	 * 析构函数,释放内存
	 *
	 */
	public function __destruct() {
		$this->_mem = 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->_mem->replace ( $key, $value, $this->_checkCompress ( $key, $value ), $expire );
				else
					$return = $this->_mem->replace ( $key, $value, $this->_checkCompress ( $key, $value ) );
			} else
				$return = $this->_mem->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->_mem->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);
	}
	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->_mem->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->_mem->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->_mem->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->_mem->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->_mem->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->_mem->replace ( $this->_tagSigName (), $aContent, 0, $expire );
			}
		} else {
			$aContent = array ('prefix' => uniqid (), 'expire' => ($expire == 0 ? 0 : $iTime + $expire) );
			$return = $this->_mem->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->_mem->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() {
	   	$isCache = defined('UTIL_MEMCACHE_CACHE') ? UTIL_MEMCACHE_CACHE : true;
       	if(!$isCache) {
            	return false;
       	}
	    if(count($this->_servers)) {
       		foreach($this->_servers as $server) {
       			if(Util_Server_Adapter_CheckServer::check($server['host'], $server['port'], 'memcache')) {
       				return true;
       			}
       		}
       	}
       	return false;
	}
}