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

	private $_mem = null; //Memcache对象
	private $_servers = null; //服务器列表
	

	private $_tag = null;
	
	/**
	 * 结构函数,建立必要参数
	 *
	 * @param string $domain 域名
	 * @param string $class 子分类
	 */
	public function __construct($domain, $class = 'default') {
	    $isCache = defined('UTIL_MEMCACHED_CACHE') ? UTIL_MEMCACHED_CACHE : true;
        if($isCache) {
           $this->_servers = Util_Server::factory ( 'cache', 'memcache' )->loadBalanceServer ();
        } else {
           return;
        }
		$this->_mem = new Memcached();
		$this->_mem->setOption ( Memcached::OPT_COMPRESSION, TRUE );
		$this->_mem->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);
		$this->_mem->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
		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);
            $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 boolean
	 */
	public function set($key, $value, $expire = null) {
		if (($return = $this->setTagSig ( isset ( $expire ) ? $expire : 0 ))) {
			$key = $this->realKey ( $key );
			$return = $this->_mem->set ( $key, $value, isset ( $expire ) ? $expire : 0 );
		}
		$this->_resetTagName ();
		return $return;
	}
	/**
	 * 一次设置多个值
	 *
	 * @param array $values pairs
	 * @param integer $expire
	 * @return boolean
	 */
	public function setMulti($items, $expire = null) {
		if (! is_array ( $items )) {
			return false;
		}
		if (($return = $this->setTagSig ( isset ( $expire ) ? $expire : 0 ))) {
			$this->_getMultiRealKey ( $items );
			$return = $this->_mem->setMulti ( $items, 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->getMulti ( $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 boolean | 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 boolean | 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;
	}
	/**
	 * 得到存储的实际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) {
		return $this->_mem->get ( $this->realKey ( $key ) ) !== 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;
        }
		$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, $expire );
			}
		} else {
			$aContent = array ('prefix' => uniqid (), 'expire' => ($expire == 0 ? 0 : $iTime + $expire) );
			$return = $this->_mem->set ( $this->_tagSigName (), $aContent, $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 _getMultiRealKey(&$items) {
		$keys = array_keys ( $items );
		$values = array_values ( $items );
		$keys = array_map ( array ($this, 'realKey' ), $keys );
		$items = array_combine ( $keys, $values );
	}
    private function _checkServer() {
       	$isCache = defined('UTIL_MEMCACHED_CACHE') ? UTIL_MEMCACHED_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;
    }
}