diff --git a/core/classes/storage.class.php b/core/classes/storage.class.php new file mode 100644 index 0000000..dd172eb --- /dev/null +++ b/core/classes/storage.class.php @@ -0,0 +1,231 @@ +config = $config_name; + } + else + { + $this->config = Core::config('storage.' . $config_name); + } + + if (!isset($this->config['driver'])) + { + $this->config['driver'] = Storage::DRIVER_FILE; + } + + $driver = 'Storage_Driver_' . $this->config['driver']; + if (!class_exists($driver,true)) + { + throw new Exception(__('The :type driver :driver does not exist',array(':type'=>'Storge',':driver'=>$this->config['driver']))); + } + + $this->driver = new $driver($this->config['driver_config']); + + # 设置前缀 + if ($this->config['prefix']) + { + $this->driver->set_prefix($this->config['prefix']); + } + } + + /** + * 获取指定KEY的缓存数据 + * + * $strone->get('a'); + * $strone->get('a','b','c'); + * $strone->get(array('a','b','c')); + * + * @param string $key 指定key + * @return mixed + * @return false 返回失败 + */ + public function get($key) + { + $columns = func_get_args(); + if (count($columns)>1) + { + $key = $columns; + } + + if (null===$key) + { + return null; + } + + return $this->driver->get($key); + } + + /** + * 保存内容 + * + * @param $key string + * @param $value fixed + * @return boolean + */ + public function set($key, $value) + { + return $this->driver->set($key, $value); + } + + /** + * 是否存在关键字的数据 + * + * @param string $key + * @return boolean + */ + public function exists($key) + { + return $this->driver->exists($key); + } + + /** + * 删除指定key的缓存数据 + * + * @param string $key + * @return boolean + */ + public function delete($key) + { + return $this->driver->delete($key); + } + + /** + * 删除全部缓存 + * + * delete_all()的别名 + * + * @return boolean + */ + public function clean() + { + return $this->delete_all(); + } + + /** + * 删除全部缓存 + * + * @return boolean + */ + public function delete_all() + { + return $this->driver->delete_all(); + } + + public function __get($key) + { + return $this->get($key); + } + + public function __set($key, $value) + { + return $this->set($key, $value); + } + + public function __unset($key) + { + return $this->delete($key); + } + + public function __call($method, $params) + { + try + { + return call_user_func_array(array($this->driver,$method) , $params); + } + catch (Exception $e) + { + $this->last_error_msg = $e->getMessage(); + $this->last_error_no = $e->getCode(); + return false; + } + } + +} \ No newline at end of file diff --git a/core/classes/storage/driver.class.php b/core/classes/storage/driver.class.php new file mode 100644 index 0000000..a61f208 --- /dev/null +++ b/core/classes/storage/driver.class.php @@ -0,0 +1,124 @@ +config = $config; + } + } + + /** + * 设置前缀 + * + * @param string $prefix + * @return $this + */ + public function set_prefix($prefix) + { + if ($prefix) + { + $this->prefix = trim($prefix, ' /_') . '_'; + } + else + { + $prefix = ''; + } + + return $this; + } + + /** + * 取得数据 + * + * @param string/array $key + * @return mixed + */ + abstract public function get($key); + + /** + * 存数据 + * + * @param string/array $key 支持多存 + * @param $data Value 多存时此项可空 + * @return boolean + */ + abstract public function set($key, $value = null); + + /** + * 删除指定key的缓存,若$key===true则表示删除全部 + * + * @param string $key + */ + abstract public function delete($key); + + /** + * 删除全部 + * + * @return boolean + */ + abstract public function delete_all(); + + protected function _de_format_data(&$data) + { + if (null===$data || is_bool($data)) + { + # bool类型不处理 + } + elseif (!is_numeric($data)) + { + # 解压 + if (substr($data,0,14)=='::gzcompress::') + { + $data = @gzuncompress(substr($data, 14)); + } + + # 解序列化 + if (substr($data,0,13)=='::serialize::') + { + $data = @unserialize(substr($data, 13)); + } + } + } + + protected function _format_data(&$data) + { + if (!is_numeric($data) && !is_string($data)) + { + # 序列化 + $data = '::serialize::' . serialize($data); + + # 压缩 + if ($this->compress) + { + $data = '::gzcompress::' . @gzcompress($data,9); + } + } + } +} \ No newline at end of file diff --git a/core/classes/storage/driver/database.class.php b/core/classes/storage/driver/database.class.php new file mode 100644 index 0000000..a6a4b6b --- /dev/null +++ b/core/classes/storage/driver/database.class.php @@ -0,0 +1,227 @@ +database = $config['database']; + $this->tablename = $config['tablename']; + + if (!$this->tablename) + { + throw new Exception(__('Database storage configuration error')); + } + + $this->_handler = new Database(array('type'=>$config['type'], 'connection'=>$config)); + } + + public function __destruct() + { + $this->_handler->close_connect(); + } + + /** + * 取得数据,支持批量取 + * @param string/array $key + * @return mixed + */ + public function get($key) + { + if (is_array($key)) + { + $md5_key = array(); + $key_map = array(); + foreach ($key as &$k) + { + $key_map[$this->prefix . $k] = $k; + $k = $this->prefix . $k; + $md5_key[] = md5($k); + } + + $this->_handler->in('key', $md5_key); + } + else + { + $key = $this->prefix . $key; + + $this->_handler->where('key', md5($key))->limit(1); + } + + $rs = $this->_handler->select('key_string', 'value')->from($this->tablename)->get(); + + if ($rs->count()) + { + if (is_array($key)) + { + $return = array(); + foreach ($rs as $data) + { + $data_key = $key_map[$data['key_string']]; + $return[$data_key] = $data['value']; + $this->_de_format_data($return[$data_key]); + } + } + else + { + $return = $rs->current(); + $return = $return['value']; + $this->_de_format_data($return); + } + + if (IS_DEBUG)Core::debug()->info($key, 'database storage hit key'); + + unset($rs); + + return $return; + } + else + { + if (IS_DEBUG)Core::debug()->error($key, 'database storage mis key'); + } + + return false; + } + + /** + * 存数据 + * + * @param string/array $key 支持多存 + * @param $data Value 多存时此项可空 + * @param $lifetime 有效期,默认3600,即1小时,0表示不限制 + * @return boolean + */ + public function set($key, $value = null) + { + if (IS_DEBUG)Core::debug()->info($key, 'database storage set key'); + + if (is_array($key)) + { + foreach ($key as $k=>$v) + { + $k = $this->prefix . $k; + + $this->_format_data($value[$k]); + + $data = array + ( + md5($k), + $k, + $value[$k], + ); + + $this->_handler->values($data); + } + } + else + { + $key = $this->prefix . $key; + + $this->_format_data($value); + $data = array + ( + md5($key), + $key, + $value, + ); + + $this->_handler->values($data); + } + + $rs = $this->_handler->columns(array('key', 'key_string', 'value'))->replace($this->tablename); + + if ($rs[0]) + { + return true; + } + else + { + return false; + } + } + + /** + * 删除指定key的缓存,若$key===true则表示删除全部 + * + * @param string $key + */ + public function delete($key) + { + if (IS_DEBUG)Core::debug()->info($key, 'database storage delete key'); + + if (is_array($key)) + { + $new_keys = array(); + foreach ($key as $k) + { + $k = $this->prefix . $k; + $new_keys[] = md5($k); + } + + $this->_handler->in('key', $new_keys); + } + elseif (true!==$key) + { + $key = $this->prefix . $key; + $this->_handler->where('key', $key); + } + + try + { + $this->_handler->delete($this->tablename); + return true; + } + catch (Exception $e) + { + Core::debug()->error($e->getMessage()); + return false; + } + } + + /** + * 删除全部 + */ + public function delete_all() + { + return $this->delete(true); + } +} \ No newline at end of file diff --git a/core/classes/storage/driver/file.class.php b/core/classes/storage/driver/file.class.php new file mode 100644 index 0000000..a8a79fd --- /dev/null +++ b/core/classes/storage/driver/file.class.php @@ -0,0 +1,187 @@ +storage = $config['storage']; + } + + $this->dir = DIR_SYSTEM . 'data' . DS . 'storage' . DS; + } + + /** + * 取得数据,支持批量取 + * + * @param string/array $key + * @return mixed + */ + public function get($key) + { + if (is_array($key)) + { + # 支持多取 + $data = array(); + foreach ($key as $k=>$v) + { + $data[$k] = $this->get((string)$v); + } + return $data; + } + + $filename = $this->get_filename_by_key($key); + + if (file_exists($filename)) + { + $data = @file_get_contents($filename); + + $this->_de_format_data($data); + return $data; + } + else + { + return null; + } + } + + /** + * 存数据 + * + * @param string/array $key 支持多存 + * @param $data Value 多存时此项可空 + * @param $lifetime 有效期,默认3600,即1小时,0表示最大值30天(2592000) + * @return boolean + */ + public function set($key, $value = null) + { + if (is_array($key)) + { + # 支持多存 + $i=0; + foreach ($key as $k=>$v) + { + if ($this->set((string)$k, $v)) + { + $i++; + } + } + + return $i==count($key)?true:false; + } + + $filename = $this->get_filename_by_key($key); + + $this->_format_data($value); + + return File::create_file($filename, $value, null, null, $this->storage); + } + + /** + * 删除指定key的缓存,若$key===true则表示删除全部 + * + * @param string $key + */ + public function delete($key) + { + if (true===$key) + { + # 删除全部 + return File::remove_dir($this->dir . $this->prefix, $this->storage); + } + + if (is_array($key)) + { + # 支持多取 + $data = array(); + $i=0; + foreach ($key as $k=>$v) + { + if ($this->delete((string)$v)) + { + $i++; + } + } + return $i==count($key)?true:false; + } + + $filename = $this->get_filename_by_key($key); + + if (!file_exists($filename)) + { + return true; + } + + return File::unlink($filename, $this->storage); + } + + /** + * 删除全部 + */ + public function delete_all() + { + return $this->delete(true); + } + + /** + * 根据KEY获取文件路径 + * + * @param string $key + */ + protected function get_filename_by_key($key) + { + return $this->dir . $this->prefix . 'storage_' . substr(preg_replace('#[^a-z0-9_\-]*#i','',$key), 0, 50) . '_' . md5($key); + } + + /** + * 设置前缀 + * + * @param string $prefix + * @return $this + */ + public function set_prefix($prefix) + { + if ($prefix) + { + $this->prefix = trim($prefix, ' /_') . '/'; + } + else + { + $prefix = ''; + } + + return $this; + } +} \ No newline at end of file diff --git a/core/classes/storage/driver/redis.class.php b/core/classes/storage/driver/redis.class.php new file mode 100755 index 0000000..1646e42 --- /dev/null +++ b/core/classes/storage/driver/redis.class.php @@ -0,0 +1,312 @@ +servers = $config_name; + $config_name = md5(serialize($config_name)); + } + else + { + $this->servers = Core::config('storage/redis.' . $config_name); + } + + if (!is_array($this->servers)) + { + throw new Exception(__('The storage redis config :config does not exist', array(':config'=>$config_name))); + } + $this->config_name = $config_name; + + $this->_connect(); + + # 增加自动关闭连接列队 + Core::add_close_connect_class('Storage_Driver_Redis'); + } + + public function __destruct() + { + $this->close_connect(); + } + + /** + * 连接服务器 + */ + protected function _connect() + { + if ($this->_redis)return; + if (!$this->config_name)return; + + $config_name = $this->config_name; + + if (!isset(Storage_Driver_Redis::$redis[$config_name])) + { + $class = 'Redis'; + Storage_Driver_Redis::$redis[$config_name] = new $class(); + Storage_Driver_Redis::$redis_num[$config_name] = 0; + + foreach ($this->servers as $server) + { + $server += array + ( + 'host' => '127.0.0.1', + 'port' => 6379, + 'persistent' => true, + 'timeout' => 2, + ); + + if ($server['persistent']) + { + $action = 'pconnect'; + } + else + { + $action = 'connect'; + } + + try + { + $time = microtime(1); + $status = Storage_Driver_Redis::$redis[$config_name]->$action($server['host'], $server['port'],$server['timeout']); + $time = microtime(1)-$time; + } + catch (Exception $e) + { + $status = false; + } + + if ($status) + { + if (IS_DEBUG)Core::debug()->info('connect storage redis server '.$server['host'].':'.$server['port'] . ' time:'.$time); + break; + } + else + { + if (IS_DEBUG)Core::debug()->error('error connect storage redis server '.$server['host'].':'.$server['port'] . ' time:'.$time); + } + } + } + + # 断开引用关系 + unset($this->_redis); + + # 设置对象 + $this->_redis = & Storage_Driver_Redis::$redis[$config_name]; + + Storage_Driver_Redis::$redis_num[$config_name]++; + } + + /** + * 关闭连接 + */ + public function close_connect() + { + if ( $this->config_name && $this->_redis ) + { + unset($this->_redis); + Storage_Driver_Redis::$redis_num[$this->config_name]--; + + if ( 0 == Storage_Driver_Redis::$redis_num[$this->config_name] ) + { + @Storage_Driver_Redis::$redis[$this->config_name]->close(); + + if (IS_DEBUG)Core::debug()->info('close storage redis server.'); + + Storage_Driver_Redis::$redis[$this->config_name] = null; + unset(Storage_Driver_Redis::$redis[$this->config_name]); + unset(Storage_Driver_Redis::$redis_num[$this->config_name]); + } + } + } + + /** + * 取得数据,支持批量取 + * + * @param string/array $key + * @return mixed + */ + public function get($key) + { + $this->_connect(); + + $time = microtime(1); + if (is_array($key)) + { + # redis多取 + if ($this->prefix) + { + foreach ($key as &$k) + { + $k = $this->prefix . $k; + } + } + + $return = $this->_redis->mget($key); + + foreach ( $return as &$item ) + { + $this->_de_format_data($item); + } + } + else + { + $return = $this->_redis->get($key); + + $this->_de_format_data($return); + } + $time = microtime(1) - $time; + + if (false===$return) + { + Core::debug()->error($key,'storage redis mis key'); + Core::debug()->info($time,'use time'); + + return false; + } + else + { + Core::debug()->info($key,'storage redis hit key'); + Core::debug()->info($time,'use time'); + } + + return $return; + } + + /** + * 存数据,支持多存 + * + * @param string/array $key + * @param $data Value 多存时此项可空 + * @return boolean + */ + public function set($key, $value = null) + { + $this->_connect(); + Core::debug()->info($key,'storage redis set key'); + + if ( is_array($key) ) + { + foreach ($key as & $item) + { + $this->_format_data($item); + } + return $this->_redis->mset($key); + } + else + { + $this->_format_data($value); + return $this->_redis->set($key, $value); + } + } + + /** + * 删除指定key的缓存,若$key===true则表示删除全部 + * + * @param string $key + */ + public function delete($key) + { + $this->_connect(); + if ($key === true) + { + + return $this->_redis->flushAll(); + } + else + { + $keys = func_get_args(); + return $this->_redis->delete($keys); + } + } + + /** + * 删除全部 + */ + public function delete_all() + { + return $this->delete(true); + } + + public function __call($method, $params) + { + $this->_connect(); + + if (method_exists($this->_redis, $method)) + { + return call_user_func_array(array($this->_redis,$method), $params); + } + } + + /** + * 关闭所有链接 + */ + public static function close_all_connect() + { + foreach (Storage_Driver_Redis::$redis as $config_name=>$obj) + { + try + { + $obj->close(); + } + catch (Exception $e) + { + Core::debug()->error('close redis storage connect error:'.$e); + } + + Storage_Driver_Redis::$redis[$config_name] = null; + } + + # 重置全部数据 + Storage_Driver_Redis::$redis = array(); + Storage_Driver_Redis::$redis_num = array(); + + if (IS_DEBUG)Core::debug()->info('close all storage redis server.'); + } +} diff --git a/core/classes/storage/driver/swift.class.php b/core/classes/storage/driver/swift.class.php new file mode 100644 index 0000000..ddb8735 --- /dev/null +++ b/core/classes/storage/driver/swift.class.php @@ -0,0 +1,798 @@ +account = $config['user']; + $this->key = $config['pass']; + $this->host = $config['host']; + $this->protocol = $config['scheme']; + + if (!$config['port']) + { + $this->port = $this->protocol=='https'?443:80; + } + + if ($config['path']) + { + $this->set_prefix($config['path']); + } + + if (!$this->host) + { + throw new Exception(__('The storage swift config :config does not exist', array(':config'=>$config_name))); + } + + # 增加自动关闭连接列队 + Core::add_close_connect_class('Storage_Driver_Swift'); + } + + /** + * 取得数据 + * + * @param string/array $key + * @return mixed + */ + public function get($key) + { + if (is_array($key)) + { + $rs = array(); + foreach ($key as $k) + { + $rs[$k] = $this->get($k); + } + + return $rs; + } + + $rs = $this->get_response($key, 'GET'); + + if ($rs['code']>=200 && $rs['code']<300) + { + $this->_de_format_data($rs['body']); + + return $rs['body']; + } + + if ($rs['code']==404)return null; + + throw new Exception(__('Swift get error, code: :code.', array(':code'=>$rs['code']))); + } + + /** + * 存数据 + * + * @param string/array $key 支持多存 + * @param $data Value 多存时此项可空 + * @return boolean + */ + public function set($key, $value = null) + { + if (is_array($key)) + { + $rs = true; + foreach ($key as $k=>$v) + { + if (!$this->set($k, $v)) + { + $rs = false; + } + } + + return $rs; + } + + $this->_format_data($value); + + $rs = $this->get_response($key, 'PUT', null, null, $value); + + if ($rs['code']>=200 && $rs['code']<300)return true; + + throw new Exception(__('Swift get error, code: :code.', array(':code'=>$rs['code']))); + } + + /** + * 删除指定key的缓存,若$key===true则表示删除全部 + * + * @param string $key + */ + public function delete($key) + { + if (is_array($key)) + { + $rs = true; + foreach ($key as $k) + { + if (!$this->delete($k)) + { + $rs = false; + } + } + + return $rs; + } + + $rs = $this->get_response('*', 'DELETE'); + + if ($rs['code']>=200 && $rs['code']<300)return true; + if ($rs['code']==404)return true; + + throw new Exception(__('Swift get error, code: :code.', array(':code'=>$rs['code']))); + } + + /** + * 删除全部 + * + * @return boolean + */ + public function delete_all() + { + //TODO 暂不支持 + + return false; + } + + /** + * 设置前缀 + * + * @param string $prefix + * @return $this + */ + public function set_prefix($prefix) + { + if ($prefix) + { + $this->prefix = trim($prefix, ' /_'); + } + else + { + $prefix = 'default'; + } + + return $this; + } + + /** + * 获取一个请求 + * + * @param string $url + * @param string $method + * @param array $headers + * @param array $query + * @param string $body + * @param int $chunk_size + * @return string + */ + protected function get_response($uri, $method, $headers = array(), $query = array(), $body = null, $chunk_size = 10240) + { + $method = strtoupper($method); + + # 获取连接对象 + $fp = $this->connection(); + + # 计数统计 + Storage_Driver_Swift::$requests_num[$this->storage_host]++; + + $metadata = stream_get_meta_data($fp); + + if ((Storage_Driver_Swift::$requests_num[$this->storage_host] % 100)===0 || true===$metadata['timed_out']) + { + unset($fp); + + $fp = $this->connection(true); + } + unset($metadata); + + if (!$headers)$headers = array(); + if (!$query)$query = array(); + + # 处理头信息 + if (!isset($headers['Content-Length'])) + { + if ($body) + { + if (is_resource($body)) + { + $headers['Transfer-Encoding'] = 'chunked'; + } + else + { + $headers['Content-Length'] = strlen($body); + } + } + else + { + $headers['Content-Length'] = '0'; + } + } + + $prepped_url = parse_url($this->storage_url); + + $host = $prepped_url['host']; + $path = $prepped_url['path']; + + $uri = $path . '/' . $this->prefix . '/' . $uri; + $headers['Host'] = $host; + + if (IS_DEBUG)Core::debug()->info($this->storage_protocol .'://'. $host . ($this->port!=80&&$this->port!=443?':'.$this->port:'') . $uri, 'Swift '.$method); + + # 拼接头信息 + $message = $this->build_request_line($method, $uri, $query) . $this->build_headers($headers); + + fwrite($fp, $message); + + # 输入内容 + if ($body) + { + if (is_resource($body)) + { + while (!feof($body)) + { + $data = fread($body, $chunk_size); + $len = dechex(strlen($data)); + fwrite($fp, $len . "\n" . $data . "\r\n"); + } + # 指针移回去 + rewind($body, 0); + + # HTTP结束符 + fwrite($fp, "0\r\n\r\n"); + } + else + { + fwrite($fp, $body); + } + } + + $rs = $this->read($fp); + + if (IS_DEBUG)Core::debug()->info('Swift get code:' . $rs['code']); + + return $rs; + } + + /** + * 获取HTTP协议内容 + * + * 将返回 + * + * array + * ( + * 'header' => array(), + * 'code' => 200, + * 'proto' => 'HTTP/1.1', + * 'body' => '', + * ) + * + * @param fsockopen $fp + * @return array + */ + protected function read($fp) + { + $rs = array + ( + 'code' => 0, + 'header' => array(), + 'body' => '', + ); + + # 读取第一行 + $head_line = fgets($fp); + + if (preg_match('#^(HTTP/[0-9\.]+) ([0-9]+) ([a-z0-9 ]+)?$#i', trim($head_line), $m)) + { + $rs['proto'] = $m[1]; + $rs['code'] = (int)$m[2]; + } + else + { + throw new Exception(__('Swift get data error.Data: :data', array(':data'=>$head_line))); + } + + # 是否分片读取 + $transfer_encoding = false; + + # 内容长度 + $content_length = 0; + + # 是否读取body部分 + $read_body = false; + + $switch = false; + + while (true) + { + if ($switch) + { + # 退出 + break; + } + + if ($read_body) + { + if ($transfer_encoding) + { + $chunk = trim(fgets($fp)); + $chunk_length = hexdec($chunk); + + if ($chunk_length>0) + { + $rs['body'] .= fread($fp, $chunk_length); + } + else + { + fread($fp, 2); //读取最后的结束符 \r\n + $switch = true; + } + } + else + { + if ($content_length>0) + { + $rs['body'] = fread($fp, $content_length); + } + $switch = true; + } + } + else + { + $tmp = fgets($fp); + if ($tmp=="\r\n") + { + $read_body = true; + } + else + { + list($k, $v) = explode(':', $tmp, 2); + $k = trim($k); + $v = trim($v); + $rs['header'][$k] = $v; + + if ($k=='Content-Length') + { + $content_length = $v; + } + else if ($k=='Transfer-Encoding') + { + # 分片获取 + $transfer_encoding = true; + } + } + } + } + + return $rs; + } + + /** + * 返回连接对象 + * + * @return resource + */ + protected function connection($re_connect=false) + { + # 更新token + $this->get_token(); + + if (!$re_connect && !isset(Storage_Driver_Swift::$connections[$this->storage_host])) + { + Storage_Driver_Swift::$connections[$this->storage_host] = $this->fp($this->storage_protocol, $this->storage_host, $this->storage_port, $this->timeout); + Storage_Driver_Swift::$requests_num[$this->storage_host] = 0; + } + else if ($re_connect || (time()-Storage_Driver_Swift::$last_used[$this->storage_host] >= $this->timeout)) + { + # 超时的连接,销毁后重新连接 + @fclose(Storage_Driver_Swift::$connections[$this->storage_host]); + unset(Storage_Driver_Swift::$connections[$this->storage_host]); + + Storage_Driver_Swift::$connections[$this->storage_host] = $this->fp($this->storage_protocol, $this->storage_host, $this->storage_port, $this->timeout); + Storage_Driver_Swift::$requests_num[$this->storage_host] = 0; + } + + Storage_Driver_Swift::$last_used[$this->storage_host] = time(); + + return Storage_Driver_Swift::$connections[$this->storage_host]; + } + + /** + * 连接服务器 + * + * @param string $host + * @param int $port + * @param int $timeout + * @throws Exception + * @return boolean + */ + protected function fp($protocol, $host, $port , $timeout) + { + try + { + if ($protocol == 'https') + { + $fp = fsockopen('tls://' . $host, $port, $errno, $errstr, $timeout); + } + else + { + $fp = fsockopen($host, $port, $errno, $errstr, $timeout); + } + } + catch (Exception $e) + { + $errstr = $e->getMessage(); + $errno = $e->getCode(); + $fp = false; + } + + if (!$fp) + { + throw new Exception('Unable to connect to: ' . $host . ':' . $port ."\nError: " . $errstr . ' : ' .$errno); + } + + return $fp; + } + + /** + * 获取token + * + * @return string + */ + protected function get_token() + { + # 缓存key + $key = 'swift_storage_auth_' . $this->host . '_' . $this->account; + + # 获取缓存token + $token = Cache::instance()->get($key); + + if ($token) + { + $this->token = $token[0]; + $this->storage_url = $token[1]; + + $h = parse_url($this->storage_url); + + $this->storage_host = $h['host']; + $this->storage_path = $h['path']; + $this->storage_protocol = $h['scheme']; + $this->storage_port = $h['port']?$h['port']:$h['scheme']=='https'?443:80; + + if (IS_DEBUG)Core::debug()->info($this->token, 'Swift Token From Cache'); + } + else + { + # 获取token + $this->get_real_token(); + + # 设置缓存 + Cache::instance()->set($key, array($this->token, $this->storage_url), $this->token_timeout); + } + } + + /** + * 获取token + */ + protected function get_real_token() + { + $headers = array + ( + 'Host' => $this->host, + 'X-Auth-User' => $this->account, + 'X-Auth-Key' => $this->key, + 'Content-Length' => 0, + ); + + if (IS_DEBUG)Core::debug()->info($this->protocol .'://'. $this->account . '@' . $this->host . ($this->port!=80&&$this->port!=443?':'.$this->port:'') . '/' . $this->swift_version, 'Swift get token url'); + + $fp = $this->fp($this->protocol, $this->host, $this->port, $this->timeout); + + $message = $this->build_request_line('GET', '/'.$this->swift_version) . $this->build_headers($headers); + + fwrite($fp, $message); + + $rs = $this->read($fp); + + if ($rs['code']<200 || $rs['code']>=300) + { + throw new Exception(__('Swift get token error. Code: :code.', array(':code'=>$rs['code']))); + } + + # 获取token + if (isset($rs['header']['X-Auth-Token'])) + { + $this->token = $rs['header']['X-Auth-Token']; + } + else + { + throw new Exception(__('Swift get token error.not found X-Auth-Token')); + } + + # 获取Storage URL + if (isset($rs['header']['X-Storage-Url'])) + { + $this->storage_url = $rs['header']['X-Storage-Url']; + $h = parse_url($this->storage_url); + + $this->storage_host = $h['host']; + $this->storage_path = $h['path']; + $this->storage_protocol = $h['scheme']; + $this->storage_port = $h['port']?$h['port']:$h['scheme']=='https'?443:80; + } + else + { + throw new Exception(__('Swift get token error.not found X-Storage-Url')); + } + if (IS_DEBUG)Core::debug()->info($this->token, 'Swift Token'); + + if ($this->storage_url) + { + if ($this->storage_host==$this->host) + { + # 将连接加到$connections里复用 + Storage_Driver_Swift::$connections[$this->host] = $fp; + Storage_Driver_Swift::$last_used[$this->host] = time(); + Storage_Driver_Swift::$requests_num[$this->host] = 0; + } + else + { + # 域名不一致,可以关闭token服务器 + fclose($fp); + } + } + } + + protected function build_request_line($method, $uri, $query = null) + { + if ($uri!='/') + { + $url_array = array(); + foreach (explode('/', $uri) as $i) + { + $url_array[] = rawurlencode($i); + } + $uri = implode('/', $url_array); + } + + if ($query) + { + if (is_array($query)) + { + foreach ($query as $key => $value) + { + $query_str .= '&' . rawurlencode($key) . '=' . rawurlencode($value) ; + } + } + else + { + $query_str = $query; + } + + $uri .= '?' . trim($query_str, '&'); + } + + $request_line = $method . ' ' . $uri . ' ' . $this->protocol_version . "\r\n"; + + return $request_line; + } + + /** + * 将数组头信息转成字符 + * + * @param array $headers + * @return string + */ + protected function build_headers(array $headers) + { + $headers['Connection'] = 'keep-alive'; + + if (!isset($headers['X-Auth-Key']) && !isset($headers['X-Auth-User'])) + { + # 加token + $headers['X-Auth-Token'] = $this->token; + } + + if (!isset($headers['Date'])) + { + $headers['Date'] = gmdate('D, d M Y H:i:s \G\M\T'); + } + + $header_str = ''; + foreach ($headers as $key => $value) + { + $header_str .= trim($key) . ": " . trim($value) . "\r\n"; + } + + $header_str .= "\r\n"; + + return $header_str; + } + + /** + * 关闭所有链接 + */ + public static function close_all_connect() + { + foreach (Storage_Driver_Swift::$connections as $host=>$obj) + { + try + { + fclose($obj); + } + catch (Exception $e) + { + Core::debug()->error('close swift storage connect error:' . $e->getMessage()); + } + + Storage_Driver_Swift::$connections[$host] = null; + } + + # 重置全部数据 + Storage_Driver_Swift::$connections = array(); + Storage_Driver_Swift::$last_used = array(); + Storage_Driver_Swift::$requests_num = array(); + + if (IS_DEBUG)Core::debug()->info('close all swift storage server.'); + } +} \ No newline at end of file