添加后台代理代码
This commit is contained in:
101
codes/agent/game/api/lib/phprs/util/AnnotationCleaner.php
Normal file
101
codes/agent/game/api/lib/phprs/util/AnnotationCleaner.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
namespace phprs\util;
|
||||
|
||||
/**
|
||||
* 去除//注释的内容,但会跳过引号内的//
|
||||
* @author caoym
|
||||
*
|
||||
*/
|
||||
class AnnotationCleaner
|
||||
{
|
||||
|
||||
static public function clean($text) {
|
||||
$o = new AnnotationCleaner();
|
||||
return $o->clean_($text);
|
||||
}
|
||||
//去掉注释
|
||||
private function clean_($text) {
|
||||
|
||||
$this->dest = '';
|
||||
$this->tmp = $text;
|
||||
$state = 'stateNormal';
|
||||
while ($state){
|
||||
$state = $this->$state();
|
||||
}
|
||||
return $this->dest;
|
||||
}
|
||||
|
||||
private function stateNormal(){
|
||||
$stateBegin = [
|
||||
'//'=>'stateAntSL', //单行注释
|
||||
'/*'=>'stateAntML',//多行注释
|
||||
'\''=>'stateStrSQ', //单引号
|
||||
'"'=>'stateStrDQ',//双引号
|
||||
];
|
||||
|
||||
$count = strlen($this->tmp);
|
||||
for($i=0; $i<$count; $i++){
|
||||
foreach ($stateBegin as $k=>$v){
|
||||
if(substr($this->tmp, $i, strlen($k)) == $k){
|
||||
$this->dest .= substr($this->tmp, 0, $i);
|
||||
$this->tmp = substr($this->tmp, $i);
|
||||
return $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->dest .= $this->tmp;
|
||||
$this->tmp = '';
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* 单行注释
|
||||
*/
|
||||
private function stateAntSL(){
|
||||
$pos = strpos($this->tmp, "\n");
|
||||
if($pos){
|
||||
$this->tmp = substr($this->tmp, $pos);
|
||||
}else{
|
||||
$this->tmp = '';
|
||||
}
|
||||
return 'stateNormal';
|
||||
}
|
||||
/**
|
||||
* 双行注释
|
||||
*/
|
||||
private function stateAntML(){
|
||||
$pos = strpos($this->tmp, "*/");
|
||||
if($pos){
|
||||
$this->tmp = substr($this->tmp, $pos+2);
|
||||
}else{
|
||||
$this->tmp = '';
|
||||
}
|
||||
return 'stateNormal';
|
||||
}
|
||||
// 单引号
|
||||
private function stateStrSQ(){
|
||||
return $this->stateStr('\'');
|
||||
}
|
||||
// 双引号
|
||||
private function stateStrDQ(){
|
||||
return $this->stateStr('"');
|
||||
}
|
||||
|
||||
private function stateStr($q){
|
||||
$count = strlen($this->tmp);
|
||||
for($i=1; $i<$count; $i++){
|
||||
if(substr($this->tmp, $i, 1) == $q){
|
||||
$this->dest .= substr($this->tmp, 0, $i+1);
|
||||
$this->tmp = substr($this->tmp, $i+1);
|
||||
return 'stateNormal';
|
||||
}else if(substr($this->tmp, $i, 1) == '\\'){//文本内转义
|
||||
$i++;
|
||||
continue;//跳过一个
|
||||
}
|
||||
}
|
||||
$this->dest .= $this->tmp;
|
||||
$this->tmp = '';
|
||||
return 'stateNormal';
|
||||
}
|
||||
private $tmp;
|
||||
private $dest;
|
||||
}
|
||||
77
codes/agent/game/api/lib/phprs/util/AnnotationReader.php
Normal file
77
codes/agent/game/api/lib/phprs/util/AnnotationReader.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: AnnotationReader.php 56458 2014-11-29 15:06:20Z caoyangmin $
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief
|
||||
**/
|
||||
namespace phprs\util;
|
||||
|
||||
//初始化全局的AnnotationReader,并增加对自定义Annotation的支持
|
||||
class AnnotationReader{
|
||||
public function __construct(){
|
||||
$this->parser= new DocParser();
|
||||
}
|
||||
public function getClassAnnotations(\ReflectionClass $class, $record_doc=false)
|
||||
{
|
||||
$cn = $class->getName();
|
||||
if(isset($this->cache[$cn]['class'])){
|
||||
return $this->cache[$cn]['class'];
|
||||
}
|
||||
$this->cache[$cn]['class'] = array();
|
||||
$annots = $this->parser->parse($class->getDocComment(), 'class '.$cn, $record_doc);
|
||||
foreach ($annots as $annot){
|
||||
$key = $annot[0];
|
||||
$annot = $annot[1];
|
||||
$this->cache[$cn]['class'][$key][]=$annot;
|
||||
}
|
||||
return $this->cache[$cn]['class'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getMethodAnnotations(\ReflectionMethod $method, $record_doc=false)
|
||||
{
|
||||
$cn = $method->getDeclaringClass()->getName();
|
||||
|
||||
$id = $method->getName();
|
||||
if(isset($this->cache[$cn]['method'][$id])){
|
||||
return $this->cache[$cn]['method'][$id];
|
||||
}
|
||||
$this->cache[$cn]['method'][$id] = array();
|
||||
$annots = $this->parser->parse($method->getDocComment(), 'method '.$cn.'::'.$id.'()', $record_doc);
|
||||
foreach ($annots as $annot){
|
||||
$key = $annot[0];
|
||||
$annot = $annot[1];
|
||||
|
||||
$this->cache[$cn]['method'][$id][$key][]=$annot;
|
||||
}
|
||||
return $this->cache[$cn]['method'][$id];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPropertyAnnotations(\ReflectionProperty $property, $record_doc=false)
|
||||
{
|
||||
$cn = $property->getDeclaringClass()->getName();
|
||||
|
||||
$id = $property->getName();
|
||||
if(isset($this->cache[$cn]['property'][$id])){
|
||||
return $this->cache[$cn]['property'][$id];
|
||||
}
|
||||
$this->cache[$cn]['property'][$id] = array();
|
||||
$annots = $this->parser->parse($property->getDocComment(), 'property '.$cn.'::$'.$id, $record_doc);
|
||||
foreach ($annots as $annot){
|
||||
$key= $annot[0];
|
||||
$annot= $annot[1];
|
||||
|
||||
$this->cache[$cn]['property'][$id][$key][]=$annot;
|
||||
}
|
||||
return $this->cache[$cn]['property'][$id];
|
||||
}
|
||||
private $cache=array() ;
|
||||
private $parser ;
|
||||
}
|
||||
|
||||
45
codes/agent/game/api/lib/phprs/util/ApcCache.php
Normal file
45
codes/agent/game/api/lib/phprs/util/ApcCache.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: ApcCache.php 57516 2014-12-23 05:44:20Z caoyangmin $
|
||||
*
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
*/
|
||||
namespace phprs\util;
|
||||
|
||||
/**
|
||||
* apc 缓存
|
||||
* @author caoym
|
||||
*
|
||||
*/
|
||||
class ApcCache implements KVCatchInterface
|
||||
{
|
||||
/**
|
||||
* get
|
||||
* @param string $key
|
||||
* @param boolean $succeeded
|
||||
* @return mixed The stored variable or array of variables on success; false on failure
|
||||
*/
|
||||
public function get($key, &$succeeded)
|
||||
{
|
||||
return apc_fetch($key, $succeeded);
|
||||
}
|
||||
/**
|
||||
* @param string $key
|
||||
* @param string $var
|
||||
* @param int $ttl
|
||||
* @return boolean bool Returns true on success or false on failure. Second syntax returns array with error keys.
|
||||
*/
|
||||
public function set($key, $var, $ttl)
|
||||
{
|
||||
return apc_store($key, $var, $ttl);
|
||||
}
|
||||
/**
|
||||
* @param string $key
|
||||
* @return boolean mixed Returns true on success or false on failure.
|
||||
*/
|
||||
public function del($key)
|
||||
{
|
||||
return apc_delete($key);
|
||||
}
|
||||
}
|
||||
39
codes/agent/game/api/lib/phprs/util/AutoClassLoader.php
Normal file
39
codes/agent/game/api/lib/phprs/util/AutoClassLoader.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: AutoClassLoader.php 58820 2015-01-16 16:29:33Z caoyangmin $
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief
|
||||
*/
|
||||
namespace phprs\util;
|
||||
|
||||
/**
|
||||
* 反序列化时自动加载类
|
||||
* @author caoym
|
||||
*
|
||||
*/
|
||||
class AutoClassLoader{
|
||||
/**
|
||||
* 反序列化时被调用
|
||||
*/
|
||||
function __wakeup(){
|
||||
ClassLoader::addClassMap($this->map, false);
|
||||
}
|
||||
/**
|
||||
* 添加类文件映射
|
||||
* @param unknown $name
|
||||
* @param unknown $file
|
||||
*/
|
||||
public function addClass($name, $file){
|
||||
$this->map[$name] = $file;
|
||||
ClassLoader::addClassMap($this->map, false);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return array:
|
||||
*/
|
||||
public function getClasses(){
|
||||
return $this->map;
|
||||
}
|
||||
private $map=array();
|
||||
}
|
||||
27
codes/agent/game/api/lib/phprs/util/Cache.php
Normal file
27
codes/agent/game/api/lib/phprs/util/Cache.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: Cache.php 57516 2014-12-23 05:44:20Z caoyangmin $
|
||||
*
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief Cache
|
||||
*/
|
||||
namespace phprs\util;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author caoym
|
||||
*/
|
||||
class Cache extends CheckableCache
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
if (!function_exists('apc_fetch') || !function_exists('apc_store') || !function_exists('apc_delete')) {
|
||||
parent::__construct(new FileCache(), $this->tag);
|
||||
}else{
|
||||
parent::__construct(new ApcCache(), $this->tag);
|
||||
}
|
||||
}
|
||||
/** @property */
|
||||
private $tag = "";
|
||||
}
|
||||
102
codes/agent/game/api/lib/phprs/util/CheckableCache.php
Normal file
102
codes/agent/game/api/lib/phprs/util/CheckableCache.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
namespace phprs\util;
|
||||
/**
|
||||
* 可检查缓存是否失效的缓存
|
||||
* @author caoym
|
||||
*
|
||||
*/
|
||||
class CheckableCache
|
||||
{
|
||||
/**
|
||||
* @param object $impl instanceof of KVCatchInterface
|
||||
*/
|
||||
function __construct($impl, $tag = ''){
|
||||
$this->impl = $impl;
|
||||
$this->tag = $tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置cache
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $var
|
||||
* @param int
|
||||
* @param SerializableFunc $expire_check
|
||||
* @return boolean
|
||||
* 缓存过期检查方法, 缓存过期(超过ttl)后, get时调用, 返回true表示缓存继续可用.
|
||||
* 如checker($got_var, $time)
|
||||
*
|
||||
*/
|
||||
public function set($name, $var, $ttl = 0, $expire_check = null)
|
||||
{
|
||||
$name = $this->tag.$name;
|
||||
$res = $this->impl->set($name, array(
|
||||
$var,
|
||||
$ttl,
|
||||
$expire_check,
|
||||
time(),
|
||||
), is_null($expire_check) ? $ttl : 0);
|
||||
if (!$res){
|
||||
Logger::warning("set cache $name failed");
|
||||
}else{
|
||||
Logger::debug("set cache $name ok, ttl=$ttl, check=".($expire_check===null?'null':get_class($expire_check)));
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取cache
|
||||
* @param string $name
|
||||
* @param boolean $succeeded
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, &$succeeded=null)
|
||||
{
|
||||
$name = $this->tag.$name;
|
||||
$res = $this->impl->get($name, $succeeded);
|
||||
if ($succeeded) {
|
||||
$succeeded = false;
|
||||
list ($data, $ttl, $checker, $create_time) = $res;
|
||||
// 如果指定了checker, ttl代表每次检查的间隔时间, 0表示每次get都需要经过checker检查
|
||||
// 如果没有指定checker, ttl表示缓存过期时间, 为0表示永不过期
|
||||
if ($checker !== null) {
|
||||
if ($ttl == 0 || ($create_time + $ttl < time())) {
|
||||
$valid = false;
|
||||
try{
|
||||
if(is_callable($checker)){
|
||||
$valid = $checker($data, $create_time);
|
||||
}
|
||||
}
|
||||
catch (\Exception $e){
|
||||
Logger::warning('call checker failed with '.$e->getTraceAsString());
|
||||
$valid = false;
|
||||
}
|
||||
if(!$valid){
|
||||
Logger::debug("cache $name expired by checker");
|
||||
$this->impl->del($name);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}else if ($ttl != 0 && ($create_time + $ttl < time())) {
|
||||
Logger::debug("cache $name expired by ttl");
|
||||
$this->impl->del($name);
|
||||
return null;
|
||||
}
|
||||
Logger::debug("get $name from cache, ttl=$ttl, create_time=$create_time, check=".($checker===null?'null':get_class($checker)));
|
||||
$succeeded = true;
|
||||
return $data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* 删除
|
||||
* @param string $name
|
||||
*/
|
||||
public function del($name){
|
||||
$name = $this->tag.$name;
|
||||
return $this->impl->del($name);
|
||||
}
|
||||
private $tag;
|
||||
private $impl;
|
||||
}
|
||||
63
codes/agent/game/api/lib/phprs/util/ClassLoader.php
Normal file
63
codes/agent/game/api/lib/phprs/util/ClassLoader.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: ClassLoader.php 58839 2015-01-17 16:18:55Z caoyangmin $
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief
|
||||
*/
|
||||
namespace phprs\util;
|
||||
|
||||
/**
|
||||
* class loader
|
||||
* @author caoym
|
||||
*
|
||||
*/
|
||||
class ClassLoader{
|
||||
|
||||
/**
|
||||
* @param unknown $path
|
||||
*/
|
||||
static public function addInclude($path){
|
||||
|
||||
if (is_array($path)){
|
||||
self::$includes = array_unique(array_merge(self::$includes, $path));
|
||||
}else{
|
||||
self::$includes[] = $path;
|
||||
self::$includes = array_unique(self::$includes);
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param array $map
|
||||
* @param boolean $replace
|
||||
*/
|
||||
static public function addClassMap($map, $replace=false){
|
||||
if($replace){
|
||||
self::$class_map = array_merge(self::$class_map, $map);
|
||||
}else{
|
||||
self::$class_map = array_merge($map, self::$class_map);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* autoLoad
|
||||
* @param unknown $classname
|
||||
* @return void
|
||||
*/
|
||||
static public function autoLoad($classname){
|
||||
if(array_key_exists($classname, self::$class_map)){
|
||||
$path = self::$class_map[$classname];
|
||||
require_once $path;
|
||||
}
|
||||
foreach(self::$includes as $path) {
|
||||
$path = $path . '/' . str_replace('\\', '/', $classname) . '.php';
|
||||
if (file_exists($path)) {
|
||||
self::$class_map[$classname] = $path;
|
||||
require_once $path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public $class_map=array();
|
||||
static public $includes=array();
|
||||
}
|
||||
141
codes/agent/game/api/lib/phprs/util/Curl.php
Normal file
141
codes/agent/game/api/lib/phprs/util/Curl.php
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
namespace phprs\util;
|
||||
class CurlResponse{
|
||||
|
||||
public $headers=array();
|
||||
public $content;
|
||||
public $http_code;
|
||||
public $errno;
|
||||
public $errstr;
|
||||
public $status;
|
||||
public $content_type;
|
||||
|
||||
public function isOk(){
|
||||
return $this->errno==0 && intval($this->http_code)>=200 && intval($this->http_code)<300;
|
||||
}
|
||||
public function message(){
|
||||
return "errno:{$this->errno}, errstr:{$this->errstr}, http_code:{$this->http_code}, content:".print_r($this->content,true);
|
||||
}
|
||||
public function handleHeader($ch, $header_line){
|
||||
if(count($this->headers) ==0){
|
||||
$this->status = trim(explode(' ', $header_line,2)[1]);
|
||||
}
|
||||
list($n,$v) = explode(':', $header_line)+array(null,null);
|
||||
if(strcasecmp(trim($n),'Content-Type')===0){
|
||||
$this->content_type = trim($v);
|
||||
}
|
||||
$this->headers[]=$header_line;
|
||||
return strlen($header_line);
|
||||
}
|
||||
/**
|
||||
* 从http返回的数据中分离header和body
|
||||
* @param string $data
|
||||
*/
|
||||
public function parseReturnData($data){
|
||||
while(true){
|
||||
$lines = explode("\n", $data, 2);
|
||||
if(!$lines || count($lines) == 0){
|
||||
break;
|
||||
}
|
||||
if(trim($lines[0]) == ''){//空行,header 结束
|
||||
if (stristr($this->content_type, 'application/Json') !== false) {
|
||||
$this->content = json_decode($lines[1], true);
|
||||
} else {
|
||||
$this->content = $lines[1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
$this->handleHeader(null, $lines[0]);
|
||||
if(count($lines) !=2){
|
||||
break;
|
||||
}
|
||||
$data = $lines[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
class Curl
|
||||
{
|
||||
public function __construct(){
|
||||
$this->ch = curl_init();
|
||||
if(!file_exists('/dev/null')){
|
||||
curl_setopt($this->ch, CURLOPT_COOKIEJAR, 'NUL');
|
||||
}else{
|
||||
curl_setopt($this->ch, CURLOPT_COOKIEJAR, '/dev/null');
|
||||
}
|
||||
|
||||
}
|
||||
public function reset(){
|
||||
curl_close($this->ch);
|
||||
$this->ch = curl_init();
|
||||
if(!file_exists('/dev/null')){
|
||||
curl_setopt($this->ch, CURLOPT_COOKIEJAR, 'NUL');
|
||||
}else{
|
||||
curl_setopt($this->ch, CURLOPT_COOKIEJAR, '/dev/null');
|
||||
}
|
||||
}
|
||||
public function __destruct(){
|
||||
curl_close($this->ch);
|
||||
}
|
||||
public function GET($url, $headers=null,$followLoc=true){
|
||||
return $this->execCurl($url, __FUNCTION__, null, $headers,$followLoc);
|
||||
}
|
||||
public function POST($url, $content, $headers=null,$followLoc=true){
|
||||
return $this->execCurl($url, __FUNCTION__, $content, $headers,$followLoc);
|
||||
}
|
||||
public function PUT($url, $content, $headers=null,$followLoc=true){
|
||||
return $this->execCurl($url, __FUNCTION__, $content, $headers,$followLoc);
|
||||
}
|
||||
private function execCurl($url, $method='GET', $content=null, $headers=null,$followLoc=true){
|
||||
$res = new CurlResponse();
|
||||
|
||||
if(isset($method)){
|
||||
curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
}
|
||||
curl_setopt($this->ch, CURLOPT_URL,$url);
|
||||
curl_setopt($this->ch, CURLOPT_TIMEOUT, 60);
|
||||
if(!empty($headers)){
|
||||
curl_setopt($this->ch, CURLOPT_HTTPHEADER, $headers);
|
||||
}
|
||||
|
||||
curl_setopt($this->ch, CURLOPT_RETURNTRANSFER,1);
|
||||
curl_setopt($this->ch,CURLOPT_FOLLOWLOCATION, $followLoc?1:0);//支持跳转
|
||||
|
||||
//curl_setopt($this->ch, CURLOPT_HEADERFUNCTION, array($res,'handleHeader')); // handle received headers
|
||||
curl_setopt($this->ch, CURLOPT_HEADER, true);
|
||||
if(!empty($content)){
|
||||
$content_type = '';
|
||||
// 取content-type
|
||||
foreach ($headers as $h){
|
||||
list($n,$v) = explode(':',$h)+array(null,null);
|
||||
if(strcasecmp(trim($n),'Content-Type')===0){
|
||||
$content_type = trim($v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(is_array($content) && $content_type == 'application/json'){
|
||||
curl_setopt($this->ch, CURLOPT_POSTFIELDS, json_encode($content));
|
||||
}else if($content_type == 'multipart/form-data'){
|
||||
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $content);
|
||||
}else{
|
||||
if(is_array($content) ){
|
||||
curl_setopt($this->ch, CURLOPT_POSTFIELDS, http_build_query($content));
|
||||
}else{
|
||||
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $content);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
$response = curl_exec($this->ch);
|
||||
|
||||
$res->http_code = curl_getinfo($this->ch,CURLINFO_HTTP_CODE);
|
||||
$res->errno = curl_errno($this->ch);
|
||||
$res->errstr = curl_error($this->ch);
|
||||
$res->parseReturnData($response);
|
||||
|
||||
return $res;
|
||||
}
|
||||
/** curl handle */
|
||||
private $ch;
|
||||
|
||||
}
|
||||
605
codes/agent/game/api/lib/phprs/util/DocParser.php
Normal file
605
codes/agent/game/api/lib/phprs/util/DocParser.php
Normal file
@@ -0,0 +1,605 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace phprs\util;
|
||||
|
||||
use ReflectionClass;
|
||||
use Doctrine\Common\Annotations\DocLexer;
|
||||
use Doctrine\Common\Annotations\AnnotationException;
|
||||
/**
|
||||
* A parser for docblock annotations.
|
||||
*
|
||||
* It is strongly discouraged to change the default annotation parsing process.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*/
|
||||
final class DocParser
|
||||
{
|
||||
/**
|
||||
* An array of all valid tokens for a class name.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $classIdentifiers = array(
|
||||
DocLexer::T_IDENTIFIER,
|
||||
DocLexer::T_TRUE,
|
||||
DocLexer::T_FALSE,
|
||||
DocLexer::T_NULL
|
||||
);
|
||||
|
||||
/**
|
||||
* The lexer.
|
||||
*
|
||||
* @var \Doctrine\Common\Annotations\DocLexer
|
||||
*/
|
||||
private $lexer;
|
||||
|
||||
/**
|
||||
* A list with annotations that are not causing exceptions when not resolved to an annotation class.
|
||||
*
|
||||
* The names must be the raw names as used in the class, not the fully qualified
|
||||
* class names.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $ignoredAnnotationNames = array();
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $context = '';
|
||||
|
||||
/**
|
||||
* Hash-map for handle types declaration.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $typeMap = array(
|
||||
'float' => 'double',
|
||||
'bool' => 'boolean',
|
||||
// allow uppercase Boolean in honor of George Boole
|
||||
'Boolean' => 'boolean',
|
||||
'int' => 'integer',
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructs a new DocParser.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->lexer = new DocLexer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the annotation names that are ignored during the parsing process.
|
||||
*
|
||||
* The names are supposed to be the raw names as used in the class, not the
|
||||
* fully qualified class names.
|
||||
*
|
||||
* @param array $names
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setIgnoredAnnotationNames(array $names)
|
||||
{
|
||||
$this->ignoredAnnotationNames = $names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given docblock string for annotations.
|
||||
*
|
||||
* @param string $input The docblock string to parse.
|
||||
* @param string $context The parsing context.
|
||||
*
|
||||
* @return array Array of annotations. If no annotations are found, an empty array is returned.
|
||||
*/
|
||||
public function parse($input, $context = '', $record_doc)
|
||||
{
|
||||
$pos = $this->findInitialTokenPosition($input);
|
||||
if ($pos === null) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$this->context = $context;
|
||||
|
||||
$this->lexer->setInput(trim(substr($input, $pos), '* /'));
|
||||
$this->lexer->moveNext();
|
||||
|
||||
return $this->Annotations($record_doc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first valid annotation
|
||||
*
|
||||
* @param string $input The docblock string to parse
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
private function findInitialTokenPosition($input)
|
||||
{
|
||||
$pos = 0;
|
||||
|
||||
// search for first valid annotation
|
||||
while (($pos = strpos($input, '@', $pos)) !== false) {
|
||||
// if the @ is preceded by a space or * it is valid
|
||||
if ($pos === 0 || $input[$pos - 1] === ' ' || $input[$pos - 1] === '*') {
|
||||
return $pos;
|
||||
}
|
||||
|
||||
$pos++;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to match the given token with the current lookahead token.
|
||||
* If they match, updates the lookahead token; otherwise raises a syntax error.
|
||||
*
|
||||
* @param integer $token Type of token.
|
||||
*
|
||||
* @return boolean True if tokens match; false otherwise.
|
||||
*/
|
||||
private function match($token)
|
||||
{
|
||||
if ( ! $this->lexer->isNextToken($token) ) {
|
||||
$this->syntaxError($this->lexer->getLiteral($token));
|
||||
}
|
||||
|
||||
return $this->lexer->moveNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to match the current lookahead token with any of the given tokens.
|
||||
*
|
||||
* If any of them matches, this method updates the lookahead token; otherwise
|
||||
* a syntax error is raised.
|
||||
*
|
||||
* @param array $tokens
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private function matchAny(array $tokens)
|
||||
{
|
||||
if ( ! $this->lexer->isNextTokenAny($tokens)) {
|
||||
$this->syntaxError(implode(' or ', array_map(array($this->lexer, 'getLiteral'), $tokens)));
|
||||
}
|
||||
|
||||
return $this->lexer->moveNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new syntax error.
|
||||
*
|
||||
* @param string $expected Expected string.
|
||||
* @param array|null $token Optional token.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
private function syntaxError($expected, $token = null)
|
||||
{
|
||||
if ($token === null) {
|
||||
$token = $this->lexer->lookahead;
|
||||
}
|
||||
|
||||
$message = sprintf('Expected %s, got ', $expected);
|
||||
$message .= ($this->lexer->lookahead === null)
|
||||
? 'end of string'
|
||||
: sprintf("'%s' at position %s", $token['value'], $token['position']);
|
||||
|
||||
if (strlen($this->context)) {
|
||||
$message .= ' in ' . $this->context;
|
||||
}
|
||||
|
||||
$message .= '.';
|
||||
|
||||
throw AnnotationException::syntaxError($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Annotations ::= Annotation {[ "*" ]* [Annotation]}*
|
||||
* @param $record_doc 记录文档
|
||||
* @return array
|
||||
*/
|
||||
private function Annotations($record_doc = false)
|
||||
{
|
||||
$annotations = array();
|
||||
$doc_begin = 0;
|
||||
while (null !== $this->lexer->lookahead) {
|
||||
if (DocLexer::T_AT !== $this->lexer->lookahead['type']) {
|
||||
$this->lexer->moveNext();
|
||||
continue;
|
||||
}
|
||||
|
||||
// make sure the @ is preceded by non-catchable pattern
|
||||
if (null !== $this->lexer->token && $this->lexer->lookahead['position'] === $this->lexer->token['position'] + strlen($this->lexer->token['value'])) {
|
||||
$this->lexer->moveNext();
|
||||
continue;
|
||||
}
|
||||
|
||||
// make sure the @ is followed by either a namespace separator, or
|
||||
// an identifier token
|
||||
if ((null === $peek = $this->lexer->glimpse())
|
||||
|| (DocLexer::T_NAMESPACE_SEPARATOR !== $peek['type'] && !in_array($peek['type'], self::$classIdentifiers, true))
|
||||
|| $peek['position'] !== $this->lexer->lookahead['position'] + 1) {
|
||||
$this->lexer->moveNext();
|
||||
continue;
|
||||
}
|
||||
|
||||
$doc_end = $this->lexer->lookahead['position'];
|
||||
$this->match(DocLexer::T_AT);
|
||||
$name = $this->Identifier();
|
||||
$value = $this->MethodCall();
|
||||
|
||||
if($record_doc){
|
||||
if(($count= count($annotations)) !==0){
|
||||
$doc= $this->lexer->getInputUntilPosition($doc_end);
|
||||
$text = substr($doc, $doc_begin);
|
||||
$annotations[$count-1][1]['doc'] = $text;
|
||||
$text = strstr($text, ')');
|
||||
$annotations[$count-1][1]['desc'] = substr($text, 1);
|
||||
}
|
||||
$doc_begin = $doc_end;
|
||||
}
|
||||
$annotations[] = array(
|
||||
$name,
|
||||
$value
|
||||
);
|
||||
}
|
||||
if ($record_doc) {
|
||||
if (($count = count($annotations)) !== 0) {
|
||||
$doc = $this->lexer->getInputUntilPosition(0xFFFF);
|
||||
$text = substr($doc, $doc_begin);
|
||||
$annotations[$count-1][1]['doc'] = $text;
|
||||
$text = strstr($text, ')');
|
||||
$annotations[$count-1][1]['desc'] = substr($text, 1);
|
||||
}
|
||||
}
|
||||
return $annotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* MethodCall ::= ["(" [Values] ")"]
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function MethodCall()
|
||||
{
|
||||
$values = array();
|
||||
|
||||
if ( ! $this->lexer->isNextToken(DocLexer::T_OPEN_PARENTHESIS)) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
$this->match(DocLexer::T_OPEN_PARENTHESIS);
|
||||
|
||||
if ( ! $this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) {
|
||||
$values = $this->Values();
|
||||
}
|
||||
|
||||
$this->match(DocLexer::T_CLOSE_PARENTHESIS);
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Values ::= Array | Value {"," Value}* [","]
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function Values()
|
||||
{
|
||||
$values = array($this->Value());
|
||||
|
||||
while ($this->lexer->isNextToken(DocLexer::T_COMMA)) {
|
||||
$this->match(DocLexer::T_COMMA);
|
||||
|
||||
if ($this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$token = $this->lexer->lookahead;
|
||||
$value = $this->Value();
|
||||
|
||||
if ( ! is_object($value) && ! is_array($value)) {
|
||||
$this->syntaxError('Value', $token);
|
||||
}
|
||||
|
||||
$values[] = $value;
|
||||
}
|
||||
|
||||
foreach ($values as $k => $value) {
|
||||
if (is_object($value) && $value instanceof \stdClass) {
|
||||
$values[$value->name] = $value->value;
|
||||
} else if ( ! isset($values['value'])){
|
||||
$values['value'] = $value;
|
||||
} else {
|
||||
if ( ! is_array($values['value'])) {
|
||||
$values['value'] = array($values['value']);
|
||||
}
|
||||
|
||||
$values['value'][] = $value;
|
||||
}
|
||||
|
||||
unset($values[$k]);
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constant ::= integer | string | float | boolean
|
||||
*
|
||||
* @return mixed
|
||||
*
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
private function Constant()
|
||||
{
|
||||
$identifier = $this->Identifier();
|
||||
|
||||
if ( ! defined($identifier) && false !== strpos($identifier, '::') && '\\' !== $identifier[0]) {
|
||||
list($className, $const) = explode('::', $identifier);
|
||||
|
||||
$alias = (false === $pos = strpos($className, '\\')) ? $className : substr($className, 0, $pos);
|
||||
$found = false;
|
||||
|
||||
switch (true) {
|
||||
case !empty ($this->namespaces):
|
||||
foreach ($this->namespaces as $ns) {
|
||||
if (class_exists($ns.'\\'.$className) || interface_exists($ns.'\\'.$className)) {
|
||||
$className = $ns.'\\'.$className;
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case isset($this->imports[$loweredAlias = strtolower($alias)]):
|
||||
$found = true;
|
||||
$className = (false !== $pos)
|
||||
? $this->imports[$loweredAlias] . substr($className, $pos)
|
||||
: $this->imports[$loweredAlias];
|
||||
break;
|
||||
|
||||
default:
|
||||
if(isset($this->imports['__NAMESPACE__'])) {
|
||||
$ns = $this->imports['__NAMESPACE__'];
|
||||
|
||||
if (class_exists($ns.'\\'.$className) || interface_exists($ns.'\\'.$className)) {
|
||||
$className = $ns.'\\'.$className;
|
||||
$found = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ($found) {
|
||||
$identifier = $className . '::' . $const;
|
||||
}
|
||||
}
|
||||
|
||||
// checks if identifier ends with ::class, \strlen('::class') === 7
|
||||
$classPos = stripos($identifier, '::class');
|
||||
if ($classPos === strlen($identifier) - 7) {
|
||||
return substr($identifier, 0, $classPos);
|
||||
}
|
||||
|
||||
if (!defined($identifier)) {
|
||||
throw AnnotationException::semanticalErrorConstants($identifier, $this->context);
|
||||
}
|
||||
|
||||
return constant($identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifier ::= string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function Identifier()
|
||||
{
|
||||
// check if we have an annotation
|
||||
if ( ! $this->lexer->isNextTokenAny(self::$classIdentifiers)) {
|
||||
$this->syntaxError('namespace separator or identifier');
|
||||
}
|
||||
|
||||
$this->lexer->moveNext();
|
||||
|
||||
$className = $this->lexer->token['value'];
|
||||
|
||||
while ($this->lexer->lookahead['position'] === ($this->lexer->token['position'] + strlen($this->lexer->token['value']))
|
||||
&& $this->lexer->isNextToken(DocLexer::T_NAMESPACE_SEPARATOR)) {
|
||||
|
||||
$this->match(DocLexer::T_NAMESPACE_SEPARATOR);
|
||||
$this->matchAny(self::$classIdentifiers);
|
||||
|
||||
$className .= '\\' . $this->lexer->token['value'];
|
||||
}
|
||||
|
||||
return $className;
|
||||
}
|
||||
|
||||
/**
|
||||
* Value ::= PlainValue | FieldAssignment
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function Value()
|
||||
{
|
||||
$peek = $this->lexer->glimpse();
|
||||
|
||||
if (DocLexer::T_EQUALS === $peek['type']) {
|
||||
return $this->FieldAssignment();
|
||||
}
|
||||
|
||||
return $this->PlainValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* PlainValue ::= integer | string | float | boolean | Array | Annotation
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function PlainValue()
|
||||
{
|
||||
if ($this->lexer->isNextToken(DocLexer::T_OPEN_CURLY_BRACES)) {
|
||||
return $this->Arrayx();
|
||||
}
|
||||
|
||||
if ($this->lexer->isNextToken(DocLexer::T_AT)) {
|
||||
return $this->Annotation();
|
||||
}
|
||||
|
||||
if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) {
|
||||
return $this->Constant();
|
||||
}
|
||||
|
||||
switch ($this->lexer->lookahead['type']) {
|
||||
case DocLexer::T_STRING:
|
||||
$this->match(DocLexer::T_STRING);
|
||||
return $this->lexer->token['value'];
|
||||
|
||||
case DocLexer::T_INTEGER:
|
||||
$this->match(DocLexer::T_INTEGER);
|
||||
return (int)$this->lexer->token['value'];
|
||||
|
||||
case DocLexer::T_FLOAT:
|
||||
$this->match(DocLexer::T_FLOAT);
|
||||
return (float)$this->lexer->token['value'];
|
||||
|
||||
case DocLexer::T_TRUE:
|
||||
$this->match(DocLexer::T_TRUE);
|
||||
return true;
|
||||
|
||||
case DocLexer::T_FALSE:
|
||||
$this->match(DocLexer::T_FALSE);
|
||||
return false;
|
||||
|
||||
case DocLexer::T_NULL:
|
||||
$this->match(DocLexer::T_NULL);
|
||||
return null;
|
||||
|
||||
default:
|
||||
$this->syntaxError('PlainValue');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* FieldAssignment ::= FieldName "=" PlainValue
|
||||
* FieldName ::= identifier
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function FieldAssignment()
|
||||
{
|
||||
$this->match(DocLexer::T_IDENTIFIER);
|
||||
$fieldName = $this->lexer->token['value'];
|
||||
|
||||
$this->match(DocLexer::T_EQUALS);
|
||||
|
||||
$item = new \stdClass();
|
||||
$item->name = $fieldName;
|
||||
$item->value = $this->PlainValue();
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Array ::= "{" ArrayEntry {"," ArrayEntry}* [","] "}"
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function Arrayx()
|
||||
{
|
||||
$array = $values = array();
|
||||
|
||||
$this->match(DocLexer::T_OPEN_CURLY_BRACES);
|
||||
|
||||
// If the array is empty, stop parsing and return.
|
||||
if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) {
|
||||
$this->match(DocLexer::T_CLOSE_CURLY_BRACES);
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
$values[] = $this->ArrayEntry();
|
||||
|
||||
while ($this->lexer->isNextToken(DocLexer::T_COMMA)) {
|
||||
$this->match(DocLexer::T_COMMA);
|
||||
|
||||
// optional trailing comma
|
||||
if ($this->lexer->isNextToken(DocLexer::T_CLOSE_CURLY_BRACES)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$values[] = $this->ArrayEntry();
|
||||
}
|
||||
|
||||
$this->match(DocLexer::T_CLOSE_CURLY_BRACES);
|
||||
|
||||
foreach ($values as $value) {
|
||||
list ($key, $val) = $value;
|
||||
|
||||
if ($key !== null) {
|
||||
$array[$key] = $val;
|
||||
} else {
|
||||
$array[] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* ArrayEntry ::= Value | KeyValuePair
|
||||
* KeyValuePair ::= Key ("=" | ":") PlainValue | Constant
|
||||
* Key ::= string | integer | Constant
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function ArrayEntry()
|
||||
{
|
||||
$peek = $this->lexer->glimpse();
|
||||
|
||||
if (DocLexer::T_EQUALS === $peek['type']
|
||||
|| DocLexer::T_COLON === $peek['type']) {
|
||||
|
||||
if ($this->lexer->isNextToken(DocLexer::T_IDENTIFIER)) {
|
||||
$key = $this->Constant();
|
||||
} else {
|
||||
$this->matchAny(array(DocLexer::T_INTEGER, DocLexer::T_STRING));
|
||||
$key = $this->lexer->token['value'];
|
||||
}
|
||||
|
||||
$this->matchAny(array(DocLexer::T_EQUALS, DocLexer::T_COLON));
|
||||
|
||||
return array($key, $this->PlainValue());
|
||||
}
|
||||
|
||||
return array(null, $this->Value());
|
||||
}
|
||||
}
|
||||
67
codes/agent/game/api/lib/phprs/util/FileCache.php
Normal file
67
codes/agent/game/api/lib/phprs/util/FileCache.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: FileCache.php 57516 2014-12-23 05:44:20Z caoyangmin $
|
||||
*
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
*/
|
||||
namespace phprs\util;
|
||||
/**
|
||||
* 基于文件实现的缓存, 类似APC
|
||||
* @author caoym
|
||||
*/
|
||||
class FileCache implements KVCatchInterface
|
||||
{
|
||||
/**
|
||||
* @param string cache_dir 缓存保存的路径, 为空则使用sys_get_temp_dir取得的路径
|
||||
*/
|
||||
public function __construct($cache_dir=null) {
|
||||
if($cache_dir===null){
|
||||
$this->cache_dir = sys_get_temp_dir().'/caoym_temp';
|
||||
}else{
|
||||
$this->cache_dir = $cache_dir;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 设置key
|
||||
* @param string $key
|
||||
* @param mixed $var
|
||||
* @param int $ttl TODO: 暂不支持
|
||||
* @return void
|
||||
*/
|
||||
public function set($key, $var, $ttl=0){
|
||||
Verify::isTrue(is_dir($this->cache_dir) || @mkdir($this->cache_dir, 0777, true));
|
||||
$path = $this->cache_dir.'/'.sha1($key);
|
||||
return SaftyFileWriter::write($path, serialize($var));
|
||||
}
|
||||
/**
|
||||
* 删除key
|
||||
* @param string $key
|
||||
* @return boolean Returns true on success or false on failure.
|
||||
*/
|
||||
public function del($key){
|
||||
$path = $this->cache_dir.'/'.sha1($key);
|
||||
return @unlink($path);
|
||||
}
|
||||
/**
|
||||
* 模拟apc, 只在没有apc的开发环境使用
|
||||
* @param string $key
|
||||
* @param boolean $succeeded
|
||||
* @return mixed object on success or false on failure
|
||||
*/
|
||||
public function get($key, &$succeeded=null){
|
||||
$succeeded = false;
|
||||
$path = $this->cache_dir.'/'.sha1($key);
|
||||
if(!file_exists($path)){
|
||||
return false;
|
||||
}
|
||||
$res = file_get_contents($path);
|
||||
if($res === false){
|
||||
return false;
|
||||
}
|
||||
$succeeded = true;
|
||||
return unserialize($res);
|
||||
}
|
||||
|
||||
private $cache_dir;
|
||||
}
|
||||
56
codes/agent/game/api/lib/phprs/util/FileExpiredChecker.php
Normal file
56
codes/agent/game/api/lib/phprs/util/FileExpiredChecker.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
namespace phprs\util;
|
||||
|
||||
/**
|
||||
* 检查文件是否过期
|
||||
* @author caoym
|
||||
*/
|
||||
class FileExpiredChecker
|
||||
{
|
||||
/**
|
||||
* @param string|array $file_name 文件的绝对路径
|
||||
*/
|
||||
function __construct($file_name){
|
||||
$file_names = array();
|
||||
if(is_string($file_name)){
|
||||
$file_names[]=$file_name;
|
||||
}else{
|
||||
Verify::isTrue(is_array($file_name));
|
||||
$file_names = $file_name;
|
||||
}
|
||||
foreach ($file_names as $file_name){
|
||||
if(is_file($file_name)){
|
||||
$this->file_name[$file_name] = @filemtime($file_name);
|
||||
}else {
|
||||
$this->file_name[$file_name] = @filemtime($file_name);
|
||||
if(!is_dir($file_name)){
|
||||
continue;
|
||||
}
|
||||
$files = @dir($file_name);
|
||||
Verify::isTrue($files!== null, "open dir $file_name failed");
|
||||
while (!!($file = $files->read())){
|
||||
if($file == '.' || $file == '..') {continue;}
|
||||
$this->file_name[$file_name.'/'.$file] = @filemtime($file_name.'/'.$file);
|
||||
}
|
||||
$files->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 判断是否过期
|
||||
* @param mixed $data 从缓存中得到的数据
|
||||
* @return boolean
|
||||
*/
|
||||
public function __invoke($data, $create_time){
|
||||
$res = false;
|
||||
foreach ($this->file_name as $name => $time){
|
||||
if(@filemtime($name) !== $time){
|
||||
Logger::info("cache expired by file $name modified");
|
||||
return false;
|
||||
}
|
||||
$res = true;
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
private $file_name=array(); //文件全路径
|
||||
}
|
||||
76
codes/agent/game/api/lib/phprs/util/FileOp.php
Normal file
76
codes/agent/game/api/lib/phprs/util/FileOp.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
namespace phprs\util;
|
||||
|
||||
class FileOp
|
||||
{
|
||||
/**
|
||||
* 复制文件或目录
|
||||
* @param string $src
|
||||
* @param string $dst
|
||||
* @param string $except
|
||||
* @return boolean
|
||||
*/
|
||||
static public function copys($src, $dst, $except=null) {
|
||||
if (is_file($src)) {
|
||||
if ($except !== null && (realpath($except) == realpath($src) || realpath($except) == realpath($dst))) {
|
||||
return true;
|
||||
}
|
||||
return @copy($src, $dst);
|
||||
}
|
||||
Verify::isTrue(is_dir($src), "$src not a file nor a dir");
|
||||
|
||||
$dir = dir($src);
|
||||
@mkdir($dst);
|
||||
while(false !== ( $file = $dir->read()) ) {
|
||||
if (( $file != '.' ) && ( $file != '..' )) {
|
||||
$src_path = $src . '/' . $file;
|
||||
$dst_path = $dst . '/' . $file;
|
||||
if(!self::copys($src_path, $dst_path)){
|
||||
$dir->close();
|
||||
return false;
|
||||
}
|
||||
Logger::debug("file copy from $src_path to $dst_path");
|
||||
}
|
||||
}
|
||||
$dir->close();
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* mkdir -p
|
||||
* @param string $path
|
||||
* @param number $mode
|
||||
* @return boolean
|
||||
*/
|
||||
static public function mkdirs($path, $mode = 0777) {
|
||||
$dirs = explode('/', $path);
|
||||
$count = count($dirs);
|
||||
$path = '';
|
||||
for ($i = 0; $i < $count; ++ $i) {
|
||||
if ($i !== 0) {
|
||||
$path .= DIRECTORY_SEPARATOR;
|
||||
}
|
||||
if ($dirs[$i] === '') {
|
||||
continue;
|
||||
}
|
||||
$path .= $dirs[$i];
|
||||
if (! is_dir($path) && ! mkdir($path, $mode)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* 临时目录
|
||||
* @param string $dir
|
||||
* @param string $prefix
|
||||
* @return string
|
||||
*/
|
||||
function tempdir($dir, $prefix) {
|
||||
$tempfile=tempnam($dir, $prefix);
|
||||
if (file_exists($tempfile)) {
|
||||
unlink($tempfile);
|
||||
}
|
||||
mkdir($tempfile);
|
||||
return $tempfile;
|
||||
}
|
||||
}
|
||||
256
codes/agent/game/api/lib/phprs/util/HttpRouterEntries.php
Normal file
256
codes/agent/game/api/lib/phprs/util/HttpRouterEntries.php
Normal file
@@ -0,0 +1,256 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: HttpRouterEntries.php 58820 2015-01-16 16:29:33Z caoyangmin $
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief HttpRouterEntries
|
||||
*/
|
||||
namespace phprs\util;
|
||||
/**
|
||||
* 记录路由信息
|
||||
* @author caoym
|
||||
* 针对 path?arg1=x&argy=y的形式url进行路由
|
||||
* 搜索匹配的规则时,如果有多条规则匹配,则返回路径最深的(或者querystring最多的)规则
|
||||
* 比如规则
|
||||
* "/api" => service1,
|
||||
* "/api?arg1=x" => service2,
|
||||
* 查询"/api?arg1=x&arg2=y" 找到service2
|
||||
*/
|
||||
class HttpRouterEntries{
|
||||
function __construct(){
|
||||
$this->routes = new Tree();
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param string $q
|
||||
* @param string& $matched_path
|
||||
* @return found object
|
||||
*/
|
||||
function find($q,&$matched_path=null){
|
||||
list($path,$param) = explode('?', $q)+array( null,null );
|
||||
$paths = self::stringToPath($path);
|
||||
$params = is_null($param)?null:explode('&', $param);
|
||||
return $this->findByArray($paths, $params, $matched_path);
|
||||
}
|
||||
/**
|
||||
* 查找url对应的实体
|
||||
* @param string $paths
|
||||
* @param string $params
|
||||
* @param array $matched_path 已匹配的路径
|
||||
* @return mixed|null
|
||||
*/
|
||||
function findByArray($paths,$params, &$matched_path =null){
|
||||
$paths = array_filter($paths,function ($i){return $i!== '/' &&!empty($i);});
|
||||
array_unshift($paths, '/');
|
||||
$paths[] = Tree::$end; //最后总是插入Tree::$end表示路径结束
|
||||
|
||||
$route=null;
|
||||
$valid_path=null;//最深一个不为空的路径
|
||||
$visited = 0;
|
||||
$geted=0;
|
||||
$walked_path=array();
|
||||
//找到匹配的最深路径,/a/b/c匹配的路径可以是/,/a,/a/c,/a/b/c,但最深的是/a/b/c
|
||||
$this->routes->visit($paths,function ($node,$value)use(&$valid_path,&$route,&$visited,&$geted,&$walked_path){
|
||||
if(!is_null($value)) {
|
||||
$route=$value;
|
||||
if(!($route instanceof \phprs\util\Tree)) {
|
||||
$valid_path=$value;
|
||||
}
|
||||
$geted = $visited;
|
||||
|
||||
}
|
||||
$walked_path[]=$node;
|
||||
$visited ++;
|
||||
return true;
|
||||
});
|
||||
if(is_null($route)){
|
||||
return null;
|
||||
}
|
||||
if($matched_path !==null){
|
||||
$matched_path = $walked_path;
|
||||
}
|
||||
//如果匹配完整的路径, 则还需要匹配querystring参数
|
||||
if(count($paths) == $geted+1){
|
||||
if($route instanceof \phprs\util\Tree){
|
||||
//条件要求有querystring,所以请求也需要有querystring
|
||||
if(empty($params)){
|
||||
return $valid_path;
|
||||
}
|
||||
$found = $this->findParams($route, $params);
|
||||
return is_null($found)?$valid_path:$found ;
|
||||
}else{
|
||||
return $route;
|
||||
}
|
||||
}else{//如果不匹配全路径,则只能处理没有querystring的条件
|
||||
if($route instanceof \phprs\util\Tree) {
|
||||
return $valid_path;
|
||||
}
|
||||
return $route;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* 查找querystring对应的实体
|
||||
* querystring 参数匹配条件在tree中的存储方式如下
|
||||
*
|
||||
* 1.c=xxx&f=xxx&g=xxx
|
||||
* 2.c=xxx&e=xxx&f-xxx
|
||||
* 3.b=xxx&c=xxx
|
||||
* (经过排序的数据结构)
|
||||
* c=xxx -> f=xxx -> g=xxx
|
||||
* \->e=xxx->f=xxx
|
||||
* b=xxx ->d=xxx
|
||||
* 当收到请求 b=xxx&d=xxx&a=xxx&c=xxx时
|
||||
* 先拆成数组再排序=> a=xxx,b=xxx,c=xxx,d=xxx
|
||||
* 先找 a=xxx(根据当前的数据结构,只可能是有一个或者没有,不可能存在多个),没找到则跳过
|
||||
* 找b=xxx,有,则在b=xxx节点下找c=xxx,没找到则跳过
|
||||
* 在b=xxx节点下找d=xxx,找到,且 b=xxx ->d=xxx是完整的规则、请求的params也已处理完,则命中此规则
|
||||
* @param Tree $root 根节点
|
||||
* @param $params 需要查找的节点名
|
||||
* @return mixed|null
|
||||
*/
|
||||
private function findParams($root,$params){
|
||||
|
||||
//排序,使参数与顺序无关
|
||||
sort($params,SORT_STRING);
|
||||
$params = array_filter($params,function ($i){return !empty($i);});
|
||||
|
||||
$find_step = array();
|
||||
$matching = null;
|
||||
foreach ($params as $param){
|
||||
$find_step[]=$param;
|
||||
$node = $root->findNode($find_step);
|
||||
if($node === null) {
|
||||
array_pop($find_step);//换一个接着找
|
||||
}else{
|
||||
if(array_key_exists('value', $node)){
|
||||
$matching = $node['value'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $matching;
|
||||
}
|
||||
/**
|
||||
* 增加一条规则
|
||||
* @param $query string 请求的url形式
|
||||
* @param $e mixed
|
||||
* @param $strict boolean 是否严格匹配
|
||||
* @returnboolean 是否插入成功
|
||||
*/
|
||||
public function insert($query,$e,$strict=false){
|
||||
list($path, $param) = explode('?', $query)+array( null, null );
|
||||
$path = str_replace('\\', '/', $path);
|
||||
$paths = explode('/', $path);
|
||||
$route=null;
|
||||
$params = null;
|
||||
if($param !==null && $param !== ""){
|
||||
$params = explode('&', $param);
|
||||
}
|
||||
if($strict || !empty($params)){
|
||||
$paths[]=Tree::$end; //对于严格匹配的路由规则(有querystring的路由,path部分也需要严格匹配),在路径末尾加入end,偏于匹配
|
||||
}
|
||||
return $this->insertByArray($paths,$params,$e);
|
||||
}
|
||||
/**
|
||||
* 增加一条路由规则
|
||||
* @param array $paths
|
||||
* @param array $params
|
||||
* @param mixed $e
|
||||
* @return boolean
|
||||
*/
|
||||
public function insertByArray($paths,$params,$e){
|
||||
//生成树形的路由表,便于快速查找,
|
||||
// 如 规则
|
||||
// "/b/a?z=1&y=2" => service1
|
||||
// "/b/a?z=1&x=2" => service2
|
||||
// 对应的 规则结构是
|
||||
// path -> a+
|
||||
// |-b+
|
||||
// param -> |-x+
|
||||
// |-z => service2
|
||||
// |-y+
|
||||
// |-z => service1
|
||||
//
|
||||
//
|
||||
//
|
||||
$paths = array_filter($paths, function ($i){return !empty($i);});
|
||||
array_unshift($paths, '/');
|
||||
if(empty($paths)){
|
||||
$paths = array('');
|
||||
}
|
||||
if( $params !== null){
|
||||
//排序,使参数与顺序无关
|
||||
sort($params, SORT_STRING);
|
||||
$params = array_filter($params,function ($i){return !empty($i);});
|
||||
$node = $this->routes->findNode($paths, true, true);
|
||||
if($node && $node['value'] instanceof \phprs\util\Tree){
|
||||
$route = $node['value'];
|
||||
return $route->insert($params, $e,false);
|
||||
}else{
|
||||
$route = new Tree();
|
||||
$route->insert($params, $e,false);
|
||||
return $this->routes->insert($paths, $route,false);
|
||||
}
|
||||
}else{
|
||||
return $this->routes->insert($paths, $e ,false);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 树打平输出成数组
|
||||
* @return array
|
||||
*/
|
||||
public function export(){
|
||||
$ori = $this->routes->export();
|
||||
$res = array();
|
||||
foreach ($ori as $v){
|
||||
list($path, $value) = $v;
|
||||
if($value instanceof \phprs\util\Tree){
|
||||
$querys = $value->export(); //提取querystring
|
||||
$path_str = self::pathToString($path);
|
||||
foreach ($querys as $query){
|
||||
list($param, $value) = $query;
|
||||
$res[]=array($path_str.'?'.implode('&',$param), $value);
|
||||
}
|
||||
}else{
|
||||
$res[]=array(self::pathToString($path), $value);
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
/**
|
||||
* 字符串路转径数组路
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
static public function stringToPath($path){
|
||||
$path = str_replace('\\', '/', $path);
|
||||
$paths = explode('/', $path);
|
||||
$paths = array_filter($paths,function ($i){return !empty($i);});
|
||||
return array_values($paths);
|
||||
}
|
||||
/**
|
||||
* 数组路径转字符串路径
|
||||
* @param array $path
|
||||
* @return string 用/连接的字符串
|
||||
*/
|
||||
static public function pathToString($path){
|
||||
$append=function (&$str, $v){
|
||||
//连续的/忽略
|
||||
if(strlen($str) !==0 && substr_compare($str, '/', strlen($str)-1) ===0 && $v==='/'){
|
||||
return;
|
||||
}
|
||||
//两个项目间加/
|
||||
if(strlen($str) !==0 && substr_compare($str, '/', strlen($str)-1) !==0 && $v!=='/'){
|
||||
$str =$str.'/'.$v;
|
||||
}else{
|
||||
$str.=$v;
|
||||
}
|
||||
};
|
||||
$str = '';
|
||||
foreach ($path as $i){
|
||||
$append($str, $i);
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
private $routes;
|
||||
}
|
||||
447
codes/agent/game/api/lib/phprs/util/IoCFactory.php
Normal file
447
codes/agent/game/api/lib/phprs/util/IoCFactory.php
Normal file
@@ -0,0 +1,447 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: IoCFactory.php 64919 2015-06-08 05:48:03Z caoyangmin $
|
||||
* TODO: Reflection有性能问题
|
||||
* @author caoyangmin
|
||||
* @brief
|
||||
* IoCFactory
|
||||
*/
|
||||
namespace phprs\util;
|
||||
|
||||
/**
|
||||
* 依赖注入工厂
|
||||
* 创建实例, 并根据配置注入依赖
|
||||
* @author caoym
|
||||
*
|
||||
*/
|
||||
class IoCFactory
|
||||
{
|
||||
/**
|
||||
* @param string|array $conf 文件或者配置数组
|
||||
* 配置数组格式如下:
|
||||
* [
|
||||
* id=>[
|
||||
* "class"=>类名,
|
||||
* "singleton"=>false, 是否是单例, 如果是, 则只会创建一份(同一个工厂内)
|
||||
* "pass_by_construct"=false, 属性是否通过构造函数传递, 如果不是, 则通过访问属性的方式传递
|
||||
* "properties"=>{
|
||||
* 属性名=>属性值
|
||||
* }
|
||||
* @param array $dict 设置字典
|
||||
* 配置文件中, 属性值可以使用{key}的方式指定模板变量, 注入属性到实例是这些模板变
|
||||
* 量会被替换成setDict方法设置的值
|
||||
* @param array $metas 类元信息, 如果不指定, 则自动从类文件中导出
|
||||
* ]
|
||||
*/
|
||||
public function __construct($conf=null, $dict=null, $metas=null)
|
||||
{
|
||||
if($conf === null){
|
||||
$this->conf = array();
|
||||
}elseif(is_array($conf)){
|
||||
$this->conf = $conf;
|
||||
}else{
|
||||
Verify::isTrue(is_file($conf), "$conf is not a valid file");
|
||||
if(strtolower(pathinfo($conf, PATHINFO_EXTENSION)) == 'php'){
|
||||
$this->conf = include($conf);
|
||||
}else{
|
||||
Verify::isTrue(false !== ($data = file_get_contents($conf)), "$conf open failed");
|
||||
$data = self::clearAnnotation($data);
|
||||
Verify::isTrue(is_array($this->conf = json_decode($data,true)), "$conf json_decode failed with ".json_last_error());
|
||||
}
|
||||
$this->conf_file = $conf;
|
||||
}
|
||||
if($dict !== null){
|
||||
$this->conf = $this->replaceByDict($this->conf, $dict);
|
||||
}
|
||||
$this->metas = $metas;
|
||||
}
|
||||
/**
|
||||
* 去掉注解
|
||||
* @param unknown $text
|
||||
*/
|
||||
static public function clearAnnotation($text){
|
||||
return AnnotationCleaner::clean($text);
|
||||
}
|
||||
/**
|
||||
* 获取配置的属性
|
||||
* @param string $id
|
||||
* @return array|null
|
||||
*/
|
||||
public function getConf($id=null){
|
||||
if($id === null){
|
||||
return $this->conf;
|
||||
}else{
|
||||
if(isset($this->conf[$id]) && isset($this->conf[$id]['properties'])){
|
||||
return $this->conf[$id]['properties'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param string $id
|
||||
*/
|
||||
public function getClassName($id=null){
|
||||
if(isset($this->conf[$id])){
|
||||
$class = $this->conf[$id];
|
||||
if(isset($class['class'])){
|
||||
return $class['class'];
|
||||
}else{
|
||||
return $id;
|
||||
}
|
||||
}else{
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 根据id得到对象实例
|
||||
* //TODO 单实例间互相引用会是问题
|
||||
* @param string $id
|
||||
* @param array $properties 类属性, 覆盖配置文件中的属性
|
||||
* @param callable $injector fun($src), 获取注入值的方法
|
||||
* @param callable $init fun($inst, &$got) 初始化实例, 在创建后, 调用构造函数前
|
||||
* @param array $construct_args 构造函数的参数
|
||||
* @return object
|
||||
*/
|
||||
public function create($id, $construct_args=null, $properties=null, $injector=null, $init=null )
|
||||
{
|
||||
if($properties === null){
|
||||
$properties = array();
|
||||
}
|
||||
if($construct_args===null){
|
||||
$construct_args = array();
|
||||
}
|
||||
$ins = null;
|
||||
$is_singleton = false;
|
||||
$class_name = $this->getClassName($id);
|
||||
if(isset($this->conf[$id])){
|
||||
$class = $this->conf[$id];
|
||||
$class_refl = new \ReflectionClass($class_name);
|
||||
// 如果是单例
|
||||
// 带构造参数的类不支持单例
|
||||
if (count($properties) ===0 && count($construct_args) === 0 && isset($class['singleton']) && $class['singleton']) {
|
||||
$is_singleton = true;
|
||||
if (isset($this->singletons[$id])) {
|
||||
$ins = $this->singletons[$id];
|
||||
Logger::info("get {$id}[$class_name] as singleton");
|
||||
return $ins;
|
||||
}
|
||||
}
|
||||
if(isset($class['properties'])){
|
||||
$properties = array_merge($class['properties'], $properties);
|
||||
}
|
||||
|
||||
if (isset($class['pass_by_construct']) && $class['pass_by_construct']){ //属性在构造时传入
|
||||
Verify::isTrue(count($construct_args) ===0, "$class_name pass_by_construct"); //构造时传输属性, 所以不能再传入其他构造参数
|
||||
//组合构造参数
|
||||
$construct_args = $this->buildConstructArgs($class_refl, $properties);
|
||||
$properties=array();
|
||||
}
|
||||
}else{
|
||||
$class_refl = new \ReflectionClass($class_name);
|
||||
}
|
||||
if(!$is_singleton){
|
||||
if (array_search($id, $this->create_stack) !== false){
|
||||
$tmp = $this->create_stack;
|
||||
Verify::e("create $id failed, cyclic dependencies can only used with singleton. cyclic:".print_r($tmp,true));
|
||||
}
|
||||
$this->create_stack[] = $id;
|
||||
}
|
||||
|
||||
try {
|
||||
if (isset($class['pass_by_construct']) && $class['pass_by_construct']){
|
||||
$ins = $class_refl->newInstanceArgs($construct_args);
|
||||
$meta = $this->getMetaInfo($class_refl);
|
||||
if ($is_singleton){
|
||||
$this->singletons[$id] = $ins;
|
||||
}
|
||||
$this->injectDependent($class_refl, $ins, $meta, $properties, $injector);
|
||||
}else{
|
||||
$nti = new NewThenInit($class_refl);
|
||||
$ins = $nti->getObject();
|
||||
$meta = $this->getMetaInfo($class_refl);
|
||||
if ($is_singleton){
|
||||
$this->singletons[$id] = $ins;
|
||||
}
|
||||
$this->injectDependent($class_refl, $ins, $meta, $properties, $injector);
|
||||
if($init !==null){
|
||||
$init($ins);
|
||||
}
|
||||
$nti->initArgs($construct_args);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
array_pop($this->create_stack);
|
||||
throw $e;
|
||||
}
|
||||
array_pop($this->create_stack);
|
||||
|
||||
Logger::info("create {$id}[$class_name] ok");
|
||||
return $ins;
|
||||
}
|
||||
/**
|
||||
* 取配置文件路径, 如果是通过数组创建的, 则返回null
|
||||
* @return string|NULL
|
||||
*/
|
||||
public function getConfFile(){
|
||||
return $this->conf_file;
|
||||
}
|
||||
/**
|
||||
* 配置中是否有指定id的类
|
||||
* @param string $id
|
||||
* @return boolean
|
||||
*/
|
||||
public function hasClass($id){
|
||||
return isset($this->conf[$id]);
|
||||
}
|
||||
/**
|
||||
* 设置属性值, 允许设置private/protected属性
|
||||
* @param $refl
|
||||
* @param object $class
|
||||
* 类名或实例
|
||||
* @param string $name
|
||||
* 属性名
|
||||
* @param mixed $value
|
||||
* 属性值
|
||||
*/
|
||||
static function setPropertyValue($refl, $ins, $name, $value)
|
||||
{
|
||||
Verify::isTrue($m = $refl->getProperty($name));
|
||||
$m->setAccessible(true);
|
||||
$m->setValue($ins, $value);
|
||||
}
|
||||
/**
|
||||
* 取属性值
|
||||
* @param $refl
|
||||
* @param object $ins
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
static function getPropertyValue($refl, $ins, $name)
|
||||
{
|
||||
Verify::isTrue($m = $refl->getProperty($name));
|
||||
$m->setAccessible(true);
|
||||
return $m->getValue($ins);
|
||||
}
|
||||
/**
|
||||
* 获取元信息
|
||||
* 会缓存
|
||||
* @param string $class
|
||||
* @return array
|
||||
*/
|
||||
public function getMetaInfo($class){
|
||||
if(is_string($class)){
|
||||
$refl = new \ReflectionClass($class);
|
||||
}else{
|
||||
$refl = $class;
|
||||
}
|
||||
$name = $refl->getName();
|
||||
if($this->metas !==null && isset($this->metas[$name])){
|
||||
return $this->metas[$name];
|
||||
}
|
||||
static $cache = null;
|
||||
if($cache === null){
|
||||
$cache = new Cache();
|
||||
}
|
||||
$succeeded = false;
|
||||
$cache_key = 'meta_'.sha1($refl->getFileName().'/'.$name);
|
||||
$data = $cache->get($cache_key, $succeeded);
|
||||
if($succeeded){
|
||||
return $data;
|
||||
}
|
||||
MetaInfo::testAnnotation();
|
||||
$data = MetaInfo::get($name);
|
||||
$files = [$refl->getFileName()];
|
||||
$parent = $refl->getParentClass();
|
||||
if($parent){
|
||||
$files[] = $parent->getFileName();
|
||||
}
|
||||
foreach ($refl->getInterfaces() as $i){
|
||||
$files[] = $i->getFileName();
|
||||
}
|
||||
$cache->set($cache_key, $data, 60, new FileExpiredChecker($files));
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据属性组合构造参数
|
||||
* @param ReflectionClass $class
|
||||
* @param array $properties
|
||||
* @return array
|
||||
*/
|
||||
private function buildConstructArgs($class, $properties){
|
||||
if($properties===null) {
|
||||
return array();
|
||||
}
|
||||
if(count($properties)==0){
|
||||
return array();
|
||||
}
|
||||
$refMethod = $class->getConstructor();
|
||||
$params = $refMethod->getParameters();
|
||||
$args = array();
|
||||
foreach ($params as $key => $param) {
|
||||
$param_name = $param->getName();
|
||||
if(isset($properties[$param_name])){
|
||||
$args[$key] = $this->getProperty($properties[$param_name]);
|
||||
}else{
|
||||
Verify::isTrue($param->isOptional(), "{$class->getName()}::__construct miss required param: $param_name");//参数没有指定, 除非是可选参数
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $args;
|
||||
}
|
||||
/**
|
||||
* 获取属性
|
||||
* 替换属性中的{}和@标记
|
||||
* @param string $value
|
||||
* @return object|string
|
||||
*/
|
||||
private function getProperty($value){
|
||||
if (is_string($value) && substr($value, 0, 1) == '@') {
|
||||
return $this->create(substr($value, 1));
|
||||
} else {
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注入依赖
|
||||
* @param ReflectionClass $refl;
|
||||
* @param unknown $ins
|
||||
* @param unknown $meta
|
||||
* @param unknown $properties
|
||||
* @return void
|
||||
*/
|
||||
public function injectDependent($refl, $ins, $meta, $properties, $injector=null)
|
||||
{
|
||||
$defaults=array();
|
||||
$class_name = $refl->getName();
|
||||
$class_defaults = $refl->getDefaultProperties();
|
||||
if(isset($meta['property']) ){
|
||||
foreach ($meta['property'] as $property => $value) {
|
||||
//参数是否可选
|
||||
if(isset($value['value']) && isset($value['value']['optional']) && $value['value']['optional']){
|
||||
continue;
|
||||
}
|
||||
//设置了默认值
|
||||
if(isset($value['value']) && isset($value['value']['default'])){
|
||||
$defaults[$property] = $value['value']['default'];
|
||||
continue;
|
||||
}
|
||||
// 是否设置了默认值
|
||||
if (array_key_exists($property, $class_defaults)) {
|
||||
continue;
|
||||
}
|
||||
Verify::isTrue(array_key_exists($property, $properties), "$class_name::$property is required");
|
||||
}
|
||||
}
|
||||
// 设置属性
|
||||
if ($properties !== null) {
|
||||
foreach ($properties as $name => $value) {
|
||||
unset($defaults[$name]);
|
||||
$v = $this->getProperty($value);
|
||||
self::setPropertyValue($refl, $ins, $name, $v);
|
||||
}
|
||||
}
|
||||
// 注入依赖
|
||||
if(isset($meta['inject'])){
|
||||
foreach ($meta['inject'] as $property => $value) {
|
||||
//先设置必须的属性
|
||||
if(is_array($value['value'])){
|
||||
$src = $value['value']['src'];
|
||||
//参数是否可选
|
||||
if(isset($value['value']) && isset($value['value']['optional']) && $value['value']['optional']){
|
||||
continue;
|
||||
}
|
||||
//设置了默认值
|
||||
if(isset($value['value']) && isset($value['value']['default'])){
|
||||
$defaults[$property] = $value['value']['default'];
|
||||
continue;
|
||||
}
|
||||
}else{
|
||||
$src = $value['value'];
|
||||
}
|
||||
//是否设置了默认值
|
||||
if(array_key_exists($property, $class_defaults)){
|
||||
continue;
|
||||
}
|
||||
if ($src === "ioc_factory" || $src == "factory"){
|
||||
continue;
|
||||
}else{
|
||||
$got = false;
|
||||
Verify::isTrue($injector !==null , "$class_name::$property is required");
|
||||
$val = $injector($src, $got);
|
||||
Verify::isTrue($got , "$class_name::$property is required");
|
||||
self::setPropertyValue($refl, $ins, $property, $val);
|
||||
unset($meta['inject'][$property]);
|
||||
}
|
||||
}
|
||||
//在设置可选的
|
||||
foreach ($meta['inject'] as $property => $value) {
|
||||
if(is_array($value['value'])){
|
||||
$src = $value['value']['src'];
|
||||
}else{
|
||||
$src = $value['value'];
|
||||
}
|
||||
if ( $src == "ioc_factory" || $src == "factory") {
|
||||
self::setPropertyValue($refl, $ins, $property, $this);
|
||||
unset($defaults[$property]);
|
||||
}else if($injector){
|
||||
$val = $injector($src, $got);
|
||||
if($got){
|
||||
self::setPropertyValue($refl, $ins, $property, $val);
|
||||
unset($defaults[$property]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 设置默认属性
|
||||
foreach ($defaults as $name => $value ){
|
||||
unset($defaults[$name]);
|
||||
$v = $this->getProperty($value);
|
||||
self::setPropertyValue($refl, $ins, $name, $v);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换字典
|
||||
* @see setDict
|
||||
* @param string|array $value
|
||||
* @return void
|
||||
*/
|
||||
private function replaceByDict($value, $dict){
|
||||
if(is_string($value)){
|
||||
$keys = $this->getDictKeys($value);
|
||||
foreach ($keys as $key){
|
||||
Verify::isTrue(isset($dict[$key]), "{$key} not specified");
|
||||
}
|
||||
foreach ($dict as $key=>$replace){
|
||||
$value = str_replace('{'.$key.'}', $replace, $value);
|
||||
}
|
||||
return $value;
|
||||
}else if(is_array($value)){
|
||||
foreach ($value as $k=>$v){
|
||||
$value[$k] = $this->replaceByDict($v, $dict);
|
||||
}
|
||||
return $value;
|
||||
}else{
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 从字符串中获取模板变量
|
||||
* @param string $value
|
||||
* @return array
|
||||
*/
|
||||
static function getDictKeys($value){
|
||||
preg_match_all('/\{([0-9a-zA-Z\-\_]*?)\}/', $value, $params);
|
||||
$params += array(null, array());
|
||||
return $params[1];
|
||||
}
|
||||
protected $metas;
|
||||
protected $singletons = array();
|
||||
protected $conf = null;
|
||||
protected $dict = array();
|
||||
protected $conf_file;
|
||||
private $create_stack=array(); // 正在创建的类,用于检测循环依赖
|
||||
}
|
||||
172
codes/agent/game/api/lib/phprs/util/IoCFactoryEx.php
Normal file
172
codes/agent/game/api/lib/phprs/util/IoCFactoryEx.php
Normal file
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
namespace phprs\util;
|
||||
|
||||
/**
|
||||
* 加强版IoCFactory...
|
||||
* 1. 创建的是指定类的容器实例,而不是类的实例
|
||||
* 2. 在第一次调用时才创建类的实例
|
||||
* 3. 支持@cache注释,对类的接口进行缓存
|
||||
* @author caoym
|
||||
*
|
||||
*/
|
||||
class IoCFactoryEx extends IoCFactory
|
||||
{
|
||||
public function __construct($conf=null, $dict=null, $metas=null){
|
||||
parent::__construct($conf, $dict, $metas);
|
||||
}
|
||||
/**
|
||||
* 根据id创建对象(的容器)实例
|
||||
* @param string $id
|
||||
* @param array $properties 类属性, 覆盖配置文件中的属性
|
||||
* @param callable $injector fun($src), 获取注入值的方法
|
||||
* @param callable $init fun($inst, &$got) 初始化实例, 在创建后, 调用构造函数前
|
||||
* @param array $construct_args 构造函数的参数
|
||||
* @return object
|
||||
*/
|
||||
public function create($id, $construct_args=null, $properties=null, $injector=null, $init=null ){
|
||||
$meta = $this->getMetaInfo($this->getClassName($id));
|
||||
if(isset($meta['cache'])){
|
||||
return new IoCObjectWrap($this, $id, $construct_args, $properties, $injector, $init);
|
||||
}
|
||||
return $this->createRawObject($id, $construct_args, $properties, $injector, $init);
|
||||
|
||||
}
|
||||
/**
|
||||
* 根据id创建对象(的容器)实例, 不使用容器
|
||||
* @param string $id
|
||||
* @param array $properties 类属性, 覆盖配置文件中的属性
|
||||
* @param callable $injector fun($src), 获取注入值的方法
|
||||
* @param callable $init fun($inst, &$got) 初始化实例, 在创建后, 调用构造函数前
|
||||
* @param array $construct_args 构造函数的参数
|
||||
* @return object
|
||||
*/
|
||||
public function createRawObject($id, $construct_args=null, $properties=null, $injector=null, $init=null ){
|
||||
return parent::create($id, $construct_args, $properties, $injector, $init);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 容器
|
||||
* @author caoym
|
||||
*
|
||||
*/
|
||||
class IoCObjectWrap{
|
||||
public function __construct($factory, $id, $construct_args, $properties, $injector, $init){
|
||||
$this->__impl__ = $factory->createRawObject('phprs\\util\\IoCContainer', [
|
||||
'id' => $id,
|
||||
'construct_args' => $construct_args,
|
||||
'properties' => $properties,
|
||||
'injector' => $injector,
|
||||
'init' => $init
|
||||
]);
|
||||
}
|
||||
public function __get($name){
|
||||
return $this->__impl__->getObj()->$name;
|
||||
}
|
||||
public function __set($name , $value ){
|
||||
$this->__impl__->getObj()->$name = $value;
|
||||
}
|
||||
public function __isset ($name){
|
||||
return isset($this->__impl__->getObj()->$name);
|
||||
}
|
||||
public function __unset ($name){
|
||||
unset($this->__impl__->getObj()->$name);
|
||||
}
|
||||
|
||||
public function __call($name, $arguments) {
|
||||
$arguments[0]=111;
|
||||
return;
|
||||
return $this->__impl__->callThroughCache($name, $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @var IoCContainerImpl
|
||||
*/
|
||||
private $__impl__;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @author caoym
|
||||
*/
|
||||
class IoCContainer{
|
||||
|
||||
public function __construct($id, $construct_args, $properties, $injector, $init){
|
||||
$this->id = $id;
|
||||
$this->construct_args = $construct_args;
|
||||
$this->properties = $properties;
|
||||
$this->injector = $injector;
|
||||
$this->init = $init;
|
||||
}
|
||||
public function getObj(){
|
||||
if($this->obj==null){
|
||||
$this->obj = $this->factory->createRawObject(
|
||||
$this->id ,
|
||||
$this->construct_args,
|
||||
$this->properties,
|
||||
$this->injector,
|
||||
$this->init
|
||||
);
|
||||
}
|
||||
return $this->obj;
|
||||
}
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function callThroughCache($method, $arguments){
|
||||
$op = $this->getCacheOptions($method);
|
||||
if($op){
|
||||
$got = false;
|
||||
$key = $this->genKey($method, $arguments);
|
||||
$res = $this->cache->get($key, $got);
|
||||
if($got){
|
||||
return $res;
|
||||
}else{
|
||||
/*static $methods = [];
|
||||
$name = $this->factory->getClassName($this->id).'::'.$method;
|
||||
if(!array_key_exists($name, $methods)){
|
||||
$refl = new \ReflectionClass($this->factory->getClassName($this->id));
|
||||
$methods[$name] = $refl->getMethod($method);
|
||||
}
|
||||
$res = $methods[$name]->invokeArgs($this->getObj(), $arguments);
|
||||
*/
|
||||
$res = call_user_func_array([$this->getObj(),$method], $arguments);
|
||||
$this->cache->set($key, $res, $op['ttl'], isset($op['checker'])?$op['checker']:null);
|
||||
return $res;
|
||||
}
|
||||
}else{
|
||||
return call_user_func_array([$this->getObj(), $method], $arguments);
|
||||
}
|
||||
}
|
||||
|
||||
public function getCacheOptions($method){
|
||||
if(!array_key_exists($method, $this->cachedMethods)){
|
||||
$meta = $this->factory->getMetaInfo($this->factory->getClassName($this->id));
|
||||
if(isset($meta['cache'][$method]['value'])){
|
||||
$val = $meta['cache'][$method]['value'];
|
||||
list($k, $v) = $val;
|
||||
Verify::isTrue($k == 'ttl', "no TTL with @cache in $method");
|
||||
$this->cachedMethods[$method][$k] = $v;
|
||||
}
|
||||
}
|
||||
return $this->cachedMethods[$method];
|
||||
}
|
||||
private function genKey($method, $arguments){
|
||||
return '_ioc_'.$this->id.$method.sha1(serialize($arguments));
|
||||
}
|
||||
private $obj;
|
||||
private $id;
|
||||
private $construct_args;
|
||||
private $properties;
|
||||
private $injector;
|
||||
private $init;
|
||||
private $cachedMethods=[];
|
||||
/**
|
||||
* @inject("factory")
|
||||
* @var IoCFactoryEx
|
||||
*/
|
||||
private $factory;
|
||||
/**
|
||||
* @property
|
||||
* @var CheckableCache
|
||||
*/
|
||||
private $cache;
|
||||
}
|
||||
34
codes/agent/game/api/lib/phprs/util/KVCatchInterface.php
Normal file
34
codes/agent/game/api/lib/phprs/util/KVCatchInterface.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: KVCatchInterface.php 57516 2014-12-23 05:44:20Z caoyangmin $
|
||||
*
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
*/
|
||||
namespace phprs\util;
|
||||
|
||||
interface KVCatchInterface
|
||||
{
|
||||
/**
|
||||
* 设置key
|
||||
* @param string $key
|
||||
* @param mixed $var
|
||||
* @param int $ttl
|
||||
* @return boolean
|
||||
*/
|
||||
public function set($key, $var, $ttl);
|
||||
/**
|
||||
* 删除key
|
||||
* @param string $key
|
||||
* @return boolean
|
||||
*/
|
||||
public function del($key);
|
||||
/**
|
||||
* get key
|
||||
* @param string $key
|
||||
* @param boolean $succeeded
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key, &$succeeded);
|
||||
|
||||
}
|
||||
118
codes/agent/game/api/lib/phprs/util/Logger.php
Normal file
118
codes/agent/game/api/lib/phprs/util/Logger.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: Logger.php 58820 2015-01-16 16:29:33Z caoyangmin $
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief
|
||||
*/
|
||||
namespace phprs\util;
|
||||
/**
|
||||
* 简单的日志输出, 方便应用替换自己的日志实现
|
||||
* @author caoym
|
||||
*
|
||||
*/
|
||||
class Logger
|
||||
{
|
||||
static $flags = 12; // ERROR|WARNING
|
||||
/**
|
||||
* echo输出
|
||||
*/
|
||||
static $to_echo;
|
||||
|
||||
/**
|
||||
* 忽略输出
|
||||
*/
|
||||
static $to_void;
|
||||
/**
|
||||
* trigger_error 输出
|
||||
* @var callable
|
||||
*/
|
||||
static $to_php_log;
|
||||
/**
|
||||
* 输出到哪, 默认为Logger::$to_php_log
|
||||
* @var callable
|
||||
*/
|
||||
static $writer;
|
||||
|
||||
const DEBUG=1;
|
||||
const INFO=2;
|
||||
const WARNING=4;
|
||||
const ERROR=8;
|
||||
/**
|
||||
* debug log
|
||||
* @param string $msg
|
||||
* @return void
|
||||
*/
|
||||
public static function debug($msg){
|
||||
if(self::$flags&self::DEBUG){
|
||||
call_user_func(Logger::$writer, self::DEBUG, $msg);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* info log
|
||||
* @param string $msg
|
||||
* @return void
|
||||
*/
|
||||
public static function info($msg){
|
||||
if(self::$flags&self::INFO){
|
||||
call_user_func(Logger::$writer, self::INFO, $msg);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* warning log
|
||||
* @param string $msg
|
||||
* @return void
|
||||
*/
|
||||
public static function warning($msg){
|
||||
if(self::$flags&self::WARNING){
|
||||
call_user_func(Logger::$writer, self::WARNING, $msg);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* error log
|
||||
* @param string $msg
|
||||
* @return void
|
||||
*/
|
||||
public static function error($msg){
|
||||
if(self::$flags&self::ERROR){
|
||||
call_user_func(Logger::$writer, self::ERROR, $msg);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* init
|
||||
* @return void
|
||||
*/
|
||||
public static function init(){
|
||||
Logger::$to_echo = function ($level, $message){
|
||||
$titles = array(
|
||||
Logger::DEBUG => '==DEBUG==',
|
||||
Logger::INFO => '==INFO==',
|
||||
Logger::WARNING => '==WARNING==',
|
||||
Logger::ERROR => '==ERROR==',
|
||||
);
|
||||
echo $titles[$level].' '.$message."<br>\n";
|
||||
};
|
||||
|
||||
Logger::$to_php_log = function ($level, $message)
|
||||
{
|
||||
$titles = array(
|
||||
Logger::DEBUG => E_USER_NOTICE,
|
||||
Logger::INFO => E_USER_NOTICE,
|
||||
Logger::WARNING => E_USER_WARNING,
|
||||
Logger::ERROR => E_USER_ERROR,
|
||||
);
|
||||
$caller = debug_backtrace()[2];
|
||||
trigger_error($message.' in '.$caller['file'].' on line '.$caller['line'].''."\n<br />", $titles[$level]);
|
||||
};
|
||||
|
||||
Logger::$to_void = function ($level, $message){
|
||||
};
|
||||
if(Logger::$writer === null){
|
||||
Logger::$writer = Logger::$to_php_log;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Logger::init();
|
||||
|
||||
219
codes/agent/game/api/lib/phprs/util/MessagePump.php
Normal file
219
codes/agent/game/api/lib/phprs/util/MessagePump.php
Normal file
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: MessagePump.php 58821 2015-01-17 03:51:41Z caoyangmin $
|
||||
*
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief ResetOutput
|
||||
*/
|
||||
namespace phprs\util;
|
||||
|
||||
/**
|
||||
* 处理消息循环
|
||||
* 消息执行顺序为先入后出(栈)
|
||||
* 消息被分成正常执行消息和空闲执行消息
|
||||
* 空闲执行消息只有在没有正常执行消息时才会执行
|
||||
* 允许同时存在多个队列,可以关闭某个队列,如果某个队列中所有消息完成,将触发end事件
|
||||
*
|
||||
* @author caoym
|
||||
*/
|
||||
class MessagePump
|
||||
{
|
||||
|
||||
/**
|
||||
* 创建一个新的消息队列
|
||||
* @param callable $onEnd 当队列执行完后调用
|
||||
* @return int 队列id
|
||||
*/
|
||||
public function newQueue(callable $onEnd = null)
|
||||
{
|
||||
Verify::isTrue(! isset($this->queues[$this->start_queue_id]));
|
||||
$this->action_queues[$this->start_queue_id] = array();
|
||||
$this->idle_queues[$this->start_queue_id] = array();
|
||||
$this->end_handles[$this->start_queue_id] = $onEnd;
|
||||
Logger::debug("[MQ {$this->start_queue_id}] created");
|
||||
return $this->start_queue_id++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭一个消息队列,队列中未执行的操作将不会再执行
|
||||
*
|
||||
* @param int $queue_id
|
||||
* @return void
|
||||
*/
|
||||
public function closeQueue($queue_id)
|
||||
{
|
||||
if (! isset($this->idle_queues[$queue_id])){
|
||||
return;
|
||||
}
|
||||
Logger::debug("[MQ $queue_id] attempt to close");
|
||||
// 队列末尾加入null,消息循环处理到null后任务队列结束
|
||||
array_unshift($this->action_queues[$queue_id], null);
|
||||
$this->next_action[]=$queue_id;
|
||||
// $this->idle_queues[$queue_id][]=null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入常规执行消息
|
||||
*
|
||||
* @param int $queue_id队列id
|
||||
* @param callable $action 调用方法
|
||||
* @param array $args 调用参数
|
||||
* @param callable $exception_handle异常处理
|
||||
* @param string $desc 描述信息
|
||||
* @param boolean immediately 是否立即执行
|
||||
* @return void
|
||||
*/
|
||||
public function pushAction($queue_id, $action, $args, $exception_handle, $desc, $immediately=false)
|
||||
{
|
||||
if (! isset($this->action_queues[$queue_id])) {
|
||||
Logger::warning("unknown message queue $queue_id");
|
||||
return;
|
||||
}
|
||||
$count=count($this->action_queues[$queue_id]);
|
||||
if($count && $this->action_queues[$queue_id][0] ===null){
|
||||
Logger::warning("[MQ $queue_id] try to add action to closed queue");
|
||||
return;
|
||||
}
|
||||
$action = array(
|
||||
$action,
|
||||
$args,
|
||||
$exception_handle,
|
||||
$desc,
|
||||
);
|
||||
if($immediately){
|
||||
Logger::debug("[MQ $queue_id] new action [$desc'] immediately");
|
||||
$this->callAction($queue_id, $action);
|
||||
}else{
|
||||
Logger::debug("[MQ $queue_id] new action [$desc']");
|
||||
$this->action_queues[$queue_id][] =$action;
|
||||
$this->next_action[] = $queue_id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入空闲执行消息
|
||||
*
|
||||
* @param callable $action
|
||||
* @param callable $exception_handle
|
||||
* @param string $desc
|
||||
* 描述信息
|
||||
* @return void
|
||||
*/
|
||||
public function pushIdle($queue_id, $action, $args, $exception_handle, $desc)
|
||||
{
|
||||
if (! isset($this->idle_queues[$queue_id])) {
|
||||
Logger::warning("unknown message queue $queue_id");
|
||||
return;
|
||||
}
|
||||
$this->idle_queues[$queue_id][] = array(
|
||||
$action,
|
||||
$args,
|
||||
$exception_handle,
|
||||
$desc,
|
||||
);
|
||||
$this->next_idle[] = $queue_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* 运行消息循环
|
||||
* @return void
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
if ($this->is_running) {
|
||||
return;
|
||||
}
|
||||
Logger::debug("[MQ Pump] begin");
|
||||
$this->is_running = true;
|
||||
while (count($this->next_action) || count($this->next_idle)) {
|
||||
//没有活动事件,执行idle事件
|
||||
if(count($this->next_action) === 0){
|
||||
$queue_id = array_pop($this->next_idle);
|
||||
if (!isset($this->idle_queues[$queue_id])) { // 队列可能被关闭
|
||||
continue;
|
||||
}
|
||||
$idle = array_pop($this->idle_queues[$queue_id]);
|
||||
Verify::isTrue($idle !== null, 'never been here!!');
|
||||
if ($idle[2] !== null) {
|
||||
try {
|
||||
call_user_func_array($idle[0], $idle[1]);
|
||||
} catch (\Exception $e) {
|
||||
$idle[2]($e);
|
||||
}
|
||||
} else {
|
||||
call_user_func_array($idle[0],$idle[1]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
$queue_id = array_pop($this->next_action);
|
||||
if (!isset($this->action_queues[$queue_id])) { // 队列可能被关闭
|
||||
continue;
|
||||
}
|
||||
$actions = &$this->action_queues[$queue_id];
|
||||
$left = count($actions);
|
||||
$action = array_pop($actions);
|
||||
if ($action !== null) {
|
||||
$this->callAction($queue_id, $action);
|
||||
} elseif ($left !== 0) {
|
||||
//null插入队列,表示执行队列结束, 可以安全关闭队列了
|
||||
$onend = $this->end_handles[$queue_id];
|
||||
unset($this->action_queues[$queue_id]);
|
||||
unset($this->idle_queues[$queue_id]);
|
||||
unset($this->end_handles[$queue_id]);
|
||||
Logger::debug("[MQ $queue_id] closed");
|
||||
if ($onend !== null) {
|
||||
$onend();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//不是在单个队列为空时将其关闭,因为对于存在子流程的时候,其消息队列可能为空,但
|
||||
//其他流程执行可能导致子流程产生活动消息,所以不能在队列为空时就关闭队列
|
||||
foreach ($this->end_handles as $onend){
|
||||
if($onend !== null){
|
||||
$onend();
|
||||
}
|
||||
}
|
||||
$this->next_action = array();
|
||||
$this->next_idle = array();
|
||||
$this->action_queues = array();
|
||||
$this->idle_queues = array();
|
||||
$this->end_handles= array();
|
||||
|
||||
$this->is_running = false;
|
||||
Logger::debug("[MQ Pump] end");
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param unknown $queue_id
|
||||
* @param unknown $action
|
||||
*/
|
||||
private function callAction($queue_id, $action){
|
||||
if ($action[3] !== null) {
|
||||
Logger::debug("[MQ $queue_id]".$action[3]);
|
||||
}
|
||||
if ($action[2] !== null) {
|
||||
try {
|
||||
call_user_func_array($action[0],$action[1]);
|
||||
} catch (\Exception $e) {
|
||||
Logger::warning("[MQ $queue_id] exception: $e");
|
||||
$action[2]($e);
|
||||
}
|
||||
} else {
|
||||
call_user_func_array($action[0],$action[1]);
|
||||
}
|
||||
}
|
||||
private $is_running = false;
|
||||
private $start_queue_id = 0;
|
||||
|
||||
private $next_action = array(); // 保存下一个操作所在的队列
|
||||
private $next_idle = array(); // 保存空闲时下一次执行操作所在的队列
|
||||
|
||||
private $action_queues = array(); // 执行队列
|
||||
private $idle_queues = array(); // 空闲队列
|
||||
|
||||
private $end_handles = array();
|
||||
}
|
||||
|
||||
?>
|
||||
83
codes/agent/game/api/lib/phprs/util/MetaInfo.php
Normal file
83
codes/agent/game/api/lib/phprs/util/MetaInfo.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
namespace phprs\util;
|
||||
|
||||
/**
|
||||
* @author caoym
|
||||
*/
|
||||
class AnnotationTest{
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function test(){
|
||||
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 元信息
|
||||
* 处理注释中的@annotation, 生成以@annotation为key的数组
|
||||
* @author caoym
|
||||
*/
|
||||
class MetaInfo
|
||||
{
|
||||
/**
|
||||
* 获取元信息
|
||||
* @param object|class $inst
|
||||
* @param boolean $record_doc 是否加载注释文本, 如果是
|
||||
* @param array $select, 只取选中的几个
|
||||
* @return array
|
||||
*/
|
||||
static function get($inst, $record_doc=false, $select=null){
|
||||
$reflection = new \ReflectionClass($inst);
|
||||
$reader= new AnnotationReader($reflection);
|
||||
$info = array();
|
||||
if($record_doc){
|
||||
if(false !== ($doc = $reflection->getDocComment())){
|
||||
$info['doc'] = $doc;
|
||||
}
|
||||
}
|
||||
if($select !== null){
|
||||
$select = array_flip($select);
|
||||
}
|
||||
foreach ($reader->getClassAnnotations($reflection, $record_doc) as $id =>$ann ){
|
||||
if($select !==null && !array_key_exists($id, $select)){
|
||||
continue;
|
||||
}
|
||||
$ann=$ann[0];//可能有多个重名的, 只取第一个
|
||||
$info[$id] = $ann;
|
||||
}
|
||||
foreach ($reflection->getMethods() as $method ){
|
||||
foreach ( $reader->getMethodAnnotations($method, $record_doc) as $id => $ann){
|
||||
if($select !==null && !array_key_exists($id, $select)){
|
||||
continue;
|
||||
}
|
||||
$ann=$ann[0];//可能有多个重名的, 只取第一个
|
||||
$info += array($id=>array());
|
||||
$info[$id][$method->getName()] = $ann;
|
||||
}
|
||||
}
|
||||
foreach ($reflection->getProperties() as $property ){
|
||||
foreach ( $reader->getPropertyAnnotations($property, $record_doc) as $id => $ann){
|
||||
if($select !==null && !array_key_exists($id, $select)){
|
||||
continue;
|
||||
}
|
||||
$ann = $ann[0];//可能有多个重名的, 只取第一个
|
||||
$info += array($id=>array());
|
||||
$info[$id][$property->getName()] = $ann;
|
||||
}
|
||||
}
|
||||
|
||||
return $info;
|
||||
}
|
||||
|
||||
static function testAnnotation(){
|
||||
Verify::isTrue(count(self::get(new AnnotationTest(),true)), 'Annotation dose not work! If opcache is enable, please set opcache.save_comments=1 and opcache.load_comments=1');
|
||||
}
|
||||
/**
|
||||
* 有效的元信息
|
||||
* @var unknown
|
||||
*/
|
||||
private static $valid=array();
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
108
codes/agent/game/api/lib/phprs/util/NestedStringCut.php
Normal file
108
codes/agent/game/api/lib/phprs/util/NestedStringCut.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/**$Id$*/
|
||||
namespace phprs\util;
|
||||
|
||||
/**
|
||||
* 剪出字符串中的嵌套字符串
|
||||
* 既从aaa"bb\"b"ccc中, 取出"bb\"b"
|
||||
* @author caoym
|
||||
*/
|
||||
class NestedStringCut{
|
||||
|
||||
public function __construct($str){
|
||||
|
||||
$pos = 0;
|
||||
$state = 'stateNormal';
|
||||
while (true){
|
||||
$pos = $this->$state($str, $pos, $state);
|
||||
if($pos === false){
|
||||
break;
|
||||
}
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getSnippets(){
|
||||
return $this->snippets;
|
||||
}
|
||||
|
||||
public function getText(){
|
||||
return implode('', $this->snippets);
|
||||
}
|
||||
/**
|
||||
* 将剪切后的字符串位置转换成原始字符串位置
|
||||
* @param int $pos
|
||||
* @param int
|
||||
*/
|
||||
public function mapPos($pos){
|
||||
|
||||
foreach ($this->snippets as $k => $v){
|
||||
$pos += $k;
|
||||
if($pos < $k + strlen($v)){
|
||||
break;
|
||||
}
|
||||
$pos -= ($k + strlen($v));
|
||||
|
||||
}
|
||||
return $pos;
|
||||
}
|
||||
/**
|
||||
* 普通状态
|
||||
*/
|
||||
private function stateNormal($str, $pos, &$next){
|
||||
$ori = $pos;
|
||||
$posSQ = strpos($str, '\'', $pos);
|
||||
$posDQ = strpos($str, '"', $pos);
|
||||
$pos = $posSQ;
|
||||
$this->subStateQ = '\'';
|
||||
$next = 'stateQ';
|
||||
if($posDQ !== false && (($posDQ < $pos) || ($pos === false)) ){
|
||||
$pos = $posDQ;
|
||||
$this->subStateQ = '"';
|
||||
}
|
||||
if($pos !== false){
|
||||
$this->snippets[$ori] = substr($str, $ori, $pos-$ori);
|
||||
$pos ++;
|
||||
}else{
|
||||
$this->snippets[$ori] = substr($str, $ori);
|
||||
}
|
||||
return $pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* 进入引号状态
|
||||
*/
|
||||
private function stateQ($str, $pos, &$next){
|
||||
$posESC = strpos($str, '\\', $pos);
|
||||
$posQ = strpos($str, $this->subStateQ, $pos);
|
||||
$pos = $posESC;
|
||||
$next = 'stateESC';
|
||||
|
||||
if($posQ !== false && (($posQ<$posESC) || ($posESC === false))){
|
||||
$pos = $posQ;
|
||||
$next = 'stateNormal';
|
||||
}
|
||||
if($pos !== false){
|
||||
$pos ++;
|
||||
}
|
||||
return $pos;
|
||||
}
|
||||
/**
|
||||
* 进入转义状态
|
||||
*/
|
||||
private function stateESC($str, $pos, &$next){
|
||||
$pos++;
|
||||
if($pos >= strlen($str)){
|
||||
return false;
|
||||
}
|
||||
$next = 'stateQ';
|
||||
return $pos;
|
||||
}
|
||||
/**
|
||||
* 去掉嵌套字符串后的内容
|
||||
* @var array
|
||||
*/
|
||||
private $snippets=array();
|
||||
|
||||
private $subStateQ;
|
||||
}
|
||||
57
codes/agent/game/api/lib/phprs/util/NewThenInit.php
Normal file
57
codes/agent/game/api/lib/phprs/util/NewThenInit.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: NewThenInit.php 65241 2015-06-12 01:55:00Z lipengcheng02 $
|
||||
*
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief
|
||||
* NewThenInit
|
||||
*/
|
||||
namespace phprs\util;
|
||||
/**
|
||||
* 创建和初始化分离
|
||||
* 先创建, 再初始化
|
||||
* @author caoym
|
||||
*/
|
||||
class NewThenInit
|
||||
{
|
||||
/**
|
||||
* @param string|ReflectionClass $class 类
|
||||
*/
|
||||
public function __construct($class){
|
||||
if(is_string($class)){
|
||||
$this->refl = new \ReflectionClass($class);
|
||||
}else{
|
||||
$this->refl = $class;
|
||||
}
|
||||
$this->obj = $this->refl->newInstanceWithoutConstructor();
|
||||
}
|
||||
/**
|
||||
* 返回创建的实例
|
||||
* @return object
|
||||
*/
|
||||
public function getObject(){
|
||||
return $this->obj;
|
||||
}
|
||||
/**
|
||||
* 初始化
|
||||
* @param 可变数量参数
|
||||
*/
|
||||
public function init($arg0 = null, $_ = null){
|
||||
$this->initArgs(func_get_args());
|
||||
}
|
||||
/**
|
||||
* 初始化
|
||||
* @param array $args 参数列表
|
||||
*/
|
||||
public function initArgs($args){
|
||||
$cnst = $this->refl->getConstructor();
|
||||
if($cnst !== null) {
|
||||
$cnst->invokeArgs( $this->obj, $args);
|
||||
}else{
|
||||
Verify::isTrue(count($args) ===0, $this->refl->getName().' no constructor found with '.func_num_args().' params');
|
||||
}
|
||||
}
|
||||
private $refl;
|
||||
private $obj;
|
||||
}
|
||||
93
codes/agent/game/api/lib/phprs/util/RedisCache.php
Normal file
93
codes/agent/game/api/lib/phprs/util/RedisCache.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
namespace phprs\util;
|
||||
use phprs\util\KVCatchInterface;
|
||||
|
||||
/*class Redis{
|
||||
public function connect(){}
|
||||
public function set(){}
|
||||
public function get(){}
|
||||
public function del(){}
|
||||
}*/
|
||||
|
||||
class RedisCache implements KVCatchInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* 设置key
|
||||
* @param string $key
|
||||
* @param mixed $var
|
||||
* @param int $ttl
|
||||
* @return boolean
|
||||
*/
|
||||
public function set($key, $var, $ttl=null){
|
||||
if($this->serialize){
|
||||
$var = serialize($var);
|
||||
}
|
||||
if($ttl === null){
|
||||
$this->getImpl()->set($key, $var);
|
||||
}else{
|
||||
return $this->getImpl()->setex($key, $ttl, $var);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 删除key
|
||||
* @param string $key
|
||||
* @return boolean
|
||||
*/
|
||||
public function del($key){
|
||||
return $this->getImpl()->delete($key) == 1;
|
||||
}
|
||||
/**
|
||||
* get key
|
||||
* @param string $key
|
||||
* @param boolean $succeeded
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key, &$succeeded=null){
|
||||
$res = $this->getImpl()->get($key);
|
||||
$succeeded = ($res !== false);
|
||||
if($this->serialize){
|
||||
$res = unserialize($res);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
private function getImpl(){
|
||||
if($this->redis === null){
|
||||
$this->redis = new \Redis();
|
||||
}
|
||||
if(!$this->redis->isConnected()){
|
||||
$this->redis->connect($this->host, $this->port);
|
||||
}
|
||||
if(!empty($this->user) && !empty($this->pwd)){
|
||||
Verify::isTrue($this->redis->auth($this->user . ":" . $this->pwd), $this->redis->getLastError());
|
||||
}
|
||||
return $this->redis;
|
||||
}
|
||||
/**
|
||||
* @property
|
||||
* 服务器地址
|
||||
*/
|
||||
private $host;
|
||||
/**
|
||||
* @property
|
||||
* 服务器端口
|
||||
*/
|
||||
private $port;
|
||||
|
||||
/**
|
||||
* @property
|
||||
* 服务器实例Id
|
||||
*/
|
||||
private $user;
|
||||
/**
|
||||
* @property
|
||||
* 服务器实例密码
|
||||
*/
|
||||
private $pwd;
|
||||
/**
|
||||
* @var \Redis
|
||||
*/
|
||||
private $redis;
|
||||
/** @property */
|
||||
private $serialize = true;
|
||||
}
|
||||
49
codes/agent/game/api/lib/phprs/util/SaftyFileWriter.php
Normal file
49
codes/agent/game/api/lib/phprs/util/SaftyFileWriter.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: SaftyFileWriter.php 57435 2014-12-21 15:04:22Z caoyangmin $
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief
|
||||
*/
|
||||
|
||||
namespace phprs\util;
|
||||
/**
|
||||
* 并发安全的写文件
|
||||
* 其原子性取决于文件系统
|
||||
* 通过先写临时文件, 然后重命名的方式实现
|
||||
* @author caoym
|
||||
*
|
||||
*/
|
||||
class SaftyFileWriter
|
||||
{
|
||||
/**
|
||||
* 写入文件
|
||||
* @param string $path 路径
|
||||
* @param mixed $data 写入的值
|
||||
* @param boolean $overwrite 是否覆盖已有文件
|
||||
* @return boolean
|
||||
*/
|
||||
static public function write($path, $data, $overwrite = true){
|
||||
$path = str_replace('\\', '/', $path);
|
||||
$pos = strrpos ($path, '/');
|
||||
$file_name="";
|
||||
$file_dir="";
|
||||
if($pos === false){
|
||||
$file_name=$path;
|
||||
}else{
|
||||
$file_dir = substr($path, 0,$pos+1);
|
||||
$file_name = substr($path, $pos+1);
|
||||
}
|
||||
$tmp_file= tempnam($file_dir, 'tsb_sfw');
|
||||
Verify::isTrue(false !== file_put_contents($tmp_file, $data), "write to file: $tmp_file failed");
|
||||
if($overwrite){
|
||||
@unlink($path); //删除原始文件
|
||||
}
|
||||
if(!@rename($tmp_file, $path)){
|
||||
@unlink($tmp_file); //删除原始文件
|
||||
Verify::e("write to file: $tmp_file failed");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
57
codes/agent/game/api/lib/phprs/util/SerializableFunc.php
Normal file
57
codes/agent/game/api/lib/phprs/util/SerializableFunc.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: SerializableFunc.php 56636 2014-12-03 11:26:59Z caoyangmin $
|
||||
*
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief SerializableFunc
|
||||
*/
|
||||
namespace phprs\util;
|
||||
|
||||
/**
|
||||
* 支持序列化的函数
|
||||
* @author caoym
|
||||
*
|
||||
*/
|
||||
class SerializableFunc{
|
||||
/**
|
||||
* 方法,绑定参数
|
||||
* 如
|
||||
* func,arg1,arg2
|
||||
* array('a','method1'), arg1,arg2
|
||||
*/
|
||||
public function __construct(/*$func, $bind*/){
|
||||
$args = func_get_args();
|
||||
Verify::isTrue(count($args)>0);
|
||||
Verify::isTrue(is_callable($args[0]));
|
||||
$this->func = $args[0];
|
||||
$this->bind = array_slice($args,1);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 调用时,将bind参数加在方法的最前面
|
||||
* @return mixed
|
||||
*/
|
||||
public function __invoke(){
|
||||
$args = func_get_args();
|
||||
$params = $this->bind;
|
||||
foreach ($args as $arg){
|
||||
array_push($params, $arg);
|
||||
}
|
||||
$res = call_user_func_array($this->func, $params);
|
||||
foreach ($this->next as $next){
|
||||
call_user_func_array($next,$args);
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
/**
|
||||
* 串行调用
|
||||
* @var unknown
|
||||
*/
|
||||
public $next=array();
|
||||
private $bind;
|
||||
private $func;
|
||||
}
|
||||
|
||||
?>
|
||||
201
codes/agent/game/api/lib/phprs/util/Tree.php
Normal file
201
codes/agent/game/api/lib/phprs/util/Tree.php
Normal file
@@ -0,0 +1,201 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: Tree.php 58820 2015-01-16 16:29:33Z caoyangmin $
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief Tree
|
||||
*/
|
||||
namespace phprs\util;
|
||||
class Tree {
|
||||
/**
|
||||
* 插入一个节点
|
||||
* 插入时允许使用通配符*
|
||||
* @param array $path
|
||||
* @param unknown $value
|
||||
* @param string $replace_exits 是否替换已存在的
|
||||
* @param mixed $replaced 被替换的原始值
|
||||
* @return boolean
|
||||
*/
|
||||
public function insert( $path, $value, $replace_exits=false, &$replaced=null){
|
||||
assert(count($path));
|
||||
$left = $path;
|
||||
$cur = array_shift($left);//剩余未遍历的
|
||||
if(!array_key_exists($cur,$this->arr)){
|
||||
$this->arr[$cur] = array();
|
||||
}
|
||||
$insert= false;
|
||||
$this->visitNode($path,function ($name,&$node)use(&$insert,&$left,$value,$replace_exits, &$replaced){
|
||||
$cur = array_shift($left);
|
||||
if($cur === null){
|
||||
if(array_key_exists('value',$node)){//是否已存在
|
||||
if(!$replace_exits){
|
||||
return false;
|
||||
}
|
||||
$replaced = $node['value'];
|
||||
}
|
||||
$node['value'] = $value;
|
||||
$insert = true;
|
||||
return false;//停止遍历
|
||||
}else{
|
||||
if(!array_key_exists('next',$node)){
|
||||
$node['next'] = array();
|
||||
}
|
||||
if(!array_key_exists($cur,$node['next'])){
|
||||
$node['next'][$cur]= array();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}, true);
|
||||
return $insert;
|
||||
}
|
||||
/**
|
||||
* 删除一个路径下的所有节点
|
||||
* @param array $path
|
||||
* @return boolean
|
||||
*/
|
||||
public function erase(array $path){
|
||||
if(count($path)==0){
|
||||
return false;
|
||||
}
|
||||
$size = count($path);
|
||||
$key = $path[$size-1];
|
||||
if($size ==1){
|
||||
if(array_key_exists($key, $this->arr)){
|
||||
unset($this->arr[$key]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
$i = 0;
|
||||
$res = false;
|
||||
$full = $this->visitNode($path,function ($name,&$node)use(&$i,$size,$key,&$res){
|
||||
if(++$i == $size-1){
|
||||
if(isset($node['next']) && isset($node['next'][$key])){
|
||||
unset($node['next'][$key]);
|
||||
$res = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}, true);
|
||||
return $res;
|
||||
}
|
||||
/**
|
||||
* 遍历路径
|
||||
* @param array $path
|
||||
* @return boolean 全部遍历完返回true,否则返回false
|
||||
*/
|
||||
public function visit( $path ,$vistor, $exact_match=false){
|
||||
return $this->visitNode($path,function ($name,$node)use($vistor){
|
||||
return $vistor($name,array_key_exists('value',$node)?$node['value']:null);
|
||||
}, $exact_match);
|
||||
}
|
||||
/**
|
||||
* 查找指定路径的节点
|
||||
* @param array $path
|
||||
* @param boolean $exact_match 是否精确匹配,如果是,则通配符被认为与其他值不同
|
||||
* @return 返回节点的值, 或者null
|
||||
*/
|
||||
public function find(array $path, $exact_match=false){
|
||||
$found = null;
|
||||
$full = $this->visitNode($path,function ($name,$node)use(&$found){
|
||||
$found = array_key_exists('value',$node)?$node['value']:null;
|
||||
return true;
|
||||
}, $exact_match);
|
||||
return $full?$found:null;
|
||||
}
|
||||
/**
|
||||
* 查找指定路径的节点
|
||||
* @param array $path
|
||||
* @param boolean $exact_match 是否精确匹配,如果是,则通配符被认为与其他值不同
|
||||
* @param boolean $all_req_paths 是否要求查询路径的所有元素都必须遍历到
|
||||
* @param boolean $all_paths 是否
|
||||
* @return 返回节点的, 或者null
|
||||
*/
|
||||
public function findNode(array $path, $exact_match=false, $all_req_paths=false){
|
||||
$found = null;
|
||||
$full = $this->visitNode($path,function ($name,$node)use(&$found){
|
||||
$found = $node;
|
||||
return true;
|
||||
}, $exact_match, $all_req_paths);
|
||||
return $full?$found:null;
|
||||
}
|
||||
/**
|
||||
* 遍历路径
|
||||
* @param array $path
|
||||
* @param boolean $exact_match 是否精确匹配,如果是,则通配符被认为与其他值不同
|
||||
* @param boolean $all_req_paths 是否要求查询路径的所有元素都必须遍历到
|
||||
* @return boolean 变量完成返回true,遍历未完成时终止返回false
|
||||
*/
|
||||
private function visitNode( $path, $vistor, $exact_match=false, $all_req_paths=false){
|
||||
assert(count($path));
|
||||
$pos = &$this->arr;
|
||||
$next = &$pos;
|
||||
foreach ($path as $i){
|
||||
if(is_null($next)) {
|
||||
return !$all_req_paths;
|
||||
}
|
||||
if(!array_key_exists($i, $next)){
|
||||
if($exact_match){
|
||||
return false;
|
||||
}
|
||||
if($i == self::$end){
|
||||
return false;
|
||||
}
|
||||
//不要求完全匹配, 尝试匹配通配符
|
||||
if(!array_key_exists(self::$wildcard, $next)){
|
||||
return false;
|
||||
}
|
||||
$pos = &$next[self::$wildcard];
|
||||
}else{
|
||||
$pos = &$next[$i];
|
||||
}
|
||||
if(!$vistor($i,$pos)){
|
||||
return false;
|
||||
}
|
||||
if(array_key_exists('next',$pos)){
|
||||
$next = &$pos['next'];
|
||||
}else{
|
||||
$nul = null;
|
||||
$next = &$nul; //$next = null 会导致引用的对象被赋值,而不是next被赋值
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* 树打平输出成数组
|
||||
* @return array
|
||||
*/
|
||||
public function export(){
|
||||
$res=array();
|
||||
self::treeToArray($this->arr, $res);
|
||||
return $res;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param array $tree
|
||||
* @param array $res
|
||||
* @return void
|
||||
*/
|
||||
static private function treeToArray($tree, &$res){
|
||||
foreach ($tree as $name=>$node){
|
||||
if($node === null){
|
||||
continue;
|
||||
}
|
||||
if( isset($node['value']) ){
|
||||
$res[] = array(array($name), $node['value']);
|
||||
}
|
||||
if( isset($node['next']) ){
|
||||
$tmp = array();
|
||||
self::treeToArray($node['next'], $tmp);
|
||||
foreach ($tmp as $v){
|
||||
array_unshift($v[0], $name);
|
||||
$res[] = array($v[0], $v[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private $arr=array();
|
||||
static public $end="\n";
|
||||
static public $wildcard='*';
|
||||
}
|
||||
49
codes/agent/game/api/lib/phprs/util/Verify.php
Normal file
49
codes/agent/game/api/lib/phprs/util/Verify.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: Verify.php 57435 2014-12-21 15:04:22Z caoyangmin $
|
||||
* @author caoym(caoyangmin@gmail.com)
|
||||
* @brief
|
||||
*/
|
||||
namespace phprs\util;
|
||||
/**
|
||||
* if(false) throw ;
|
||||
* @param boolen $var 判断条件
|
||||
* @param string $msg 异常消息
|
||||
* @throws Exception
|
||||
* @return unknown
|
||||
*/
|
||||
class Verify{
|
||||
/**
|
||||
* 如果判断不为true,抛出异常
|
||||
* @param boolean $var
|
||||
* @param string|Exception $msg
|
||||
* @param number $code
|
||||
* @throws \Exception
|
||||
* @return unknown
|
||||
*/
|
||||
static public function isTrue($var, $msg = null)
|
||||
{
|
||||
if (!$var) {
|
||||
if($msg === null || is_string($msg)){
|
||||
throw new \Exception($msg);
|
||||
}else{
|
||||
throw $msg;
|
||||
}
|
||||
} else {
|
||||
return $var;
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param \Exception|string $e
|
||||
* @throws unknown
|
||||
*/
|
||||
static public function e($e){
|
||||
if ($e === null || is_string($e)) {
|
||||
throw new \Exception($e);
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace phprs\util\exceptions;
|
||||
|
||||
class BadRequest extends \Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace phprs\util\exceptions;
|
||||
|
||||
class Forbidden extends \Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace phprs\util\exceptions;
|
||||
|
||||
class NotFound extends \Exception
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user