添加后台代理代码
This commit is contained in:
13
codes/agent/game/api/lib/phprs/AutoLoad.php
Normal file
13
codes/agent/game/api/lib/phprs/AutoLoad.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: AutoLoad.php 56761 2014-12-08 05:17:37Z caoyangmin $
|
||||
* @author caoyangmin(caoyangmin@gmail.com)
|
||||
* @brief AutoLoad
|
||||
*/
|
||||
namespace phprs;
|
||||
require __DIR__.'/util/ClassLoader.php';
|
||||
require __DIR__.'/util/AutoClassLoader.php';
|
||||
use phprs\util\ClassLoader;
|
||||
ClassLoader::addInclude(dirname(__DIR__));
|
||||
spl_autoload_register(array(__NAMESPACE__.'\util\ClassLoader', 'autoLoad'));
|
||||
109
codes/agent/game/api/lib/phprs/BindParams.php
Normal file
109
codes/agent/game/api/lib/phprs/BindParams.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/**
|
||||
* $Id: BindParams.php 60686 2015-03-10 10:48:49Z caoyangmin $
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief
|
||||
*/
|
||||
namespace phprs;
|
||||
|
||||
use phprs\util\Verify;
|
||||
use phprs\util\exceptions\BadRequest;
|
||||
|
||||
//TODO: 把绑定参数的各种信息保存在数组中, 已经有点难看了.
|
||||
/**
|
||||
*
|
||||
* @author caoym
|
||||
* 绑定响应
|
||||
*/
|
||||
class BindParams{
|
||||
/**
|
||||
* @param string $class_name
|
||||
* @param string $method_name
|
||||
*/
|
||||
public function __construct($class_name, $method_name){
|
||||
$this->class_name = $class_name;
|
||||
$this->method_name = $method_name;
|
||||
}
|
||||
/**
|
||||
* 设置绑定
|
||||
* @param int $id 参数声明的序号
|
||||
* @param $params 绑定相关参数:[目标变量 , 源变量]
|
||||
* @param $method_info 方法变量信息 [ [变量名, 是否引用, 是否有默认值, 默认值], ...
|
||||
*/
|
||||
public function set($id, $params, $method_info){
|
||||
Verify::isTrue(is_array($params) && count($params) ==2, "{$this->class_name}::{$this->method_name} invalid @param");
|
||||
list($to, $from) = $params;
|
||||
$pos = -1;
|
||||
$step = 0;
|
||||
foreach ($method_info as $item){
|
||||
list($param_name, ) = $item;
|
||||
if($to === $param_name){
|
||||
$pos = $step;
|
||||
break;
|
||||
}
|
||||
$step++;
|
||||
}
|
||||
Verify::isTrue($pos !== -1, "{$this->class_name}::{$this->method_name} param: $to not found");
|
||||
Verify::isTrue(!isset($this->params[$pos]),"{$this->class_name}::{$this->method_name} param: $to repeated bound" );
|
||||
$this->params[$pos] = array(substr($from, 0, 1) !='$', $from, $item,$id,);//[是否常量, 值 , 参数信息]
|
||||
}
|
||||
/**
|
||||
* 绑定到函数调用的参数上去
|
||||
* @param $req
|
||||
* @param $res
|
||||
* @param array $args
|
||||
*/
|
||||
public function bind($req, &$res, &$args){
|
||||
foreach ($this->params as $pos=>$param){
|
||||
list($is_const, $value, $info) = $param;
|
||||
if($is_const){// 常量
|
||||
$args[$pos] = $value;
|
||||
}else{ //变量
|
||||
list(, $is_ref, $is_optional, $default) = $info;
|
||||
$found = $req->find($value, $is_ref, $default);
|
||||
if(!$found[1]){
|
||||
Verify::isTrue($is_optional, new BadRequest("{$this->class_name}::{$this->method_name} $value not found in request"));
|
||||
$args[$pos] = $default;
|
||||
}else{
|
||||
if($is_ref){
|
||||
$args[$pos] = &$found[0];
|
||||
}else{
|
||||
$args[$pos] = $found[0];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 获取被绑定的参数列表
|
||||
* 返回的是参数位置
|
||||
* @return array
|
||||
*/
|
||||
public function getBindParamPos(){
|
||||
$params=array();
|
||||
foreach ($this->params as $pos=>$param){
|
||||
$params[] = $pos;
|
||||
}
|
||||
return $params;
|
||||
}
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function isEmpty(){
|
||||
return count($this->params) ===0;
|
||||
}
|
||||
/**
|
||||
* 获取绑定参数信息
|
||||
* @return array
|
||||
* [ [输入参数位置=>[是否常量, 值, 参数信息]], ...]
|
||||
* 其中参数信息形式
|
||||
* [变量名, 是否引用, 是否有默认值, 默认值]
|
||||
*/
|
||||
public function getParams(){
|
||||
return $this->params;
|
||||
}
|
||||
private $params= array();
|
||||
private $class_name;
|
||||
private $method_name;
|
||||
}
|
||||
175
codes/agent/game/api/lib/phprs/BindReturns.php
Normal file
175
codes/agent/game/api/lib/phprs/BindReturns.php
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
/**
|
||||
* $Id: BindReturns.php 60686 2015-03-10 10:48:49Z caoyangmin $
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief
|
||||
*/
|
||||
namespace phprs;
|
||||
use phprs\util\Verify;
|
||||
|
||||
/**
|
||||
* 绑定@return 变量
|
||||
* @author caoym
|
||||
*
|
||||
*/
|
||||
class BindReturns
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @param string $class_name
|
||||
* @param string $method_name
|
||||
* @param boolean $auto_bind_return 是否默认绑定返回值
|
||||
*/
|
||||
public function __construct($class_name, $method_name, $auto_bind_return=true){
|
||||
$this->class_name = $class_name;
|
||||
$this->method_name = $method_name;
|
||||
$this->auto_bind_return = $auto_bind_return;
|
||||
//默认return输出到body
|
||||
if($auto_bind_return){
|
||||
$this->params['body'][-1] = array(0=>array(false, null, -1, null));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置绑定
|
||||
* @param $params 绑定相关参数:[目标变量 , 源变量]
|
||||
* @param $method_info 方法变量信息 [变量名=>[是否引用, 是否有默认值, 默认值]]
|
||||
*/
|
||||
public function set($id, $params, $method_info){
|
||||
|
||||
Verify::isTrue(is_array($params)||is_string($params), "{$this->class_name}::{$this->method_name} invalid @return");
|
||||
if (is_string($params)){
|
||||
$to = $params;
|
||||
$from = array();
|
||||
}else{
|
||||
$to = $params[0];
|
||||
$from = array_slice($params, 1);
|
||||
}
|
||||
|
||||
$to_func = &$this->params[$to][$id];
|
||||
if($this->auto_bind_return && ($to == 'body'||$to == 'res') && isset($this->params['body'][-1])){
|
||||
unset($this->params['body'][-1]);// 指定了body输出, 去掉默认的body输出
|
||||
}
|
||||
if(0 === count($from)){
|
||||
$to_func[0]=array(false, null, -1, null,);
|
||||
}
|
||||
foreach ($from as $index=>$name){ // 输入方法 $index变量序号 $name变量名
|
||||
$is_const = (substr($name, 0, 1) !='$');
|
||||
if ($is_const){ // 输出常量, 不需要绑定变量
|
||||
$to_func[$index] = array(true, $name, 0, null,);//[是否常量, 值 , 参数来源位置, 参数信息]
|
||||
continue;
|
||||
}
|
||||
$name = substr($name, 1);
|
||||
//变量输出, 需要绑定
|
||||
$pos = -1;
|
||||
$step = 0;
|
||||
|
||||
//是(输出)方法的第几个参数
|
||||
foreach ($method_info as $item){
|
||||
list($param_name, ) = $item;
|
||||
if($name === $param_name){
|
||||
$pos = $step;
|
||||
break;
|
||||
}
|
||||
$step++;
|
||||
}
|
||||
Verify::isTrue($pos !== -1, "{$this->class_name}::{$this->method_name} param: $name not found");
|
||||
//只能是引用
|
||||
list(, $is_ref, ) = $item;
|
||||
Verify::isTrue($is_ref, "{$this->class_name}::{$this->method_name} param: $name @return must be a reference");
|
||||
$to_func[$index] = array(false, $name, $pos, $item,);//[是否常量, 值 , 参数位置, 参数信息]
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 绑定到函数调用的参数上去
|
||||
* @param $req
|
||||
* @param $res
|
||||
* @param array $args
|
||||
*/
|
||||
public function bind($req, &$res, &$args)
|
||||
{
|
||||
//params保存的数据
|
||||
// [
|
||||
// 'body'=>[// 输出方法
|
||||
// [arg1, arg2, arg3], //调用列表
|
||||
// [arg1, arg2, arg3]
|
||||
// ],
|
||||
// ]
|
||||
// ]
|
||||
//没有指定body输出, 默认使用返回值作为输出
|
||||
|
||||
foreach ($this->params as $fun_name => $calls) {//
|
||||
foreach ($calls as $id => $call) {
|
||||
foreach ($call as $num => $arg) { // 方法
|
||||
list ($is_const, $value, $pos, $info) = $arg;
|
||||
if ($is_const) { // 常量,直接输出
|
||||
$res[$fun_name][$id][$num] = $value;
|
||||
} else if($pos === -1){ // 返回值作为变量输出
|
||||
$res[$fun_name][$id][$num] = &$this->return_val;
|
||||
}else{
|
||||
if (! array_key_exists($pos, $args)) { // 没有输入,只有输出
|
||||
list (, $is_ref, $is_optional, $default) = $info;
|
||||
if ($is_optional) {
|
||||
$args[$pos] = $default;
|
||||
} else {
|
||||
$args[$pos] = null;
|
||||
}
|
||||
}
|
||||
$res[$fun_name][$id][$num] = &$args[$pos];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* 设置返回值
|
||||
* @param unknown $var
|
||||
*/
|
||||
public function setReturn($var){
|
||||
$this->return_val = $var;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取被绑定的参数列表
|
||||
* 返回的是参数位置
|
||||
* @return array
|
||||
*/
|
||||
public function getBindParamPos(){
|
||||
$params=array();
|
||||
foreach ($this->params as $fun_name => $calls) {
|
||||
foreach ($calls as $id => $call) {
|
||||
foreach ($call as $num => $arg) {
|
||||
list ($is_const, $value, $pos, $info) = $arg;
|
||||
if($pos !== -1){
|
||||
$params[] = $pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $params;
|
||||
|
||||
}
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function isEmpty(){
|
||||
return count($this->params) ===0;
|
||||
}
|
||||
/**
|
||||
* 返回绑定的参数信息
|
||||
* @return array
|
||||
* [[输出方法=>[[是否常量, 值 , 输出参数位置, 参数信息],..]]
|
||||
*
|
||||
*/
|
||||
public function getParams(){
|
||||
return $this->params;
|
||||
}
|
||||
private $params= array();// [目标=>[[是否常量, 值 , 参数位置, 参数信息],..]]
|
||||
private $class_name;
|
||||
private $method_name;
|
||||
private $return_val;
|
||||
private $auto_bind_return;
|
||||
}
|
||||
|
||||
?>
|
||||
109
codes/agent/game/api/lib/phprs/BindThrows.php
Normal file
109
codes/agent/game/api/lib/phprs/BindThrows.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
/**
|
||||
* $Id: BindThrows.php 57067 2014-12-15 05:39:13Z caoyangmin $
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief
|
||||
*/
|
||||
namespace phprs;
|
||||
use phprs\util\Verify;
|
||||
/**
|
||||
*
|
||||
* @author caoym
|
||||
*
|
||||
*/
|
||||
class BindThrows
|
||||
{
|
||||
/**
|
||||
* @param string $class_name
|
||||
* @param string $method_name
|
||||
*/
|
||||
public function __construct($class_name, $method_name){
|
||||
$this->class_name = $class_name;
|
||||
$this->method_name = $method_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置绑定
|
||||
* @param $params 绑定相关参数:[目标变量 , 源变量]
|
||||
* @param $method_info 方法变量信息 [变量名=>[是否引用, 是否有默认值, 默认值]]
|
||||
*/
|
||||
public function set($id, $params, $method_info){
|
||||
|
||||
Verify::isTrue(is_array($params) && count($params) >=2, "{$this->class_name}::{$this->method_name} invalid @throws");
|
||||
$exception = $params[0];
|
||||
$to = $params[1];
|
||||
$from = array_slice($params, 2);
|
||||
$to_func = &$this->params[$exception][$to][$id];
|
||||
foreach ($from as $index=>$name){ // 输入方法 $index变量序号 $name变量名
|
||||
if(is_array($name)){
|
||||
$is_const = true;
|
||||
}else{
|
||||
$is_const = (substr($name, 0, 1) !='$');
|
||||
}
|
||||
Verify::isTrue($is_const, "{$this->class_name}::{$this->method_name} dynamic variable not supported by @throws");
|
||||
$to_func[$index] = $name;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 绑定到函数调用的参数上去
|
||||
* @param $req
|
||||
* @param $res
|
||||
* @param $e 异常值
|
||||
*/
|
||||
public function bind($req, &$res, $e)
|
||||
{
|
||||
|
||||
if(!isset($this->params['body']) && !isset($this->params['res'])){
|
||||
$res['body'][0][0] = $e;
|
||||
}
|
||||
//先处理完全匹配的
|
||||
$matched = false;
|
||||
$funcs = array(); //输出方法
|
||||
foreach ($this->params as $exce_name => $calls){
|
||||
if(get_class($e) == $exce_name){
|
||||
$funcs[] = $calls;
|
||||
}
|
||||
}
|
||||
//没有完全匹配的异常, 尝试匹配父类
|
||||
if(count($funcs)===0){
|
||||
foreach ($this->params as $exce_name => $calls){
|
||||
if(is_a($e, $exce_name) ){
|
||||
$funcs[] = $calls;
|
||||
}
|
||||
}
|
||||
}
|
||||
// [
|
||||
// 'body'=>[
|
||||
// [0=>[arg1, arg2, arg3]],
|
||||
// ]
|
||||
// ]
|
||||
foreach ($funcs as $id=>$calls) {
|
||||
foreach ($calls as $fun_name => $call) {
|
||||
foreach ($call as $arg) {
|
||||
$res[$fun_name][$id] = $arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(count($funcs) ===0 ){
|
||||
throw $e;
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function isEmpty(){
|
||||
return count($this->params) ===0;
|
||||
}
|
||||
/**
|
||||
* 返回绑定的变量信息
|
||||
* @return array
|
||||
* [异常名=>[输出方法]=>[输出参数位置=>[值 , 参数信息],..]]
|
||||
*/
|
||||
public function getParams(){
|
||||
return $this->params;
|
||||
}
|
||||
private $params= array();// [异常=>[目标]=>[位置=>[值 , 参数信息],..]]
|
||||
private $class_name;
|
||||
private $method_name;
|
||||
}
|
||||
54
codes/agent/game/api/lib/phprs/Bootstrap.php
Normal file
54
codes/agent/game/api/lib/phprs/Bootstrap.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
namespace phprs;
|
||||
|
||||
use phprs\util\IoCFactory;
|
||||
use phprs\util\exceptions\NotFound;
|
||||
use phprs\util\exceptions\BadRequest;
|
||||
use phprs\util\exceptions\Forbidden;
|
||||
|
||||
class Bootstrap
|
||||
{
|
||||
static public function run($conf_file)
|
||||
{
|
||||
$err = null;
|
||||
$protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
|
||||
//执行请求
|
||||
try
|
||||
{
|
||||
require_once __DIR__ . '/AutoLoad.php';
|
||||
$factory = new IoCFactory($conf_file);
|
||||
$router = $factory->create('phprs\\RouterWithCache');
|
||||
//var_dump($router);
|
||||
$router();
|
||||
}
|
||||
catch (NotFound $e)
|
||||
{
|
||||
header($protocol . ' 404 Not Found');
|
||||
$err = $e;
|
||||
}
|
||||
catch (BadRequest $e)
|
||||
{
|
||||
header($protocol . ' 400 Bad Request');
|
||||
$err = $e;
|
||||
}
|
||||
catch (Forbidden $e)
|
||||
{
|
||||
header($protocol . ' 403 Forbidden');
|
||||
$err = $e;
|
||||
}
|
||||
catch (\Exception $e)
|
||||
{
|
||||
header($protocol . ' 500 Internal Server Error');
|
||||
$err = $e;
|
||||
}
|
||||
if ($err)
|
||||
{
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
$estr = array(
|
||||
'error' => get_class($err),
|
||||
'message' => $err->getMessage(),
|
||||
);
|
||||
echo json_encode($estr, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
}
|
||||
}
|
||||
153
codes/agent/game/api/lib/phprs/Container.php
Normal file
153
codes/agent/game/api/lib/phprs/Container.php
Normal file
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
/**
|
||||
* $Id: Container.php 58155 2015-01-05 14:45:30Z caoyangmin $
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief RestfulApiContainer
|
||||
*/
|
||||
namespace phprs;
|
||||
use phprs\util\AnnotationReader;
|
||||
use phprs\util\Verify;
|
||||
use phprs\util\Logger;
|
||||
|
||||
/**
|
||||
* restful api 容器
|
||||
* @author caoym
|
||||
*
|
||||
*/
|
||||
class Container{
|
||||
/**
|
||||
* @param string $class 类名
|
||||
* @param string $method 方法名, 如果为空, 则加载此类的所有方法
|
||||
*/
|
||||
function __construct($class, $method = null){
|
||||
$this->load($class, $method);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param string $class 类名
|
||||
* @param string $method ==null时load所有方法, !==null时load指定方法
|
||||
*/
|
||||
public function load($class, $method){
|
||||
$this->class = $class;
|
||||
//获取方法
|
||||
$reflection = new \ReflectionClass($class);
|
||||
$reader= new AnnotationReader($reflection);
|
||||
$class_ann = $reader->getClassAnnotations($reflection);
|
||||
Verify::isTrue(isset($class_ann['path']), $class.' @path not found');
|
||||
Verify::isTrue(count($class_ann['path'])===1, $class.' @path ambiguity');
|
||||
$path = $class_ann['path'][0]['value'];
|
||||
$this->path = $path;
|
||||
$specified = $method;
|
||||
foreach ($reflection->getMethods() as $method){
|
||||
if($specified !== null && $specified !== $method->getName()){
|
||||
Logger::DEBUG("specified method: $specified, ignore $class::{$method->getName()}");
|
||||
continue;
|
||||
}
|
||||
$anns = $reader->getMethodAnnotations($method, false);
|
||||
if(!isset($anns['route'])){
|
||||
Logger::DEBUG("no @route, ignore $class::{$method->getName()}");
|
||||
continue;
|
||||
}
|
||||
//Verify::isTrue(count($anns['route']) == 1, "$class::{$method->getName()} @route repeated set");
|
||||
$invoker = $this->factory->create('phprs\Invoker', array($this, $method) );
|
||||
foreach ($anns['route'] as $ann){
|
||||
$route = $ann['value'];
|
||||
Verify::isTrue(is_array($route) && (count($route)==2 || count($route)==3),
|
||||
"$class::{$method->getName()} syntax error @route, example: @route({\"GET\" ,\"/api?a=2\"}) or @route({\"GET\" ,\"/api?a=2\",true})"
|
||||
);
|
||||
list($http_method, $uri,$strict) = $route+[null,null,null];
|
||||
$this->routes[$http_method][] = [$path.'/'.$uri, $invoker,$strict];
|
||||
}
|
||||
|
||||
foreach ($anns as $type =>$v){
|
||||
if($type == 'route'){
|
||||
continue;
|
||||
}
|
||||
$id = 0;
|
||||
foreach ($v as $ann){
|
||||
if(!is_array($ann) || !isset($ann['value'])) {
|
||||
continue;
|
||||
}
|
||||
$invoker->bind($id++, $type, $ann['value']);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//检查是否所有必须的参数均已绑定
|
||||
$invoker->check();
|
||||
}
|
||||
//属性注入
|
||||
/*foreach ($reflection->getProperties() as $property ){
|
||||
foreach ( $reader->getPropertyAnnotations($property) as $id => $ann){
|
||||
if($id !== 'inject') { continue;}
|
||||
$name = $property->getName();
|
||||
if($name == "ioc_factory"){// ioc_factory由工厂负责注入
|
||||
//TODO: 用@ioc_factory替代ioc_factory
|
||||
continue;
|
||||
}
|
||||
Verify::isTrue(count($ann) ===1, "$class::$name ambiguity @inject");
|
||||
Verify::isTrue(isset($ann[0]['value']), "$class::$name invalid @inject");
|
||||
Verify::isTrue(is_string($ann[0]['value']), "$class::$name invalid @inject");
|
||||
$this->injectors[] = new Injector($this, $name, $ann[0]['value']);
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
}
|
||||
/**
|
||||
* 获取API实现类的实例
|
||||
* @param Request $request
|
||||
* @return object
|
||||
*/
|
||||
public function getImpl($request){
|
||||
Verify::isTrue($request !== null);
|
||||
if($this->impl === null){
|
||||
$injected = &$this->injected;
|
||||
$injected = array();
|
||||
$this->impl = $this->factory->create($this->class, null, null, function($src, &$succeeded)use($request, &$injected){
|
||||
list($val, $found) = $request->find($src);
|
||||
$succeeded = $found;
|
||||
$injected[$src]=$val;
|
||||
return $val;
|
||||
});
|
||||
asort($injected);
|
||||
}
|
||||
return $this->impl;
|
||||
}
|
||||
/**
|
||||
* 获取实例被注入的方法
|
||||
* 只有实例被创建后才能取到值
|
||||
* @return array
|
||||
*/
|
||||
public function getInjected(){
|
||||
return $this->injected;
|
||||
}
|
||||
///**
|
||||
// * 从http请求中提取属性
|
||||
// * @param RestfulApiRequest $request http请求
|
||||
// */
|
||||
//public function inject($request){
|
||||
// foreach ($this->injectors as $inject){
|
||||
// $inject($request);
|
||||
// }
|
||||
//}
|
||||
///**
|
||||
// * 获取注入的依赖
|
||||
// * @return arra:
|
||||
// */
|
||||
//public function getInjectors(){
|
||||
// return $this->injectors;
|
||||
//}
|
||||
//每次处理请求时属性被重新注入到API实例
|
||||
//private $injectors=array();
|
||||
public $routes=array(); //['GET'=>[Invoker,Invoker,Invoker...],'POST',....];
|
||||
//API 实现类;
|
||||
public $class;
|
||||
//API 实例;
|
||||
private $impl;
|
||||
private $injected;// 被注入的属性, 记录下来, 可以作为缓存key的一部分
|
||||
/** @inject("ioc_factory") */
|
||||
public $factory;
|
||||
|
||||
public $path;
|
||||
}
|
||||
251
codes/agent/game/api/lib/phprs/Invoker.php
Normal file
251
codes/agent/game/api/lib/phprs/Invoker.php
Normal file
@@ -0,0 +1,251 @@
|
||||
<?php
|
||||
/**
|
||||
* $Id: Invoker.php 63816 2015-05-15 11:35:31Z caoyangmin $
|
||||
*
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief
|
||||
*/
|
||||
namespace phprs;
|
||||
|
||||
use phprs\util\Verify;
|
||||
use phprs\util\Logger;
|
||||
use phprs\util\exceptions\BadRequest;
|
||||
use phprs\util\CheckableCache;
|
||||
|
||||
/**
|
||||
* api调用包装
|
||||
* 从请求中提取api所需的参数, 调用API, 并将结果输出到Response对象
|
||||
*
|
||||
* @author caoym
|
||||
*
|
||||
*/
|
||||
class Invoker
|
||||
{
|
||||
/**
|
||||
* @param Container $ins
|
||||
* 被调用的实例容器
|
||||
* @param string $method
|
||||
* 被调用的实例方法
|
||||
*/
|
||||
public function __construct($ins, $method)
|
||||
{
|
||||
$this->ins = $ins;
|
||||
if($this->cache === null){
|
||||
$this->checkAbleCache = $this->factory->create('phprs\util\Cache');
|
||||
}else{
|
||||
$this->checkAbleCache = new CheckableCache($this->cache);
|
||||
}
|
||||
|
||||
$this->method_name = $method->getName();
|
||||
foreach ($method->getParameters() as $param) {
|
||||
$this->method_args[] = array(
|
||||
$param->getName(), // name
|
||||
$param->isPassedByReference(), // ref
|
||||
$param->isOptional(), // isOptional
|
||||
$param->isOptional() ? $param->getDefaultValue() : null,
|
||||
) // default
|
||||
;
|
||||
}
|
||||
$this->bind = array(
|
||||
'param' => new BindParams($this->ins->class, $this->method_name),
|
||||
'return' => new BindReturns($this->ins->class, $this->method_name),
|
||||
'throws' => new BindThrows($this->ins->class, $this->method_name),
|
||||
'cache' => new BindReturns($this->ins->class, $this->method_name, false),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定参数,返回值,异常
|
||||
* @param int $id
|
||||
* 参数id
|
||||
* @param string $type 绑定类型 param/return/throws
|
||||
* @param $param
|
||||
* param:
|
||||
* [['arg_name'=>'$._SERVER.REQUEST_URI'],...]
|
||||
* [[参数名=>jsonpath表达式],...]
|
||||
* 指定api接口参数从http请求中获取的方式
|
||||
* returns:
|
||||
* 指定哪些参数被作为返回值
|
||||
* [
|
||||
* ['status','200 OK'], 200 OK 作为常量, 输出到status
|
||||
* ['cookie','$arg0'], $arg0变量输出到cookie
|
||||
* ['body'], 未指定变量, 则取返回值输出
|
||||
* ]
|
||||
* throws 指定根据不同的异常设置响应信息
|
||||
* [
|
||||
* ['MyException','status' , '404 Not Found'],
|
||||
* ['MyException','body','$msg'],
|
||||
* ['Exception','status','500 Internal Server Error'],
|
||||
* ]
|
||||
*/
|
||||
public function bind($id, $type, $param)
|
||||
{
|
||||
if (! isset($this->bind[$type])) {
|
||||
return;
|
||||
}
|
||||
$this->bind[$type]->set($id, $param, $this->method_args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行API
|
||||
*
|
||||
* @param Request $request 请求
|
||||
* @param Response $response 响应
|
||||
*/
|
||||
public function __invoke($request, &$response)
|
||||
{
|
||||
$args = array();
|
||||
//绑定参数和返回值
|
||||
$this->bind['param']->bind($request, $response, $args);
|
||||
$this->bind['return']->bind($request, $response, $args);
|
||||
//利用参数绑定的能力,提取@cache注释的信息
|
||||
$cache_ttl = 0;
|
||||
$cache_check = null;
|
||||
$cache_res = new Response(array(
|
||||
'ttl' => function ($param) use(&$cache_ttl)
|
||||
{
|
||||
$cache_ttl = $param;
|
||||
},
|
||||
'check' => function ($param) use(&$cache_check)
|
||||
{
|
||||
$cache_check = $param;
|
||||
},
|
||||
'body'=>function ($_=null){}
|
||||
));
|
||||
$this->bind['cache']->bind($request, $cache_res, $args);
|
||||
|
||||
$use_cache = !$this->bind['cache']->isEmpty();
|
||||
|
||||
$given = count($args);
|
||||
if ($given === 0) {
|
||||
$required_num = 0;
|
||||
} else {
|
||||
ksort($args, SORT_NUMERIC);
|
||||
end($args);
|
||||
$required_num = key($args) + 1;
|
||||
}
|
||||
|
||||
Verify::isTrue($given === $required_num, new BadRequest("{$this->ins->class}::{$this->method_name} $required_num params required, $given given")); // 变量没给全
|
||||
$cache_key = null;
|
||||
if ($use_cache) {
|
||||
//输入参数包括函数参数和类注入数据两部分
|
||||
//所以以这些参数的摘要作为缓存的key
|
||||
$injected = $this->ins->getInjected();
|
||||
$cache_res->flush();//取出cache参数
|
||||
$cache_key = "invoke_{$this->ins->class}_{$this->method_name}_" . sha1(serialize($args).serialize($injected).$cache_ttl);
|
||||
$succeeded = false;
|
||||
$data = $this->checkAbleCache->get($cache_key, $succeeded);
|
||||
if ($succeeded && is_array($data)) {
|
||||
$response->setBuffer($data);
|
||||
$response->flush();
|
||||
Logger::info("{$this->ins->class}::{$this->method_name} get response from cache $cache_key");
|
||||
return;
|
||||
}
|
||||
}
|
||||
$impl = $this->ins->getImpl($request);
|
||||
//
|
||||
if (!$this->bind['throws']->isEmpty()) {
|
||||
try {
|
||||
$res = call_user_func_array(array(
|
||||
$impl,
|
||||
$this->method_name,
|
||||
), $args);
|
||||
} catch (\Exception $e) {
|
||||
$response->clear(); // 清除之前绑定的变量, 异常发生时可能已经写入了一些参数
|
||||
$this->bind['throws']->bind($request, $response, $e);
|
||||
$response['break'][][0]=true;
|
||||
$response->flush();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
$res = call_user_func_array(array(
|
||||
$impl,
|
||||
$this->method_name,
|
||||
), $args);
|
||||
}
|
||||
$this->bind['return']->setReturn($res);
|
||||
if ($use_cache) {
|
||||
$this->checkAbleCache->set($cache_key, $response->getBuffer(), $cache_ttl, $cache_check);
|
||||
Logger::info("{$this->ins->class}::{$this->method_name} set response to cache $cache_key, ttl=$cache_ttl, check=".($cache_check===null?'null':get_class($cache_check)));
|
||||
}
|
||||
$response->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取被绑定的参数列表
|
||||
* 返回的是参数位置
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getBindParamPos()
|
||||
{
|
||||
return array_merge($this->bind['return']->getBindParamPos(), $this->bind['param']->getBindParamPos(), $this->bind['cache']->getBindParamPos());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查参数是否均已经绑定
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
$params = $this->getBindParamPos();
|
||||
foreach ($this->method_args as $id => $arg) {
|
||||
list ($name, $is_ref, $is_optional, $default) = $arg;
|
||||
if (false === array_search($id, $params)) {
|
||||
Verify::isTrue($is_optional, "{$this->ins->class}::{$this->method_name} param: $name not be bound");
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getMethodName(){
|
||||
return $this->method_name;
|
||||
}
|
||||
/**
|
||||
* 绑定的参数
|
||||
*/
|
||||
public function getParams(){
|
||||
return $this->bind['param'];
|
||||
}
|
||||
/**
|
||||
* 绑定的返回值
|
||||
*/
|
||||
public function getReturns(){
|
||||
return $this->bind['return'];
|
||||
}
|
||||
/**
|
||||
* 绑定的异常
|
||||
*/
|
||||
public function getThrows(){
|
||||
return $this->bind['throws'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getClassName(){
|
||||
return $this->ins->class;
|
||||
}
|
||||
/**
|
||||
* @return object
|
||||
*/
|
||||
public function getContainer(){
|
||||
return $this->ins;
|
||||
}
|
||||
public $method_name;
|
||||
|
||||
private $method_args = array();
|
||||
|
||||
private $ins;
|
||||
// 绑定的变量
|
||||
private $bind = array();
|
||||
|
||||
/**
|
||||
* @property
|
||||
* @var phprs\util\KVCatchInterface
|
||||
*/
|
||||
private $cache=null;
|
||||
private $checkAbleCache;
|
||||
/** @inject("ioc_factory") */
|
||||
private $factory;
|
||||
}
|
||||
154
codes/agent/game/api/lib/phprs/Request.php
Normal file
154
codes/agent/game/api/lib/phprs/Request.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
/**
|
||||
* $Id: Request.php 63900 2015-05-19 07:09:58Z caoyangmin $
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief Request
|
||||
*/
|
||||
namespace phprs;
|
||||
use phprs\util\Verify;
|
||||
use Peekmo\JsonPath\JsonStore;
|
||||
use phprs\util\exceptions\BadRequest;
|
||||
/**
|
||||
* @author caoym
|
||||
* http请求包装
|
||||
* 允许使用jsonpath表达式获取http请求信息
|
||||
* 如
|
||||
* req['$._GET.param1']
|
||||
*
|
||||
* TODO: 使用更友好的变量名替代直接用php的全局变量, 如$.uri.query 替代$._GET, $.body 替代$._POST
|
||||
*/
|
||||
class Request implements \ArrayAccess
|
||||
{
|
||||
//TODO: 在API实现中直接使用$_GET等全局变量是不推荐的, 应该给予警告提醒
|
||||
/**
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
function __construct($data=null,$url_begin=0){
|
||||
if($data === null){
|
||||
$data = $GLOBALS;
|
||||
$data['header'] = $this->getAllHeaders();
|
||||
}
|
||||
$this->url_begin = $url_begin;
|
||||
//支持json请求(Content-Type: application/json)
|
||||
$contentType = null;
|
||||
if(isset($data['header']['Content-Type'])){
|
||||
$contentType = $data['header']['Content-Type'];
|
||||
list($contentType, ) = explode(';', $contentType)+array(null,null);
|
||||
if($contentType == 'application/json'){
|
||||
$post = file_get_contents('php://input');
|
||||
if($post != ''){
|
||||
$post = json_decode($post, true);
|
||||
Verify::isTrue(is_array($post), new BadRequest('post unjson data with application/json type'));
|
||||
$data['_POST'] = $post;
|
||||
if(!isset($data['_REQUEST'])){
|
||||
$data['_REQUEST'] = [];
|
||||
}
|
||||
$data['_REQUEST'] = array_merge($data['_POST'], $data['_REQUEST'] );
|
||||
}
|
||||
}
|
||||
elseif($contentType == 'text/plain'){
|
||||
$data['_POST'] = file_get_contents('php://input');
|
||||
}
|
||||
}
|
||||
//TODO: 支持put请求
|
||||
if(isset($data['_SERVER']['REQUEST_METHOD']) && 'PUT' == $data['_SERVER']['REQUEST_METHOD']){
|
||||
if($contentType == 'application/x-www-form-urlencoded'){
|
||||
$queryString = file_get_contents('php://input');
|
||||
$query = array();
|
||||
parse_str($queryString, $query);
|
||||
$data['_POST'] = $query;
|
||||
}
|
||||
}
|
||||
|
||||
$full_path = $data['_SERVER']['REQUEST_URI'];
|
||||
Verify::isTrue($full_path, '$._SERVER.REQUEST_URI not found' );
|
||||
list($full_path,) = explode('?', $full_path);
|
||||
|
||||
$paths = explode('/', $full_path);
|
||||
$paths = array_filter($paths,function ($i){return $i !== '';});
|
||||
$paths = array_slice($paths, $this->url_begin);
|
||||
$data['path'] = $paths;
|
||||
$this->data = new JsonStore($data);
|
||||
}
|
||||
/**
|
||||
* 获取http请求的所有header信息
|
||||
* @return array
|
||||
*/
|
||||
function getAllHeaders() {
|
||||
$headers = array();
|
||||
foreach ($_SERVER as $name => $value)
|
||||
{
|
||||
if (substr($name, 0, 5) == 'HTTP_')
|
||||
{
|
||||
$name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))));
|
||||
$headers[$name] = $value;
|
||||
} else if ($name == "CONTENT_TYPE") {
|
||||
$headers["Content-Type"] = $value;
|
||||
} else if ($name == "CONTENT_LENGTH") {
|
||||
$headers["Content-Length"] = $value;
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
* @see ArrayAccess::offsetExists()
|
||||
*/
|
||||
public function offsetExists($offset){
|
||||
return count($this->data->get($offset)) !==0;
|
||||
}
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
* @see ArrayAccess::offsetGet()
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
$res = $this->data->get($offset);
|
||||
if(!isset($res[0])){
|
||||
trigger_error("$offset not exist");
|
||||
}
|
||||
return $res[0];
|
||||
}
|
||||
/**
|
||||
* @param string $expr jsonpath 表达式
|
||||
* @param $create 是否找不到就创建
|
||||
* @return [found, is succeed]
|
||||
*/
|
||||
public function find($expr, $create=false){
|
||||
$res= $this->data->get($expr, false, $create);
|
||||
if(count($res) ===0 ){
|
||||
return array(null,false);
|
||||
}else if(count($res) ===1){
|
||||
return array(&$res[0], true);
|
||||
}else{
|
||||
return array(&$res, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
* @see ArrayAccess::offsetSet()
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
Verify::isTrue($this->data->set($offset, $value));
|
||||
}
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
* @see ArrayAccess::offsetUnset()
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
Verify::isTrue(false, 'NOT IMPL');
|
||||
}
|
||||
/**
|
||||
* 取数包含所有请求信息的数组
|
||||
* @return multitype:
|
||||
*/
|
||||
public function toArray(){
|
||||
return $this->data->toArray();
|
||||
}
|
||||
private $data;
|
||||
private $url_begin;
|
||||
}
|
||||
188
codes/agent/game/api/lib/phprs/Response.php
Normal file
188
codes/agent/game/api/lib/phprs/Response.php
Normal file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: Response.php 58820 2015-01-16 16:29:33Z caoyangmin $
|
||||
*
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief Request
|
||||
*/
|
||||
namespace phprs;
|
||||
|
||||
use phprs\util\Verify;
|
||||
|
||||
/**
|
||||
* http响应包装
|
||||
* 保存http响应, 并通过sender写出
|
||||
* 并可以数组的方式设置数据
|
||||
* 如
|
||||
* $res['status'][]='200 OK'
|
||||
* $res['body'][]='...'
|
||||
*
|
||||
* @author caoym
|
||||
*/
|
||||
class Response implements \ArrayAccess
|
||||
{
|
||||
/**
|
||||
* 创建响应
|
||||
* @param array $sender 数据发送方法
|
||||
*/
|
||||
function __construct($sender = null)
|
||||
{
|
||||
if ($sender !== null) {
|
||||
$this->sender = $sender;
|
||||
}
|
||||
if ($this->sender === null) {
|
||||
// TODO 严格检查方法的参数数量
|
||||
// 输出时按照下面数组中的顺序输出
|
||||
$this->sender = array(
|
||||
'header' => function ($_=null)
|
||||
{
|
||||
call_user_func_array('header', func_get_args());
|
||||
},
|
||||
'status' => function ($var, $replace = true)
|
||||
{
|
||||
header($_SERVER["SERVER_PROTOCOL"] . ' '.$var, $replace);
|
||||
},
|
||||
'cookie' =>function ($name, $value, $expire=null, $path='/', $domain=null, $secure=null){
|
||||
if(is_string($expire)){
|
||||
$expire = strtotime($expire);
|
||||
}
|
||||
setcookie($name, $value, $expire, $path, $domain, $secure);
|
||||
},
|
||||
'body' => function ($var)
|
||||
{
|
||||
if (is_array($var) || is_object($var)) {
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
echo json_encode($var); // TODO 自定义适配方法
|
||||
} else {
|
||||
echo $var;
|
||||
}
|
||||
},
|
||||
'break'=>function($_=null){
|
||||
//do nothing
|
||||
},
|
||||
'res'=>function ($status, $body){
|
||||
header($_SERVER["SERVER_PROTOCOL"] . ' '.$status, true);
|
||||
if (is_array($body)) {
|
||||
header("Content-Type: application/json; charset=UTF-8");
|
||||
echo json_encode($body); // TODO 自定义适配方法
|
||||
} else {
|
||||
echo $body;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
*
|
||||
* @see ArrayAccess::offsetExists()
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return isset($this->sender[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过[]操作符设置输出数据
|
||||
*/
|
||||
public function &offsetGet($offset)
|
||||
{
|
||||
Verify::isTrue($this->offsetExists($offset), 'unsupported response ' . $offset);
|
||||
if (! isset($this->buffer[$offset])) {
|
||||
$this->buffer[$offset] = array();
|
||||
}
|
||||
return $this->buffer[$offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
*
|
||||
* @see ArrayAccess::offsetSet()
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
Verify::isTrue(false, 'NOT IMPL');
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
*
|
||||
* @see ArrayAccess::offsetUnset()
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
Verify::isTrue(false, 'NOT IMPL');
|
||||
}
|
||||
/**
|
||||
* 清除缓存
|
||||
* @return void
|
||||
*/
|
||||
public function clear(){
|
||||
$this->buffer = array();
|
||||
}
|
||||
/**
|
||||
* 想缓存写出
|
||||
* @param $limit 取指定的项目
|
||||
* @param $func 取出后调用的方法
|
||||
* @return array:
|
||||
*/
|
||||
public function flush($limit=null, $func=null)
|
||||
{
|
||||
foreach ($this->sender as $name=>$sender){
|
||||
if (!isset($this->buffer[$name])){
|
||||
continue;
|
||||
}
|
||||
if($limit !==null ){
|
||||
if($limit !== $name){
|
||||
continue;
|
||||
}
|
||||
if($func!==null){
|
||||
$sender = $func;
|
||||
}
|
||||
}
|
||||
$funcs = $this->buffer[$name];
|
||||
foreach ($funcs as $args) {
|
||||
// 确保所有参数均已设置
|
||||
ksort($args, SORT_NUMERIC);
|
||||
$i = 0;
|
||||
foreach ($args as $k => $v) {
|
||||
Verify::isTrue($k === $i ++, "the no.$i arg from $name not exist");
|
||||
}
|
||||
call_user_func_array($sender, $args);
|
||||
}
|
||||
if($limit !==null){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 附加更多数据
|
||||
* @param array $buffer
|
||||
*/
|
||||
public function append($buffer){
|
||||
foreach ($buffer as $name => $funcs) {
|
||||
foreach ($funcs as $func){
|
||||
$this->buffer[$name][]=$func;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return multitype:
|
||||
*/
|
||||
public function getBuffer(){
|
||||
return $this->buffer;
|
||||
}
|
||||
/**
|
||||
* @param unknown $buffer
|
||||
*/
|
||||
public function setBuffer($buffer){
|
||||
$this->buffer = $buffer;
|
||||
}
|
||||
private $buffer = array();
|
||||
|
||||
private $sender;
|
||||
}
|
||||
|
||||
346
codes/agent/game/api/lib/phprs/Router.php
Normal file
346
codes/agent/game/api/lib/phprs/Router.php
Normal file
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: Router.php 58820 2015-01-16 16:29:33Z caoyangmin $
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief Router
|
||||
*/
|
||||
namespace phprs;
|
||||
use phprs\util\HttpRouterEntries;
|
||||
use phprs\util\Verify;
|
||||
use phprs\util\AutoClassLoader;
|
||||
use phprs\util\Logger;
|
||||
use phprs\util\exceptions\NotFound;
|
||||
use phprs\util\ClassLoader;
|
||||
|
||||
/**
|
||||
* 结果保留在内存
|
||||
* @author caoym
|
||||
*/
|
||||
class BufferedRespond extends Response{
|
||||
/**
|
||||
* flush 不直接输出
|
||||
* @see \phprs\Response::flush()
|
||||
* @param string limit 现在输出的项目
|
||||
* @param callable $func 输出方法
|
||||
* @return void
|
||||
*/
|
||||
public function flush($limit=null, $func=null)
|
||||
{
|
||||
return ;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* restful api 路由
|
||||
* 加载API类文件, 通过API类的@annotation获取路由规则信息, 当请求到来时, 调用匹配的
|
||||
* API类方法.
|
||||
*
|
||||
* 通常每一个请求只对应到一个最严格匹配的API接口, 所谓"最严格匹配",比如:
|
||||
* API1 接口 => url: /apis/test
|
||||
* API2 接口 => url: /apis
|
||||
* 那么当请求"/apis/test/123/" 最严格匹配的接口是API1
|
||||
*
|
||||
* 如果需要让一个请求经过多个API调用, 比如有时候会需要一个统一验证的接口, 让所有请
|
||||
* 求先通过验证接口, 再调用其他接口. 此时可以通过Router的hooks属性, 设置一组hook实
|
||||
* 现. hook其实和普通的接口一样, 只是在hooks中指定后, 执行行为将有所不同: 请求会按
|
||||
* 优先级逐个经过hook, 只要匹配, hook的方法就会被调用, 直到最后调用普通的API
|
||||
*
|
||||
* 通过@return({"break", true})停止请求链路
|
||||
*
|
||||
* @author caoym
|
||||
*/
|
||||
class Router
|
||||
{
|
||||
private $class_loader; //用于确保反序列化时自动加载类文件
|
||||
/**
|
||||
*
|
||||
*/
|
||||
function __construct( ){
|
||||
//AutoClassLoader确保序列化后自动加载类文件
|
||||
$this->class_loader = new AutoClassLoader();
|
||||
ClassLoader::addInclude($this->api_path);
|
||||
//TODO: 支持名字空间
|
||||
//TODO: 支持多路径多类名
|
||||
$this->load($this->api_path, $this->apis, $this->api_method);
|
||||
//允许通过接口访问api信息
|
||||
if($this->export_apis){
|
||||
$this->loadApi($this->routes, __DIR__.'/apis/ApiExporter.php', 'phprs\apis\ApiExporter');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取api文件列表
|
||||
* @return array
|
||||
*/
|
||||
public function getApiFiles(){
|
||||
return $this->class_loader->getClasses();
|
||||
}
|
||||
/**
|
||||
* 调用路由规则匹配的api
|
||||
* @param Request $request
|
||||
* @param Response $respond
|
||||
* @return void
|
||||
*/
|
||||
public function __invoke($request=null, &$respond=null){
|
||||
if($request === null){
|
||||
$request = new Request(null,$this->url_begin);
|
||||
}
|
||||
if($respond==null){
|
||||
$respond = new Response();
|
||||
}
|
||||
$request['$.router'] = $this;
|
||||
//先按配置的顺序调用hook
|
||||
foreach ($this->hook_routes as $hook){
|
||||
$res = new BufferedRespond();
|
||||
if(!$this->invokeRoute($hook, $request, $res)){
|
||||
continue;
|
||||
}
|
||||
$respond->append($res->getBuffer());
|
||||
$break = false;
|
||||
$respond->flush('break', function($var)use(&$break){$break = $var;});
|
||||
if($break){
|
||||
Logger::info("invoke break");
|
||||
$respond->flush();
|
||||
return;
|
||||
}
|
||||
}
|
||||
$res = new BufferedRespond();
|
||||
|
||||
Verify::isTrue($this->invokeRoute($this->routes, $request, $res), new NotFound());
|
||||
$respond->append($res->getBuffer());
|
||||
$respond->flush();
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param string|array $api_path
|
||||
* @param string $apis
|
||||
* @param string $api_method
|
||||
*/
|
||||
public function load($api_path, $apis=null , $api_method=null){
|
||||
if(is_string($api_path)){
|
||||
$api_paths = array($api_path);
|
||||
}else{
|
||||
$api_paths = $api_path;
|
||||
}
|
||||
Verify::isTrue(is_array($api_paths), 'invalid param');
|
||||
|
||||
foreach ($api_paths as $api_path){
|
||||
$this->loadRoutes($this->routes, $api_path, $apis, $api_method);
|
||||
foreach ($this->hooks as $hook) {
|
||||
$hook_route=array();
|
||||
$this->loadRoutes($hook_route, $api_path.'/hooks', $hook, null);
|
||||
$this->hook_routes[] = $hook_route;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getRoutes(){
|
||||
return $this->routes;
|
||||
}
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getHooks(){
|
||||
return $this->hook_routes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用路由规则匹配的api
|
||||
* @param array $routes 路由规则
|
||||
* @param unknown $request
|
||||
* @param unknown $respond
|
||||
* @return boolean 是否有匹配的接口被调用
|
||||
*/
|
||||
private function invokeRoute($routes, $request, &$respond){
|
||||
$method = $request['$._SERVER.REQUEST_METHOD'];
|
||||
$path = $request['$.path'];
|
||||
$uri = $request['$._SERVER.REQUEST_URI'];
|
||||
list(,$params) = explode('?', $uri)+array( null,null );
|
||||
$params = is_null($params)?null:explode('&', $params);
|
||||
|
||||
Logger::debug("try to find route $method ".$uri);
|
||||
$match_path = array();
|
||||
if(isset($routes[$method])){
|
||||
if(($api = $routes[$method]->findByArray($path,$params,$match_path)) !== null){
|
||||
Logger::debug("invoke $uri => {$api->getClassName()}::{$api->getMethodName()}");
|
||||
$api($request, $respond);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!isset($routes['*'])){
|
||||
return false;
|
||||
}
|
||||
if(($api = $routes['*']->find($uri, $match_path)) === null){
|
||||
return false;
|
||||
}
|
||||
Logger::debug("invoke $uri => {$api->getClassName()}::{$api->getMethodName()}");
|
||||
$api($request, $respond);
|
||||
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* @param string $apis_dir
|
||||
* @param string $class
|
||||
* @param string $method
|
||||
* @return void
|
||||
*/
|
||||
public function addRoutes($apis_dir, $class=null, $method=null){
|
||||
$this->loadRoutes($this->routes, $apis_dir, $class, $method);
|
||||
}
|
||||
/**
|
||||
* 遍历API目录生成路由规则
|
||||
* @param object $factory
|
||||
* @param string $apis_dir
|
||||
* @param string $class
|
||||
* @param string $method
|
||||
* @param array $skipclass 需要跳过的类名
|
||||
* @return Router
|
||||
*/
|
||||
private function loadRoutes(&$routes, $apis_dir, $class, $method){
|
||||
Logger::info("attempt to load router $apis_dir");
|
||||
$dir = null;
|
||||
|
||||
if(is_dir($apis_dir) && $class === null){
|
||||
$apis_dir=$apis_dir.'/';
|
||||
Verify::isTrue(is_dir($apis_dir), "$apis_dir not a dir");
|
||||
$dir = @dir($apis_dir);
|
||||
Verify::isTrue($dir !== null, "open dir $apis_dir failed");
|
||||
$geteach = function ()use($dir){
|
||||
$name = $dir->read();
|
||||
if(!$name){
|
||||
return $name;
|
||||
}
|
||||
return $name;
|
||||
};
|
||||
}else{
|
||||
if(is_file($apis_dir)){
|
||||
$files = array($apis_dir);
|
||||
$apis_dir = '';
|
||||
}else{
|
||||
$apis_dir=$apis_dir.'/';
|
||||
if(is_array($class)){
|
||||
foreach ($class as &$v){
|
||||
$v .= '.php';
|
||||
}
|
||||
$files = $class;
|
||||
}else{
|
||||
$files = array($class.'.php');
|
||||
}
|
||||
}
|
||||
$geteach = function ()use(&$files){
|
||||
// 🚨 PHP8兼容性:each()函数替换,严格保持原有行为
|
||||
// ⚠️ 重要:必须保持数组指针移动行为与each()完全一致
|
||||
// ⚠️ 重要:必须保持文件加载顺序绝对不变
|
||||
$key = key($files);
|
||||
if($key === null) {
|
||||
return false; // ⚠️ 保持与each()相同的结束判断逻辑
|
||||
}
|
||||
$value = current($files);
|
||||
next($files); // ⚠️ 关键:保持指针移动与each()完全一致
|
||||
return $value; // ⚠️ 保持返回值格式与each()[1]完全一致
|
||||
};
|
||||
}
|
||||
while( !!($entry = $geteach()) ){
|
||||
$path = $apis_dir. str_replace('\\', '/', $entry);
|
||||
if(is_file($path) && substr_compare ($entry, '.php', strlen($entry)-4,4,true) ==0){
|
||||
$class_name = substr($entry, 0, strlen($entry)-4);
|
||||
$this->loadApi($routes, $path, $class_name, $method);
|
||||
}else{
|
||||
Logger::debug($path.' ignored');
|
||||
}
|
||||
}
|
||||
if($dir !== null){
|
||||
$dir->close();
|
||||
}
|
||||
Logger::info("load router $apis_dir ok");
|
||||
return $routes;
|
||||
}
|
||||
/**
|
||||
* 加载api类
|
||||
* @param array $routes
|
||||
* @param string $class_file
|
||||
* @param string $class_name
|
||||
* @param string $method
|
||||
* @return void
|
||||
*/
|
||||
private function loadApi(&$routes, $class_file, $class_name, $method=null){
|
||||
Verify::isTrue(is_file($class_file), $class_file.' is not an exist file');
|
||||
Logger::debug("attempt to load api: $class_name, $class_file");
|
||||
|
||||
$this->class_loader->addClass($class_name, $class_file);
|
||||
$api = null;
|
||||
if ($this->ignore_load_error){
|
||||
try {
|
||||
$api = $this->factory->create('phprs\\Container', array($class_name, $method), null, null);
|
||||
} catch (\Exception $e) {
|
||||
Logger::warning("load api: $class_name, $class_file failed with ".$e->getMessage());
|
||||
return ;
|
||||
}
|
||||
}else{
|
||||
$api = $this->factory->create('phprs\\Container', array($class_name, $method), null, null);
|
||||
}
|
||||
|
||||
foreach ($api->routes as $http_method=>$route){
|
||||
if(!isset($routes[$http_method])){
|
||||
$routes[$http_method] = new HttpRouterEntries();
|
||||
}
|
||||
$cur = $routes[$http_method];
|
||||
foreach ($route as $entry){
|
||||
list($uri,$invoke,$strict) = $entry;
|
||||
$realpath = preg_replace('/\/+/', '/', '/'.$uri);
|
||||
$strict = ($strict===null)?$this->default_strict_matching:$strict;
|
||||
Verify::isTrue($cur->insert($realpath, $invoke, $strict), "repeated path $realpath");
|
||||
Logger::debug("api: $http_method $realpath => $class_name::{$entry[1]->method_name} ok, strict:$strict");
|
||||
}
|
||||
}
|
||||
Logger::debug("load api: $class_name, $class_file ok");
|
||||
}
|
||||
private $routes=array();
|
||||
private $hook_routes=array();
|
||||
|
||||
|
||||
/** @inject("ioc_factory") */
|
||||
public $factory;
|
||||
/** @property */
|
||||
private $api_path='apis';
|
||||
/** @property */
|
||||
private $apis=null;
|
||||
/** @property */
|
||||
private $api_method=null;
|
||||
|
||||
private $api_root=null;
|
||||
/** @property
|
||||
* 指定hook的类名, 从优先级高到低
|
||||
* 如果一条规则匹配多个hook, 则优先级高的先执行, 再执行优先级低的
|
||||
*/
|
||||
private $hooks=array();
|
||||
/**
|
||||
* @property
|
||||
* 是否允许通过接口获取api信息
|
||||
*/
|
||||
private $export_apis=true;
|
||||
/**
|
||||
* 用于匹配路由的url偏移
|
||||
* @property
|
||||
*/
|
||||
public $url_begin=0;
|
||||
|
||||
/**
|
||||
* 指定路由规则默认情况下是否严格匹配path,如果@route中已经指定严格模式,则忽略此默认设置
|
||||
* 严格模式将只允许同级目录匹配,否则父目录和子目录也匹配。
|
||||
* 非严格匹配时
|
||||
* 路由"GET /a" 和请求"GET /a"、"GET /a/b"、"GET /a/b/c"等匹配
|
||||
* 严格匹配时
|
||||
* 路由"GET /a" 和请求"GET /a"匹配、和"GET /a/b"、"GET /a/b/c"等不匹配
|
||||
* @property
|
||||
*/
|
||||
public $default_strict_matching=false;
|
||||
/**
|
||||
* @property 忽略类加载时的错误,只是跳过出错的接口。否则抛出异常。
|
||||
*/
|
||||
public $ignore_load_error=true;
|
||||
|
||||
}
|
||||
70
codes/agent/game/api/lib/phprs/RouterWithCache.php
Normal file
70
codes/agent/game/api/lib/phprs/RouterWithCache.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: RouterWithCache.php 57516 2014-12-23 05:44:20Z caoyangmin $
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief RouterCache
|
||||
*/
|
||||
|
||||
namespace phprs;
|
||||
use phprs\util\FileExpiredChecker;
|
||||
use phprs\util\Logger;
|
||||
/**
|
||||
* 支持缓存的Router
|
||||
* 初始化Router需要解析类和方法的注释, 通过@标记绑定接口的参数, 此过程非常耗时, 缓
|
||||
* 存可以将解析后的结果保留, 包括api容器, 调用参数的绑定顺序等, 避免此消耗.
|
||||
*
|
||||
* 由于Router初始化时并不会创建API实例, 而是在API被调用时才创建, 所以缓存不会保存
|
||||
* API实例, 此特性有助于简化API的设计, API会在每个请求中重新初始化
|
||||
* @author caoym
|
||||
*/
|
||||
class RouterWithCache
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
function __construct(){
|
||||
|
||||
$ok=false;
|
||||
if($this->factory->getConfFile() ===null){
|
||||
$key = 'phprs_route3_'.sha1(serialize($this->factory->getConf()));
|
||||
}else{
|
||||
$key = 'phprs_route3_'.sha1($this->factory->getConfFile());
|
||||
}
|
||||
$this->impl = $this->cache->get($key, $ok);
|
||||
if($ok && is_object($this->impl)){
|
||||
Logger::info("router loaded from cache");
|
||||
return ;
|
||||
}
|
||||
$this->impl = $this->factory->create('phprs\\Router');
|
||||
//缓存过期判断依据
|
||||
//检查接口文件是否有修改\新增
|
||||
$check_files = array_values($this->impl->getApiFiles());
|
||||
$check_dirs=array();
|
||||
foreach($check_files as $file){
|
||||
if(is_file($file)){
|
||||
$check_dirs[] = dirname($file);
|
||||
}
|
||||
}
|
||||
$check_files = array_merge($check_files, $check_dirs);
|
||||
$check_files[]=$this->factory->getConfFile();
|
||||
|
||||
$this->cache->set($key, $this->impl, 0, new FileExpiredChecker($check_files)); //接口文件或者配置文件修改
|
||||
}
|
||||
/**
|
||||
* 调用路由规则匹配的api
|
||||
* @param Request $request
|
||||
* @param Response $respond
|
||||
* @return mixed
|
||||
*/
|
||||
function __invoke($request=null, &$respond=null){
|
||||
return $this->impl->__invoke($request, $respond);
|
||||
}
|
||||
|
||||
/** @property({"default":"@phprs\util\Cache"}) */
|
||||
private $cache;
|
||||
/** @inject("ioc_factory") */
|
||||
private $factory;
|
||||
|
||||
private $impl;
|
||||
}
|
||||
799
codes/agent/game/api/lib/phprs/apis/ApiExporter.php
Normal file
799
codes/agent/game/api/lib/phprs/apis/ApiExporter.php
Normal file
@@ -0,0 +1,799 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* $Id: ApiExporter.php 58155 2015-01-05 14:45:30Z caoyangmin $
|
||||
*
|
||||
* @author caoyangmin(caoyangmin@baidu.com)
|
||||
* @brief
|
||||
* ApiExporter
|
||||
*/
|
||||
namespace phprs\apis;
|
||||
|
||||
use phprs\Invoker;
|
||||
use phprs\util\AnnotationReader;
|
||||
use Peekmo\JsonPath\JsonStore;
|
||||
use phprs\util\HttpRouterEntries;
|
||||
use phprs\util\Verify;
|
||||
|
||||
/**
|
||||
* 导出AP描述信息
|
||||
* @author caoym
|
||||
* @path("/")
|
||||
*/
|
||||
class ApiExporter
|
||||
{
|
||||
/**
|
||||
* 导出API信息
|
||||
* @route({"GET", "/apis.json"})
|
||||
* @return({"header", "Content-Type: application/json; charset=UTF-8"})
|
||||
* @return({"body"}) array
|
||||
*/
|
||||
public function exportJson()
|
||||
{
|
||||
$apis = array();
|
||||
// 导出hook信息
|
||||
foreach ($this->router->getHooks() as $hooks) {
|
||||
foreach ($hooks as $method => $hook) {
|
||||
$entries = $hook->export();
|
||||
foreach ($entries as $entry) {
|
||||
list ($uri, $invoker) = $entry;
|
||||
$info['type'] = 'hook';
|
||||
$info['uri'] = array(
|
||||
$method,
|
||||
$uri
|
||||
);
|
||||
$info = array_merge($info, $this->getInvokerInfo($method,$uri, $invoker));
|
||||
$apis[$invoker->getClassName()]['apis'][] = $info;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 导出api信息
|
||||
foreach ($this->router->getRoutes() as $method => $route) {
|
||||
$entries = $route->export();
|
||||
foreach ($entries as $entry) {
|
||||
list ($uri, $invoker) = $entry;
|
||||
$info['type'] = 'api';
|
||||
$info['uri'] = array(
|
||||
$method,
|
||||
$uri,
|
||||
);
|
||||
$info = array_merge($info, $this->getInvokerInfo($method,$uri,$invoker));
|
||||
$apis[$invoker->getClassName()]['apis'][] = $info;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($apis as $class_name => &$info) {
|
||||
$ann = new \ReflectionClass($class_name);
|
||||
$apis[$class_name]['doc'] = $this->getDocText($ann->getDocComment());
|
||||
//排序, 便于阅读
|
||||
usort($info['apis'], function($lh, $rh){
|
||||
return strcmp($rh['uri'][1].$rh['uri'][0], $lh['uri'][1].$lh['uri'][0] );
|
||||
});
|
||||
}
|
||||
return $apis;
|
||||
}
|
||||
|
||||
private static $css = <<<EOT
|
||||
<style type="text/css">
|
||||
pre {border: 1px solid #bbb; padding: 10px;}
|
||||
</style>
|
||||
EOT;
|
||||
|
||||
/**
|
||||
* 导出API信息
|
||||
* 简陋的html形式
|
||||
*
|
||||
* @param Router $router
|
||||
* @route({"GET","/apis/",true})
|
||||
* @return ({"header", "Content-Type: text/html; charset=UTF-8"})
|
||||
*/
|
||||
public function exportMainHtml()
|
||||
{
|
||||
$info = $this->exportJson();
|
||||
|
||||
$body = '<html>';
|
||||
$body .= '<body>';
|
||||
$body = '<ol>';
|
||||
foreach ($info as $class_name => $apis) {
|
||||
$body .= "<li><a href=./" . str_replace('\\', '/', $class_name) . ">$class_name</a></li>";
|
||||
$body .= '<p>';
|
||||
$body .= nl2br(htmlentities($apis['doc']));
|
||||
$body .= '</p>';
|
||||
}
|
||||
$body .= '</ol>';
|
||||
$body .= '</body>';
|
||||
$body .= '</html>';
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出API信息
|
||||
* 简陋的html形式
|
||||
*
|
||||
* @route({"GET", "/apis/*"})
|
||||
*
|
||||
* @param({"class_name", "$.path[1:]"})
|
||||
* @return ({"header", "Content-Type: text/html; charset=UTF-8"})
|
||||
*/
|
||||
public function exportApiHtml($class_name)
|
||||
{
|
||||
if (is_array($class_name)) {
|
||||
$class_name = implode('\\', $class_name);
|
||||
}
|
||||
// TODO: html+js重写吧
|
||||
$body = self::$css;
|
||||
$body .= '<html>';
|
||||
$body .= '<body>';
|
||||
$info = $this->exportJson();
|
||||
$apis = $info[$class_name];
|
||||
// 类名
|
||||
$body .= '<h1>';
|
||||
$body .= htmlentities($class_name);
|
||||
$body .= '</h1>';
|
||||
|
||||
$body .= '<p>';
|
||||
$body .= nl2br(htmlentities($apis['doc']));
|
||||
$body .= '</p>';
|
||||
|
||||
$body .= '<ol>';
|
||||
// 接口
|
||||
foreach ($apis['apis'] as $api) {
|
||||
$body .= '<h2>';
|
||||
$body .= '<li>';
|
||||
$body .= htmlentities($api['uri'][0] . ' ' . $api['uri'][1]);
|
||||
$body .= '</li>';
|
||||
$body .= '</h2>';
|
||||
// 说明
|
||||
$body .= '<p>';
|
||||
$body .= nl2br(htmlentities($api['doc']));
|
||||
$body .= '</p>';
|
||||
|
||||
// 请求
|
||||
list ($sample, $doc) = $this->createRequestDoc($api);
|
||||
$body .= '<h3>>>Request</h3>';
|
||||
$body .= '<pre>';
|
||||
$body .= $sample;
|
||||
$body .= '</pre>';
|
||||
|
||||
$body .= '<p>';
|
||||
$body .= nl2br(htmlentities($doc));
|
||||
$body .= '</p>';
|
||||
|
||||
// 响应
|
||||
|
||||
list ($sample, $doc) = $this->createResponseDoc($api, $api['type'] ==='api');
|
||||
$body .= '<h3>>>Response(OK)</h3>';
|
||||
$body .= '<pre>';
|
||||
$body .= $sample;
|
||||
$body .= '</pre>';
|
||||
|
||||
$body .= '<p>';
|
||||
$body .= nl2br(htmlentities($doc));
|
||||
$body .= '</p>';
|
||||
|
||||
// 异常
|
||||
$fails = $this->createExceptionDoc($api);
|
||||
|
||||
foreach ($fails as $name => $info) {
|
||||
$body .= '<h3>>>Response (Fail: ' . $name . ')</h3>';
|
||||
$body .= '<pre>';
|
||||
$body .= $info[0];
|
||||
$body .= '</pre>';
|
||||
|
||||
$body .= '<p>';
|
||||
$body .= nl2br(htmlentities($info[1]));
|
||||
$body .= '</p>';
|
||||
}
|
||||
|
||||
$body .= '<h3>>>Response (Fail: unknown)</h3>';
|
||||
$body .= '<pre>';
|
||||
$body .= "HTTP/1.1 500 Internal Server Error\r\n\r\n";
|
||||
$body .= '</pre>';
|
||||
|
||||
}
|
||||
$body .= '</ol>';
|
||||
|
||||
$body .= '</body>';
|
||||
$body .= '</html>';
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成请求的示例和说明
|
||||
*
|
||||
* @param array $api
|
||||
* @return array [sample, doc]
|
||||
*/
|
||||
private function createRequestDoc($api)
|
||||
{
|
||||
//TODO: 需要处理特殊情况: 输入被绑定在多个参数, 或者输入的不同重叠区域被绑定到不同参数时
|
||||
$docs = '';
|
||||
// 提取参数
|
||||
$params = new JsonStore(array());
|
||||
foreach ($api['params'] as $name => $param) {
|
||||
$ori = $params->get($param['value']);
|
||||
if (count($ori) !== 0) { // 现在不支持同一个变量多个地方引用
|
||||
continue;
|
||||
}
|
||||
$info = new \ArrayObject(array(
|
||||
$name,
|
||||
$param,
|
||||
));
|
||||
$params->set($param['value'], $info);
|
||||
}
|
||||
$params = $params->toArray();
|
||||
// 路由中指定的路径
|
||||
$route_path = HttpRouterEntries::stringToPath($api['uri'][1]); // 这是绝对路径
|
||||
$path = $api['uri'][0];
|
||||
// 路径拼到示例中
|
||||
if (isset($params['path'])) {
|
||||
$req_path = $params['path']; // 请求中使用的路径, 这是相对路径
|
||||
$offest = count(HttpRouterEntries::stringToPath($api['root'])); // 相对于绝对路径的偏移
|
||||
|
||||
if (is_array($req_path)) { // 参数只是路径的一部分
|
||||
if(count($req_path)>0){
|
||||
$end = max(array_keys($req_path));
|
||||
Verify::isTrue($end <128, "too long path with length $end");
|
||||
for ($i = 0; $i <= $end; $i ++) {
|
||||
if (isset($req_path[$i])) {
|
||||
list ($arg_name, $arg_info) = $req_path[$i];
|
||||
if(isset($route_path[$i + $offest]) && $route_path[$i + $offest] !=='*'){
|
||||
//忽略固定的路径
|
||||
}else{
|
||||
$route_path[$i + $offest] = "[$arg_name]";
|
||||
$docs = "$docs$arg_name:\r\n {$arg_info['doc']}\r\n\r\n";
|
||||
}
|
||||
} else {
|
||||
if (! isset($route_path[$i + $offest])) {
|
||||
$route_path[$i + $offest] = '*';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // 参数整个路径
|
||||
list ($arg_name, $arg_info) = $req_path;
|
||||
$route_path[$offest] = "[$arg_name]";
|
||||
$docs = "$docs$arg_name:\r\n {$arg_info['doc']}\r\n\r\n";
|
||||
}
|
||||
|
||||
unset($params['path']);
|
||||
}
|
||||
$path .= ' /';
|
||||
$path .= implode('/', $route_path);
|
||||
// querystring
|
||||
if (isset($params['_GET'])) {
|
||||
$get = $params['_GET'];
|
||||
if(is_array($get)){
|
||||
$first = true;
|
||||
foreach ($get as $name => $value) {
|
||||
list ($arg_name, $arg_info) = $value;
|
||||
if ($first) {
|
||||
$path = $path . '?';
|
||||
$first = false;
|
||||
} else {
|
||||
$path = $path . '&';
|
||||
}
|
||||
$path = "$path$name=[$arg_name]";
|
||||
$docs = "$docs$arg_name:\r\n {$arg_info['doc']}\r\n\r\n";
|
||||
}
|
||||
}else{
|
||||
// 参数整个_GET
|
||||
list ($arg_name, $arg_info) = $get;
|
||||
$path = "$path?[$arg_name]";
|
||||
$docs = "$docs$arg_name:\r\n {$arg_info['doc']}\r\n\r\n";
|
||||
}
|
||||
unset($params['_GET']);
|
||||
}
|
||||
$path .= " HTTP/1.1\r\n";
|
||||
|
||||
// header
|
||||
$header = '';
|
||||
if (isset($params['header'])) {
|
||||
$headers = $params['header'];
|
||||
$first = true;
|
||||
foreach ($headers as $header_name => $value) {
|
||||
|
||||
//if (substr_compare($name, 'HTTP_X_', 0, 7) !== 0) {
|
||||
// continue;
|
||||
//}
|
||||
//$words = explode('_', substr($name, 7));
|
||||
//$header_name = '';
|
||||
//foreach ($words as $k => $word) {
|
||||
// $words[$k] = ucwords(strtolower($word));
|
||||
//}
|
||||
//$header_name = implode('-', $words);
|
||||
list ($arg_name, $arg_info) = $value;
|
||||
$header = "$header$header_name: [$arg_name]\r\n";
|
||||
$docs = "$docs$arg_name:\r\n {$arg_info['doc']}\r\n\r\n";
|
||||
unset($params['_SERVER'][$name]);
|
||||
}
|
||||
}
|
||||
|
||||
// cookie
|
||||
$header = '';
|
||||
if (isset($params['_COOKIE'])) {
|
||||
$cookies = $params['_COOKIE'];
|
||||
$first = true;
|
||||
$header = $header."Cookie: ";
|
||||
foreach ($cookies as $cookie_name => $value) {
|
||||
list ($arg_name, $arg_info) = $value;
|
||||
$header = "$header$cookie_name=[$arg_name];";
|
||||
$docs = "$docs$arg_name:\r\n {$arg_info['doc']}\r\n\r\n";
|
||||
}
|
||||
$header .= "\r\n";
|
||||
}
|
||||
|
||||
// body
|
||||
$body = '';
|
||||
if (isset($params['_POST'])) {
|
||||
$post = $params['_POST'];
|
||||
$first = true;
|
||||
if(is_array($post)){
|
||||
foreach ($post as $name => $value) {
|
||||
list ($arg_name, $arg_info) = $value;
|
||||
if ($first) {
|
||||
$first = false;
|
||||
} else {
|
||||
$body = $body . '&';
|
||||
}
|
||||
$body = "$body$name=[$arg_name]";
|
||||
$docs = "$docs$arg_name:\r\n {$arg_info['doc']}\r\n\r\n";
|
||||
}
|
||||
}else{
|
||||
// 参数整个_POST
|
||||
list ($arg_name, $arg_info) = $post;
|
||||
$body = "{$body}[$arg_name]";
|
||||
$docs = "$docs$arg_name:\r\n {$arg_info['doc']}\r\n\r\n";
|
||||
}
|
||||
unset($params['_POST']);
|
||||
}
|
||||
|
||||
if (isset($params['_FILES'])) {
|
||||
$files = $params['_FILES'];
|
||||
if(is_array($files)){
|
||||
foreach ($files as $name => $value) {
|
||||
//TODO: 这里假设只有一个文件上传
|
||||
list ($arg_name, $arg_info) = $this->searchArgInfo($value);
|
||||
$docs = "$docs$name:\r\n {$arg_info['doc']}\r\n\r\n";
|
||||
}
|
||||
}
|
||||
unset($params['_POST']);
|
||||
}
|
||||
|
||||
$sample = $path . $header . "\r\n" . $body;
|
||||
return array(
|
||||
$sample,
|
||||
$docs,
|
||||
);
|
||||
}
|
||||
private function searchArgInfo($value){
|
||||
if(is_object($value)){
|
||||
return $value;
|
||||
}
|
||||
return $this->searchArgInfo(array_values($value)[0]);
|
||||
}
|
||||
/**
|
||||
* 生成响应的示例和说明
|
||||
*
|
||||
* @param array $api
|
||||
* @return array [sample, doc]
|
||||
*/
|
||||
private function createResponseDoc($api, $default_return=true)
|
||||
{
|
||||
// 'name' => $fun_name,
|
||||
// 'args' => $args,
|
||||
// 'doc' => $anns['return'][$id]['doc']
|
||||
$status = '';
|
||||
if($default_return){
|
||||
$status = "HTTP/1.1 200 OK\r\n";
|
||||
}
|
||||
|
||||
$header = '';
|
||||
$doc = '';
|
||||
$body = '';
|
||||
foreach ($api['returns'] as $return) {
|
||||
$info = $this->getResponseInfo($return);
|
||||
if (isset($info['status'])) {
|
||||
$status = $info['status'];
|
||||
}
|
||||
if (isset($info['header'])) {
|
||||
$header .= $info['header'];
|
||||
}
|
||||
if (isset($info['body'])) {
|
||||
$body .= $info['body'];
|
||||
}
|
||||
if (isset($info['doc']) && $info['doc']) {
|
||||
$doc .= $info['doc'];
|
||||
}
|
||||
}
|
||||
$sample = $status . $header . "\r\n" . $body;
|
||||
return array(
|
||||
$sample,
|
||||
$doc,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成响应的示例和说明
|
||||
*
|
||||
* @param array $api
|
||||
* @return array [sample, doc]
|
||||
*/
|
||||
private function createExceptionDoc($api)
|
||||
{
|
||||
// 'name' => $fun_name,
|
||||
// 'args' => $args,
|
||||
// 'doc' => $anns['return'][$id]['doc']
|
||||
$res = array();
|
||||
foreach ($api['throws'] as $name => $throws) {
|
||||
$status = "HTTP/1.1 500 Internal Server Error\r\n";
|
||||
$header = '';
|
||||
$doc = '';
|
||||
$body = '';
|
||||
|
||||
foreach ($throws as $throw) {
|
||||
$info = $this->getResponseInfo($throw);
|
||||
if (isset($info['status'])) {
|
||||
$status = $info['status'];
|
||||
}
|
||||
if (isset($info['header'])) {
|
||||
$header .= $info['header'];
|
||||
}
|
||||
if (isset($info['body'])) {
|
||||
if(is_array($info['body'])){
|
||||
$body .= json_encode($info['body']);
|
||||
}else{
|
||||
$body .= $info['body'];
|
||||
}
|
||||
}
|
||||
if (isset($info['doc']) && $info['doc']) {
|
||||
$doc .= $info['doc'];
|
||||
}
|
||||
}
|
||||
$sample = $status . $header . "\r\n" . $body;
|
||||
$res[$name] = array(
|
||||
$sample,
|
||||
$doc,
|
||||
);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单个响应的示例和说明
|
||||
*
|
||||
* @param array $api
|
||||
* @return array [sample, doc]
|
||||
*/
|
||||
private function getResponseInfo($return)
|
||||
{
|
||||
$res = array();
|
||||
if ($return['name'] === 'status') {
|
||||
$arg = $return['args'][0];
|
||||
$value = $arg['value'];
|
||||
if ($arg['is_const']) {
|
||||
$res['status'] = "HTTP/1.1 $value\r\n";
|
||||
} else {
|
||||
$res['status'] = "HTTP/1.1 [$value]\r\n";
|
||||
}
|
||||
if ($return['doc']) {
|
||||
$res['doc'] = "$value:\r\n {$return['doc']}\r\n\r\n";
|
||||
}
|
||||
} elseif($return['name'] === 'res'){
|
||||
$arg = $return['args'][0];
|
||||
$value = $arg['value'];
|
||||
if ($arg['is_const']) {
|
||||
$res['status'] = "HTTP/1.1 $value\r\n";
|
||||
} else {
|
||||
$res['status'] = "HTTP/1.1 [$value]\r\n";
|
||||
}
|
||||
if ($return['doc']) {
|
||||
$res['doc'] = "$value:\r\n {$return['doc']}\r\n\r\n";
|
||||
}
|
||||
|
||||
$arg = $return['args'][1];
|
||||
$value = $arg['value'];
|
||||
if ($arg['is_const']) {
|
||||
$res['body'] = $value;
|
||||
} else {
|
||||
if($value){
|
||||
$res['body'] = "[$value]";
|
||||
}else{
|
||||
$res['body'] = "[return]";
|
||||
}
|
||||
|
||||
}
|
||||
if ($return['doc']) {
|
||||
if(is_array($value)){
|
||||
$value = json_encode($value);
|
||||
}
|
||||
$res['doc'] = "return $value:\r\n {$return['doc']}\r\n\r\n";
|
||||
}
|
||||
|
||||
}elseif($return['name'] === 'header') {
|
||||
$arg = $return['args'][0];
|
||||
$value = $arg['value'];
|
||||
if ($arg['is_const']) {
|
||||
$res['header'] .= "$value \r\n";
|
||||
} else {
|
||||
$res['header'] .= "[$value] \r\n";
|
||||
}
|
||||
if ($return['doc']) {
|
||||
$res['doc'] = "$value:\r\n {$return['doc']}\r\n\r\n";
|
||||
}
|
||||
} elseif ($return['name'] === 'cookie') {
|
||||
$args = $return['args'];
|
||||
foreach ($args as $k => &$arg) {
|
||||
if (! $arg['is_const']) {
|
||||
$value = $arg['value'];
|
||||
$arg['value'] = "[$value]";
|
||||
}
|
||||
}
|
||||
if ($return['doc']) {
|
||||
$res['doc'] = "cookie {$args[0]['value']}:\r\n {$return['doc']}\r\n\r\n";
|
||||
}
|
||||
|
||||
$res['header'] = 'Set-Cookie: ' . $args[0]['value'] . '=' . $args[1]['value']
|
||||
. (empty($args[2]['value']) ? '' : '; expires=' . gmdate('D, d-M-Y H:i:s', strtotime($args[2]['value'])) . ' GMT')
|
||||
. (empty($args[3]['value']) ? '' : '; path=' . $args[3]['value'])
|
||||
. (empty($args[4]['value']) ? '' : '; domain=' . $args[4]['value'])
|
||||
. (empty($args[5]['value']) ? '' : '; secure')
|
||||
. (empty($args[6]['value']) ? '' : '; HttpOnly');
|
||||
$res['header'] .= "\r\n";
|
||||
} elseif ($return['name'] === 'body') {
|
||||
$arg = $return['args'][0];
|
||||
$value = $arg['value'];
|
||||
if ($arg['is_const']) {
|
||||
$res['body'] = $value;
|
||||
} else {
|
||||
if($value){
|
||||
$res['body'] = "[$value]";
|
||||
}else{
|
||||
$res['body'] = "[return]";
|
||||
}
|
||||
|
||||
}
|
||||
if ($return['doc']) {
|
||||
if(is_array($value)){
|
||||
$value = json_encode($value);
|
||||
}
|
||||
$res['doc'] = "return $value:\r\n {$return['doc']}\r\n\r\n";
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 遍历数组, 子数组
|
||||
*
|
||||
* @param unknown $arr
|
||||
*/
|
||||
static function walkTree(&$arr, $visitor)
|
||||
{
|
||||
foreach ($arr as $k => &$v) {
|
||||
if (is_array($v)) {
|
||||
self::walkTree($v, $visitor);
|
||||
} else {
|
||||
$visitor($v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取invoker信息
|
||||
*
|
||||
* @param Invoker $invoker
|
||||
* @param $method http方法
|
||||
* @param $uri http url
|
||||
* @return array
|
||||
*/
|
||||
public function getInvokerInfo( $method,$uri, $invoker)
|
||||
{
|
||||
$res = array();
|
||||
$res['impl'] = $invoker->getMethodName();
|
||||
$ann = new AnnotationReader();
|
||||
$refl = new \ReflectionClass($invoker->getClassName());
|
||||
$mrefl = $refl->getMethod($invoker->getMethodName());
|
||||
$anns = $ann->getMethodAnnotations($mrefl, true);
|
||||
// 过滤无效的参数
|
||||
if(isset($anns['param'])){
|
||||
$anns['param'] = array_values(array_filter($anns['param'], function ($i)
|
||||
{
|
||||
return isset($i['value']);
|
||||
}));
|
||||
}
|
||||
if(isset($anns['return'])){
|
||||
$anns['return'] = array_values(array_filter($anns['return'], function ($i)
|
||||
{
|
||||
return isset($i['value']);
|
||||
}));
|
||||
}
|
||||
if(isset($anns['throws'])){
|
||||
$anns['throws'] = array_values(array_filter($anns['throws'], function ($i)
|
||||
{
|
||||
return isset($i['value']);
|
||||
}));
|
||||
}
|
||||
|
||||
$res['doc'] = $this->getDocText($mrefl->getDocComment());
|
||||
//找到匹配的@route注释
|
||||
foreach ($anns['route'] as $route_doc){
|
||||
//TODO: 同时精确匹配和通配符匹配时, 怎么处理
|
||||
if(isset($route_doc['value'])){
|
||||
list($m, $u) = $route_doc['value'];
|
||||
$full_url = $invoker->getContainer()->path.'/'.$u;
|
||||
$full_url = HttpRouterEntries::stringToPath($full_url);
|
||||
if($full_url == HttpRouterEntries::stringToPath($uri) &&
|
||||
$m === $method){
|
||||
$text = $this->getDocText($route_doc['desc']);
|
||||
if(!empty($text)){
|
||||
$res['doc'] .= "\r\n";
|
||||
$res['doc'] .= $text;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
$anns['route'];
|
||||
$res['root'] = '/';
|
||||
$res['path'] = $invoker->getContainer()->path;
|
||||
|
||||
// 获取参数信息
|
||||
$res['params'] = array();
|
||||
foreach ($invoker->getParams()->getParams() as $param) {
|
||||
list ($is_const, $value, $info, $id) = $param;
|
||||
list ($name, $is_ref, $is_optional, $default) = $info;
|
||||
if (! $is_const) {
|
||||
$res['params'][$name] = array(
|
||||
'value' => $value,
|
||||
'is_ref' => $is_ref,
|
||||
'is_optional' => $is_optional,
|
||||
'default' => $default,
|
||||
'doc' => $this->getDocText($anns['param'][$id]['desc']),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 依赖只是特殊的参数
|
||||
$defaults = $refl->getDefaultProperties();
|
||||
foreach ($refl->getProperties() as $property) {
|
||||
foreach ($ann->getPropertyAnnotations($property, true) as $type => $value) {
|
||||
if ($type !== 'inject') {
|
||||
continue;
|
||||
}
|
||||
$name = $property->getName();
|
||||
$value = $value[0];
|
||||
if (is_array($value['value'])) {
|
||||
$src = $value['value']['src'];
|
||||
if (isset($value['value']['optional']) && $value['value']['optional']) {
|
||||
$is_optional = true;
|
||||
}
|
||||
if (isset($value['value']['default'])) {
|
||||
$default = $value['value']['default'];
|
||||
}
|
||||
} else {
|
||||
$src = $value['value'];
|
||||
}
|
||||
|
||||
if (substr($src, 0, 1) !== '$') {
|
||||
continue;
|
||||
}
|
||||
if (array_key_exists($name, $defaults)) {
|
||||
$is_optional = true;
|
||||
$default = $defaults[$name];
|
||||
}
|
||||
|
||||
$res['params'][$name] = array(
|
||||
'value' => $src,
|
||||
'is_ref' => false,
|
||||
'is_optional' => $is_optional,
|
||||
'default' => $default,
|
||||
'doc' => $this->getDocText($value['desc']),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取返回值信息
|
||||
$res['returns'] = array();
|
||||
foreach ($invoker->getReturns()->getParams() as $fun_name => $calls) {
|
||||
foreach ($calls as $id => $call) {
|
||||
$args = array();
|
||||
foreach ($call as $num => $arg) {
|
||||
list ($is_const, $value, $pos, $info) = $arg;
|
||||
list ($name, $is_ref, $is_optional, $default) = $info;
|
||||
|
||||
$args[$num] = array(
|
||||
'value' => $value,
|
||||
'name' => $name,
|
||||
'is_const' => $is_const,
|
||||
);
|
||||
}
|
||||
|
||||
$res['returns'][] = array(
|
||||
'name' => $fun_name,
|
||||
'args' => $args,
|
||||
'doc' => $id===-1?null:$this->getDocText($anns['return'][$id]['desc']),
|
||||
);
|
||||
}
|
||||
}
|
||||
// 获取异常信息
|
||||
$res['throws'] = array();
|
||||
foreach ($invoker->getThrows()->getParams() as $exce_name => $throws) {
|
||||
// $res['throws'][$exce_name] = array();
|
||||
foreach ($throws as $fun_name => $calls) {
|
||||
foreach ($calls as $id => $call) {
|
||||
$args = array();
|
||||
if($call !== null){
|
||||
foreach ($call as $num => $arg) {
|
||||
$args[$num] = array(
|
||||
'value' => $arg,
|
||||
'name' => null,
|
||||
'is_const' => true,
|
||||
);
|
||||
}
|
||||
}
|
||||
$res['throws'][$exce_name][] = array(
|
||||
'name' => $fun_name,
|
||||
'args' => $args,
|
||||
'doc' => $this->getDocText($anns['throws'][$id]['desc']),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 去掉文档中的@标记和*号等, 保留可读文本
|
||||
*
|
||||
* @param string $doc
|
||||
* @return string
|
||||
*/
|
||||
private function getDocText($doc)
|
||||
{
|
||||
$lines = explode("\n", $doc);
|
||||
$ignore = array(
|
||||
'@return',
|
||||
'@param',
|
||||
'@throws',
|
||||
'@route',
|
||||
'@path',
|
||||
'@cache',
|
||||
);
|
||||
$text = '';
|
||||
$fistline = true;
|
||||
$is_ignored = false;
|
||||
foreach ($lines as $num => $ori_line) {
|
||||
$line = trim($ori_line, "*/\r\n\t ");
|
||||
if($is_ignored){
|
||||
if(substr($line, 0, 1 ) !== '@'){
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$is_ignored = false;
|
||||
foreach ($ignore as $word) {
|
||||
if (strlen($line)>= strlen($word) && 0 === substr_compare($line, $word, 0, strlen($word))) {
|
||||
$is_ignored = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($is_ignored) {
|
||||
continue;
|
||||
}
|
||||
if ($fistline) {
|
||||
$fistline = false;
|
||||
} else {
|
||||
$text .= "\r\n";
|
||||
}
|
||||
$text .= trim(trim($ori_line, " \t\r\n"), "*/");
|
||||
}
|
||||
$text = trim($text, "\r\n");
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inject("$.router")
|
||||
*/
|
||||
private $router;
|
||||
}
|
||||
22
codes/agent/game/api/lib/phprs/ezsql/DB.php
Normal file
22
codes/agent/game/api/lib/phprs/ezsql/DB.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* $Id: DB.php 131 2015-10-10 02:25:57Z yangmin.cao $
|
||||
* @author caoym(caoyangmin@gmail.com)
|
||||
*/
|
||||
namespace phprs\ezsql;
|
||||
/**
|
||||
*
|
||||
* @author caoym
|
||||
*
|
||||
*/
|
||||
class DB extends \PDO
|
||||
{
|
||||
public function __construct(
|
||||
$dsn,
|
||||
$username,
|
||||
$passwd,
|
||||
$options = [\PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES'utf8';"]){
|
||||
parent::__construct($dsn, $username, $passwd, $options);
|
||||
$this->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
}
|
||||
27
codes/agent/game/api/lib/phprs/ezsql/Native.php
Normal file
27
codes/agent/game/api/lib/phprs/ezsql/Native.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* $Id: Native.php 131 2015-10-10 02:25:57Z yangmin.cao $
|
||||
* @author caoym(caoyangmin@gmail.com)
|
||||
*/
|
||||
namespace phprs\ezsql;
|
||||
/**
|
||||
* 原始sql字符串, 拼接时不进行转义
|
||||
* @author caoym
|
||||
*
|
||||
*/
|
||||
class Native
|
||||
{
|
||||
/**
|
||||
* @param string $str
|
||||
*/
|
||||
function __construct($str) {
|
||||
$this->str = $str;
|
||||
}
|
||||
public function __toString(){
|
||||
return $this->str;
|
||||
}
|
||||
public function get(){
|
||||
return $this->str;
|
||||
}
|
||||
private $str;
|
||||
}
|
||||
44
codes/agent/game/api/lib/phprs/ezsql/README.md
Normal file
44
codes/agent/game/api/lib/phprs/ezsql/README.md
Normal file
@@ -0,0 +1,44 @@
|
||||
# ezsql
|
||||
An an easy-to-use SQL builder.
|
||||
|
||||
## HOW TO USE
|
||||
|
||||
$db = new \PDO($dsn, $username, $passwd);
|
||||
|
||||
### SELECT
|
||||
|
||||
$res = Sql::select('a, b')
|
||||
->from('table')
|
||||
->leftJoin('table1')->on('table.id=table1.id')
|
||||
->where('a=?',1)
|
||||
->groupBy('b')->having('sum(b)=?', 2)
|
||||
->orderBy('c', Sql::$ORDER_BY_ASC)
|
||||
->limit(0,1)
|
||||
->forUpdate()->of('d')
|
||||
->get($db);
|
||||
### UPDATE
|
||||
|
||||
$rows = Sql::update('table')
|
||||
->set('a', 1)
|
||||
->where('b=?', 2)
|
||||
->orderBy('c', Sql::$ORDER_BY_ASC)
|
||||
->limit(1)
|
||||
->exec($db)
|
||||
->rows
|
||||
|
||||
### INSERT
|
||||
|
||||
$newId = Sql::insertInto('table')
|
||||
->values(['a'=>1])
|
||||
->exec($db)
|
||||
->lastInsertId()
|
||||
|
||||
### DELETE
|
||||
|
||||
$rows = Sql::deleteFrom('table')
|
||||
->where('b=?', 2)
|
||||
->orderBy('c', Sql::$ORDER_BY_ASC)
|
||||
->limit(1)
|
||||
->exec($db)
|
||||
->rows
|
||||
|
||||
132
codes/agent/game/api/lib/phprs/ezsql/Sql.php
Normal file
132
codes/agent/game/api/lib/phprs/ezsql/Sql.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
/**
|
||||
* $Id: Sql.php 131 2015-10-10 02:25:57Z yangmin.cao $
|
||||
* @author caoym(caoyangmin@gmail.com)
|
||||
*/
|
||||
namespace phprs\ezsql;
|
||||
|
||||
use phprs\ezsql\rules\select\SelectRule;
|
||||
use phprs\ezsql\rules\insert\InsertRule;
|
||||
use phprs\ezsql\rules\update\UpdateRule;
|
||||
use phprs\ezsql\rules\delete\DeleteRule;
|
||||
use phprs\ezsql\rules\basic\BasicRule;
|
||||
use phprs\ezsql\rules\replace\ReplaceIntoRule;
|
||||
|
||||
require_once __DIR__.'/rules/select.php';
|
||||
require_once __DIR__.'/rules/insert.php';
|
||||
require_once __DIR__.'/rules/update.php';
|
||||
require_once __DIR__.'/rules/delete.php';
|
||||
require_once __DIR__.'/rules/replace.php';
|
||||
|
||||
/**
|
||||
* Easy SQL
|
||||
*
|
||||
* How-to-use:
|
||||
*
|
||||
* $db = new DB($dsn, $username, $passwd);
|
||||
* // 1. select
|
||||
* $res = Sql::select('a, b')
|
||||
* ->from('table')
|
||||
* ->leftJoin('table1')->on('table.id=table1.id')
|
||||
* ->where('a=?',1)
|
||||
* ->groupBy('b')->having('sum(b)=?', 2)
|
||||
* ->orderBy('c', Sql::$ORDER_BY_ASC)
|
||||
* ->limit(0,1)
|
||||
* ->forUpdate()->of('d')
|
||||
* ->get($db);
|
||||
*
|
||||
* // 2. update
|
||||
* $rows = Sql::update('table')
|
||||
* ->set('a', 1)
|
||||
* ->where('b=?', 2)
|
||||
* ->orderBy('c', Sql::$ORDER_BY_ASC)
|
||||
* ->limit(1)
|
||||
* ->exec($db)
|
||||
* ->rows
|
||||
*
|
||||
* // 3. insert
|
||||
* $newId = Sql::insertInto('table')
|
||||
* ->values(['a'=>1])
|
||||
* ->exec($db)
|
||||
* ->lastInsertId()
|
||||
*
|
||||
* //4. delete
|
||||
* $rows = Sql::deleteFrom('table')
|
||||
* ->where('b=?', 2)
|
||||
* ->orderBy('c', Sql::$ORDER_BY_ASC)
|
||||
* ->limit(1)
|
||||
* ->exec($db)
|
||||
* ->rows
|
||||
*
|
||||
* @author caoym <caoyangmin@gmail.com>
|
||||
*/
|
||||
class Sql{
|
||||
/**
|
||||
* select('column0,column1') => "SELECT column0,column1"
|
||||
*
|
||||
* select('column0', 'column1') => "SELECT column0,column1"
|
||||
*
|
||||
* @param $param0 columns
|
||||
* @return \phprs\ezsql\rules\select\FromRule
|
||||
*/
|
||||
static public function select($param0='*', $_=null){
|
||||
$obj = new SelectRule(new SqlConetxt());
|
||||
$args = func_get_args();
|
||||
if(empty($args)){
|
||||
$args = ['*'];
|
||||
}
|
||||
return $obj->select(implode(',', $args));
|
||||
}
|
||||
/**
|
||||
* insertInto('table') => "INSERT INTO table"
|
||||
*
|
||||
* @param string $table
|
||||
* @return \phprs\ezsql\rules\insert\ValuesRule
|
||||
*/
|
||||
static public function insertInto($table) {
|
||||
$obj = new InsertRule(new SqlConetxt());
|
||||
return $obj->insertInto($table);
|
||||
}
|
||||
/**
|
||||
* update('table') => "UPDATE table"
|
||||
* @param string $table
|
||||
* @return \phprs\ezsql\rules\update\UpdateSetRule
|
||||
*/
|
||||
static public function update($table) {
|
||||
$obj = new UpdateRule(new SqlConetxt());
|
||||
return $obj->update($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* deleteFrom('table') => "DELETE FROM table"
|
||||
* @param string $table
|
||||
* @return \phprs\ezsql\rules\basic\WhereRule
|
||||
*/
|
||||
static public function deleteFrom($table){
|
||||
$obj = new DeleteRule(new SqlConetxt());
|
||||
return $obj->deleteFrom($table);
|
||||
}
|
||||
/**
|
||||
* replaceInto('table') => "REPLACE INTO table"
|
||||
* @param string $table
|
||||
* @return \phprs\ezsql\rules\replace\ValuesRule
|
||||
*/
|
||||
static public function replaceInto($table){
|
||||
$obj = new ReplaceIntoRule(new SqlConetxt());
|
||||
return $obj->replaceInto($table);
|
||||
}
|
||||
/**
|
||||
* Splice sql use native string(without escaping)
|
||||
* for example:
|
||||
* where('time>?', 'now()') => " WHERE time > 'now()' "
|
||||
* where('time>?', Sql::native('now()')) => " WHERE time > now() "
|
||||
* @param string $str
|
||||
* @return Native
|
||||
*/
|
||||
static public function native($str){
|
||||
return new Native($str);
|
||||
}
|
||||
|
||||
static public $ORDER_BY_ASC ='ASC';
|
||||
static public $ORDER_BY_DESC ='DESC';
|
||||
}
|
||||
37
codes/agent/game/api/lib/phprs/ezsql/SqlConetxt.php
Normal file
37
codes/agent/game/api/lib/phprs/ezsql/SqlConetxt.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* $Id: SqlConetxt.php 131 2015-10-10 02:25:57Z yangmin.cao $
|
||||
* @author caoym(caoyangmin@gmail.com)
|
||||
*/
|
||||
namespace phprs\ezsql;
|
||||
|
||||
/**
|
||||
* @author caoym
|
||||
*/
|
||||
class SqlConetxt{
|
||||
|
||||
/**
|
||||
* 拼接sql语句,并自动插入空格
|
||||
* @param string $sql 表达式
|
||||
*/
|
||||
public function appendSql($sql, $addSpace=true){
|
||||
if($this->sql == ''){
|
||||
$this->sql = $sql;
|
||||
}else{
|
||||
if($addSpace){
|
||||
$this->sql = $this->sql.' '.$sql;
|
||||
}else{
|
||||
$this->sql = $this->sql.$sql;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 增加绑定变量值
|
||||
* @param array $params 变量
|
||||
*/
|
||||
public function appendParams($params){
|
||||
$this->params = array_merge($this->params, $params);
|
||||
}
|
||||
public $sql='';
|
||||
public $params=[];
|
||||
}
|
||||
469
codes/agent/game/api/lib/phprs/ezsql/impls.php
Normal file
469
codes/agent/game/api/lib/phprs/ezsql/impls.php
Normal file
@@ -0,0 +1,469 @@
|
||||
<?php
|
||||
/**
|
||||
* $Id: impls.php 401 2015-11-06 08:28:26Z dong.chen $
|
||||
* @author caoym(caoyangmin@gmail.com)
|
||||
*/
|
||||
namespace phprs\ezsql\impls;
|
||||
use phprs\util\NestedStringCut;
|
||||
use phprs\util\Verify;
|
||||
use phprs\ezsql\SqlConetxt;
|
||||
|
||||
class Response{
|
||||
public function __construct($success,$pdo, $st){
|
||||
$this->pdo = $pdo;
|
||||
$this->st = $st;
|
||||
$this->success = $success;
|
||||
$this->rows = $this->st->rowCount();
|
||||
}
|
||||
public function lastInsertId($name=null){
|
||||
return $this->pdo->lastInsertId($name);
|
||||
}
|
||||
/**
|
||||
* @var bool
|
||||
* true on success or false on failure.
|
||||
*/
|
||||
public $success;
|
||||
/**
|
||||
* @var int
|
||||
* the number of rows.
|
||||
*/
|
||||
public $rows;
|
||||
/**
|
||||
*
|
||||
* @var \PDO
|
||||
*/
|
||||
public $pdo;
|
||||
|
||||
/**
|
||||
* @var \PDOStatement
|
||||
*/
|
||||
public $st;
|
||||
}
|
||||
|
||||
class SelectImpl
|
||||
{
|
||||
static public function select($context, $columns){
|
||||
$context->appendSql("SELECT $columns");
|
||||
}
|
||||
}
|
||||
|
||||
class FromImpl
|
||||
{
|
||||
static public function from($context, $tables){
|
||||
$context->appendSql("FROM $tables");
|
||||
}
|
||||
}
|
||||
|
||||
class DeleteImpl
|
||||
{
|
||||
static public function deleteFrom($context, $from)
|
||||
{
|
||||
$context->appendSql("DELETE FROM $from");
|
||||
}
|
||||
}
|
||||
|
||||
class JoinImpl
|
||||
{
|
||||
static public function join($context, $type, $table) {
|
||||
if($type){
|
||||
$context->appendSql("$type JOIN $table");
|
||||
}else{
|
||||
$context->appendSql("JOIN $table");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class JoinOnImpl
|
||||
{
|
||||
static public function on($context, $condition) {
|
||||
$context->appendSql("ON $condition");
|
||||
}
|
||||
}
|
||||
|
||||
class ForUpdateImpl
|
||||
{
|
||||
static public function forUpdate($context){
|
||||
$context->appendSql("FOR UPDATE");
|
||||
}
|
||||
}
|
||||
|
||||
class ForUpdateOfImpl
|
||||
{
|
||||
static public function of($context, $column){
|
||||
$context->appendSql("OF $column");
|
||||
}
|
||||
}
|
||||
|
||||
class InsertImpl
|
||||
{
|
||||
static public function insertInto($context, $table) {
|
||||
$context->appendSql("INSERT INTO $table");
|
||||
}
|
||||
}
|
||||
class ReplaceImpl
|
||||
{
|
||||
static public function replaceInto($context, $table) {
|
||||
$context->appendSql("REPLACE INTO $table");
|
||||
}
|
||||
}
|
||||
class ValuesImpl
|
||||
{
|
||||
static public function values($context, $values){
|
||||
$params = [];
|
||||
$stubs = [];
|
||||
foreach ($values as $v){
|
||||
if(is_a($v, 'phprs\\ezsql\\Native')){//直接拼接sql,不需要转义
|
||||
$stubs[]=$v->get();
|
||||
}else{
|
||||
$stubs[]='?';
|
||||
$params[] = $v;
|
||||
}
|
||||
}
|
||||
$stubs = implode(',', $stubs);
|
||||
|
||||
if(array_keys($values) === range(0, count($values) - 1)){
|
||||
//VALUES(val0, val1, val2)
|
||||
$context->appendSql("VALUES($stubs)");
|
||||
|
||||
}else{
|
||||
//(col0, col1, col2) VALUES(val0, val1, val2)
|
||||
$columns = implode(',', array_keys($values));
|
||||
$context->appendSql("($columns) VALUES($stubs)",false);
|
||||
}
|
||||
$context->appendParams($params);
|
||||
}
|
||||
private $sql = null;
|
||||
}
|
||||
|
||||
class UpdateImpl
|
||||
{
|
||||
static public function update($context, $table){
|
||||
$context->appendSql("UPDATE $table");
|
||||
}
|
||||
}
|
||||
|
||||
class UpdateSetImpl
|
||||
{
|
||||
public function set($context, $column, $value){
|
||||
$prefix = '';
|
||||
if($this->first){
|
||||
$this->first = false;
|
||||
$prefix = 'SET ';
|
||||
}else{
|
||||
$prefix = ',';
|
||||
}
|
||||
if(is_a($value, 'phprs\\ezsql\\Native')){
|
||||
$context->appendSql("$prefix$column=$value",$prefix == 'SET ');
|
||||
}else{
|
||||
$context->appendSql("$prefix$column=?",$prefix == 'SET ');
|
||||
$context->appendParams([$value]);
|
||||
}
|
||||
}
|
||||
public function setArgs($context, $values){
|
||||
$set = [];
|
||||
$params = [];
|
||||
foreach ($values as $k=>$v){
|
||||
if(is_a($v, 'phprs\\ezsql\\Native')){//直接拼接sql,不需要转义
|
||||
$set[]= "$k=".$v->get();
|
||||
}else{
|
||||
$set[]= "$k=?";
|
||||
$params[]=$v;
|
||||
}
|
||||
}
|
||||
if($this->first){
|
||||
$this->first = false;
|
||||
$context->appendSql('SET '.implode(',', $set));
|
||||
$context->appendParams($params);
|
||||
}else{
|
||||
$context->appendSql(','.implode(',', $set),false);
|
||||
$context->appendParams($params);
|
||||
}
|
||||
}
|
||||
private $first=true;
|
||||
}
|
||||
class OrderByImpl
|
||||
{
|
||||
public function orderByArgs($context, $orders){
|
||||
if(empty($orders)){
|
||||
return $this;
|
||||
}
|
||||
$params = array();
|
||||
foreach ($orders as $k=>$v){
|
||||
if(is_integer($k)){
|
||||
Verify::isTrue(
|
||||
preg_match('/^[a-zA-Z0-9_.]+$/', $v),
|
||||
new \InvalidArgumentException("invalid params for orderBy(".json_encode($orders).")"));
|
||||
|
||||
$params[] = $v;
|
||||
}else{
|
||||
$v = strtoupper($v);
|
||||
Verify::isTrue(
|
||||
preg_match('/^[a-zA-Z0-9_.]+$/', $k) &&
|
||||
($v =='DESC' || $v =='ASC'), new \InvalidArgumentException("invalid params for orderBy(".json_encode($orders).")"));
|
||||
|
||||
$params[] = "$k $v";
|
||||
}
|
||||
}
|
||||
if($this->first){
|
||||
$this->first = false;
|
||||
$context->appendSql('ORDER BY '.implode(',', $params));
|
||||
}else{
|
||||
$context->appendSql(','.implode(',', $params),false);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
public function orderBy($context, $column, $order=null){
|
||||
if($this->first){
|
||||
$this->first = false;
|
||||
$context->appendSql("ORDER BY $column");
|
||||
}else{
|
||||
$context->appendSql(",$column", false);
|
||||
}
|
||||
if($order){
|
||||
$context->appendSql($order);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
private $first=true;
|
||||
}
|
||||
|
||||
class LimitImpl
|
||||
{
|
||||
static public function limit($context, $size){
|
||||
$intSize = intval($size);
|
||||
Verify::isTrue(strval($intSize) == $size,
|
||||
new \InvalidArgumentException("invalid params for limit($size)"));
|
||||
$context->appendSql("LIMIT $size");
|
||||
}
|
||||
static public function limitWithOffset($context,$start, $size){
|
||||
$intStart = intval($start);
|
||||
$intSize = intval($size);
|
||||
Verify::isTrue(strval($intStart) == $start && strval($intSize) == $size,
|
||||
new \InvalidArgumentException("invalid params for limit($start, $size)"));
|
||||
$context->appendSql("LIMIT $start,$size");
|
||||
}
|
||||
}
|
||||
|
||||
class WhereImpl{
|
||||
|
||||
static private function findQ($str,$offset = 0,$no=0){
|
||||
$found = strpos($str, '?', $offset);
|
||||
if($no == 0 || $found === false){
|
||||
return $found;
|
||||
}
|
||||
return self::findQ($str, $found+1, $no-1);
|
||||
}
|
||||
static public function having($context, $expr, $args){
|
||||
self::condition($context, 'HAVING', $expr, $args);
|
||||
}
|
||||
static public function where($context, $expr, $args){
|
||||
self::condition($context, 'WHERE', $expr, $args);
|
||||
}
|
||||
|
||||
static public function havingArgs($context, $args){
|
||||
self::conditionArgs($context, 'HAVING', $args);
|
||||
}
|
||||
static public function whereArgs($context, $args){
|
||||
self::conditionArgs($context, 'WHERE', $args);
|
||||
}
|
||||
/**
|
||||
* find like Mongodb query glossary
|
||||
* whereArray(
|
||||
* [
|
||||
* 'id'=>['>'=>1],
|
||||
* 'name'=>'cym',
|
||||
* ]
|
||||
* )
|
||||
* 支持的操作符有
|
||||
* = 'id'=>['=' => 1]
|
||||
* > 'id'=>['>' => 1]
|
||||
* < 'id'=>['<' => 1]
|
||||
* <> 'id'=>['<>' => 1]
|
||||
* >= 'id'=>['>=' => 1]
|
||||
* <= 'id'=>['<=' => 1]
|
||||
* BETWEEN 'id'=>['BETWEEN' => [1 ,2]]
|
||||
* LIKE 'id'=>['LIKE' => '1%']
|
||||
* IN 'id'=>['IN' => [1,2,3]]
|
||||
* NOT IN 'id'=>['NOT IN' => [1,2,3]]
|
||||
*
|
||||
* @param array $args
|
||||
*/
|
||||
static public function conditionArgs($context, $prefix, $args=[]){
|
||||
if($args ===null){
|
||||
return ;
|
||||
}
|
||||
$exprs = array();
|
||||
$params = array();
|
||||
foreach ($args as $k => $v){
|
||||
if(is_array($v)){
|
||||
$ops = ['=', '>', '<', '<>', '>=', '<=', 'IN', 'NOT IN', 'BETWEEN', 'LIKE'];
|
||||
$op = array_keys($v)[0];
|
||||
$op = strtoupper($op);
|
||||
|
||||
Verify::isTrue(
|
||||
false !== array_search($op, $ops),
|
||||
new \InvalidArgumentException("invalid param $op for whereArgs"));
|
||||
|
||||
$var = array_values($v)[0];
|
||||
if($op == 'IN' || $op == 'NOT IN'){
|
||||
$stubs = [];
|
||||
foreach ($var as $i){
|
||||
if(is_a($i, 'phprs\\ezsql\\Native')){
|
||||
$stubs[]=strval($i);
|
||||
}else{
|
||||
$stubs[]='?';
|
||||
$params[] = $i;
|
||||
}
|
||||
}
|
||||
$stubs = implode(',', $stubs);
|
||||
$exprs[] = "$k $op ($stubs)";
|
||||
}else if($op == 'BETWEEN'){
|
||||
$cond = "$k BETWEEN";
|
||||
if(is_a($var[0], 'phprs\\ezsql\\Native')){
|
||||
$cond = "$cond ".strval($var[0]);
|
||||
}else{
|
||||
$cond = "$cond ?";
|
||||
$params[] = $var[0];
|
||||
}
|
||||
if(is_a($var[1], 'phprs\\ezsql\\Native')){
|
||||
$cond = "$cond AND ".strval($var[1]);
|
||||
}else{
|
||||
$cond = "$cond AND ?";
|
||||
$params[] = $var[1];
|
||||
}
|
||||
$exprs[] = $cond;
|
||||
}else{
|
||||
if(is_a($var, 'phprs\\ezsql\\Native')){
|
||||
$exprs[] = "$k $op ".strval($var);
|
||||
}else{
|
||||
$exprs[] = "$k $op ?";
|
||||
$params[] = $var;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if(is_a($v, 'phprs\\ezsql\\Native')){
|
||||
$exprs[] = "$k = ".strval($v);
|
||||
|
||||
}else{
|
||||
$exprs[] = "$k = ?";
|
||||
$params[] = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return self::condition($context, $prefix, implode(' AND ', $exprs), $params);
|
||||
}
|
||||
static public function condition($context, $prefix, $expr, $args){
|
||||
if(!empty($expr)){
|
||||
if($args){
|
||||
//因为PDO不支持绑定数组变量, 这里需要手动展开数组
|
||||
//也就是说把 where("id IN(?)", [1,2]) 展开成 where("id IN(?,?)", 1,2)
|
||||
$cutted = null;
|
||||
$cut = null;
|
||||
$toReplace = array();
|
||||
|
||||
$newArgs=array();
|
||||
//找到所有数组对应的?符位置
|
||||
foreach ($args as $k =>$arg){
|
||||
if(is_array($arg) || is_a($arg, 'phprs\\ezsql\\Native')){
|
||||
if(!$cutted){
|
||||
$cut = new NestedStringCut($expr);
|
||||
$cutted = $cut->getText();
|
||||
}
|
||||
//找到第$k个?符
|
||||
$pos = self::findQ($cutted, 0, $k);
|
||||
$pos = $cut->mapPos($pos);
|
||||
Verify::isTrue($pos !== false,
|
||||
new \InvalidArgumentException("unmatched params and ? @ $expr"));
|
||||
|
||||
if(is_array($arg)){
|
||||
$stubs = [];
|
||||
foreach ($arg as $i){
|
||||
if(is_a($i, 'phprs\\ezsql\\Native')){
|
||||
$stubs[] = strval($i);
|
||||
}else{
|
||||
$stubs[] = '?';
|
||||
$newArgs[] = $i;
|
||||
}
|
||||
}
|
||||
$stubs = implode(',', $stubs);
|
||||
}else{
|
||||
$stubs = strval($arg);
|
||||
}
|
||||
$toReplace[] = [$pos, $stubs];
|
||||
|
||||
}else{
|
||||
$newArgs[]=$arg;
|
||||
}
|
||||
}
|
||||
|
||||
if(count($toReplace)){
|
||||
$toReplace = array_reverse($toReplace);
|
||||
foreach ($toReplace as $i){
|
||||
list($pos, $v) = $i;
|
||||
$expr = substr($expr, 0, $pos).$v. substr($expr, $pos+1);
|
||||
}
|
||||
$args = $newArgs;
|
||||
}
|
||||
}
|
||||
$context->appendSql($prefix.' '.$expr);
|
||||
if($args){
|
||||
$context->appendParams($args);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GroupByImpl{
|
||||
static public function groupBy($context, $column){
|
||||
$context->appendSql("GROUP BY $column");
|
||||
}
|
||||
}
|
||||
|
||||
class ExecImpl
|
||||
{
|
||||
/**
|
||||
*
|
||||
* @param $context SqlConetxt
|
||||
* @param $db \PDO
|
||||
* @param $exceOnError boolean whether throw exceptions
|
||||
* @return Response
|
||||
*/
|
||||
static public function exec($context, $db, $exceOnError=true) {
|
||||
if($exceOnError){
|
||||
$db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
$st = $db->prepare($context->sql);
|
||||
$success = $st->execute($context->params);
|
||||
return new Response($success, $db,$st);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param SqlConetxt $context
|
||||
* @param PDO $db
|
||||
* @param boolean $errExce
|
||||
* @param string $asDict return as dict or array
|
||||
* @return false|array
|
||||
*/
|
||||
static public function get($context, $db, $dictAs=null ,$errExce=true){
|
||||
if($errExce){
|
||||
$db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
|
||||
$st = $db->prepare($context->sql);
|
||||
if($st->execute($context->params)){
|
||||
$res = $st->fetchAll(\PDO::FETCH_ASSOC);
|
||||
if ($dictAs){
|
||||
$dict= [];
|
||||
foreach ($res as $i){
|
||||
$dict[$i[$dictAs]]=$i;
|
||||
}
|
||||
return $dict;
|
||||
}
|
||||
return $res;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
115
codes/agent/game/api/lib/phprs/ezsql/rules/basic.php
Normal file
115
codes/agent/game/api/lib/phprs/ezsql/rules/basic.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/**
|
||||
* $Id: basic.php 131 2015-10-10 02:25:57Z yangmin.cao $
|
||||
* @author caoym(caoyangmin@gmail.com)
|
||||
*/
|
||||
namespace phprs\ezsql\rules\basic;
|
||||
|
||||
use phprs\ezsql\impls\ExecImpl;
|
||||
use phprs\ezsql\impls\LimitImpl;
|
||||
use phprs\ezsql\impls\OrderByImpl;
|
||||
use phprs\ezsql\impls\WhereImpl;
|
||||
require_once dirname(__DIR__).'/impls.php';
|
||||
|
||||
class BasicRule
|
||||
{
|
||||
public function __construct($context){
|
||||
$this->context = $context;
|
||||
}
|
||||
protected $context;
|
||||
}
|
||||
|
||||
class ExecRule extends BasicRule
|
||||
{
|
||||
/**
|
||||
* Execute sql
|
||||
* @param \PDO $db
|
||||
* @param boolean $errExce whether throw exceptios
|
||||
* @return Response
|
||||
*/
|
||||
public function exec($db, $errExce=true) {
|
||||
return ExecImpl::exec($this->context, $db, $errExce);
|
||||
}
|
||||
}
|
||||
|
||||
class LimitRule extends ExecRule
|
||||
{
|
||||
/**
|
||||
* limit(1) => "LIMIT 1"
|
||||
* @param int $size
|
||||
* @return \phprs\ezsql\rules\basic\ExecRule
|
||||
*/
|
||||
public function limit($size) {
|
||||
LimitImpl::limit($this->context, $size);
|
||||
return new ExecRule($this->context);
|
||||
}
|
||||
}
|
||||
|
||||
class OrderByRule extends LimitRule
|
||||
{
|
||||
public function __construct($context){
|
||||
parent::__construct($context);
|
||||
$this->impl = new OrderByImpl();
|
||||
}
|
||||
/**
|
||||
* orderByArgs(['column0', 'column1'=>Sql::$ORDER_BY_ASC]) => "ORDER BY column0,column1 ASC"
|
||||
* @param array $orders
|
||||
* @return \phprs\ezsql\rules\basic\LimitRule
|
||||
*/
|
||||
public function orderByArgs($orders) {
|
||||
$this->impl->orderByArgs($this->context, $orders);
|
||||
return new LimitRule($this->context);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* orderBy('column') => "ORDER BY column"
|
||||
* orderBy('column', Sql::$ORDER_BY_ASC) => "ORDER BY column ASC"
|
||||
* orderBy('column0')->orderBy('column1') => "ORDER BY column0, column1"
|
||||
*
|
||||
* @param string $column
|
||||
* @param string $order Sql::$ORDER_BY_ASC or Sql::$ORDER_BY_DESC
|
||||
*
|
||||
* @return \phprs\ezsql\rules\basic\LimitRule
|
||||
*/
|
||||
public function orderBy($column, $order=null) {
|
||||
$this->impl->orderBy($this->context, $column, $order);
|
||||
return new LimitRule($this->context);
|
||||
}
|
||||
private $impl;
|
||||
}
|
||||
|
||||
class WhereRule extends OrderByRule
|
||||
{
|
||||
/**
|
||||
*
|
||||
* where('a=?', 1) => "WHERE a=1"
|
||||
* where('a=?', Sql::native('now()')) => "WHERE a=now()"
|
||||
* where('a IN (?)', [1, 2]) => "WHERE a IN (1,2)"
|
||||
*
|
||||
* @param string $expr
|
||||
* @param mixed $_
|
||||
* @return \phprs\ezsql\rules\basic\OrderByRule
|
||||
*/
|
||||
public function where($expr, $_= null) {
|
||||
WhereImpl::where($this->context, $expr, array_slice(func_get_args(), 1));
|
||||
return new OrderByRule($this->context);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* whereArgs([
|
||||
* 'a'=>1,
|
||||
* 'b'=>['IN'=>[1,2]]
|
||||
* 'c'=>['BETWEEN'=>[1,2]]
|
||||
* 'd'=>['<>'=>1]
|
||||
* ])
|
||||
*
|
||||
* =>
|
||||
* "WHERE a=1 AND b IN(1,2) AND c BETWEEN 1 AND 2 AND d<>1"
|
||||
* @param string $args
|
||||
* @return \phprs\ezsql\rules\basic\OrderByRule
|
||||
*/
|
||||
public function whereArgs($args) {
|
||||
WhereImpl::whereArgs($this->context, $args);
|
||||
return new OrderByRule($this->context);
|
||||
}
|
||||
}
|
||||
25
codes/agent/game/api/lib/phprs/ezsql/rules/delete.php
Normal file
25
codes/agent/game/api/lib/phprs/ezsql/rules/delete.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* $Id: delete.php 131 2015-10-10 02:25:57Z yangmin.cao $
|
||||
* @author caoym(caoyangmin@gmail.com)
|
||||
*/
|
||||
namespace phprs\ezsql\rules\delete;
|
||||
use phprs\ezsql\rules\basic\BasicRule;
|
||||
use phprs\ezsql\impls\DeleteImpl;
|
||||
use phprs\ezsql\rules\basic\WhereRule;
|
||||
|
||||
require_once dirname(__DIR__).'/impls.php';
|
||||
require_once __DIR__.'/basic.php';
|
||||
|
||||
class DeleteRule extends BasicRule
|
||||
{
|
||||
/**
|
||||
* deleteFrom('table') => "DELETE FROM table"
|
||||
* @param string $table
|
||||
* @return \phprs\ezsql\rules\basic\WhereRule
|
||||
*/
|
||||
public function deleteFrom($table) {
|
||||
DeleteImpl::deleteFrom($this->context, $table);
|
||||
return new WhereRule($this->context);
|
||||
}
|
||||
}
|
||||
42
codes/agent/game/api/lib/phprs/ezsql/rules/insert.php
Normal file
42
codes/agent/game/api/lib/phprs/ezsql/rules/insert.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* $Id: insert.php 131 2015-10-10 02:25:57Z yangmin.cao $
|
||||
* @author caoym(caoyangmin@gmail.com)
|
||||
*/
|
||||
namespace phprs\ezsql\rules\insert;
|
||||
|
||||
use phprs\ezsql\rules\basic\BasicRule;
|
||||
use phprs\ezsql\rules\basic\ExecRule;
|
||||
use phprs\ezsql\impls\InsertImpl;
|
||||
use phprs\ezsql\impls\ValuesImpl;
|
||||
|
||||
require_once dirname(__DIR__).'/impls.php';
|
||||
require_once __DIR__.'/basic.php';
|
||||
|
||||
class InsertRule extends BasicRule
|
||||
{
|
||||
/**
|
||||
*
|
||||
* insertInto('table')->values([1,2]) => "INSERT INTO table VALUES(1,2)"
|
||||
* @param string $table
|
||||
* @return \phprs\ezsql\rules\insert\ValuesRule
|
||||
*/
|
||||
public function insertInto($table) {
|
||||
InsertImpl::insertInto($this->context, $table);
|
||||
return new ValuesRule($this->context);
|
||||
}
|
||||
}
|
||||
class ValuesRule extends BasicRule
|
||||
{
|
||||
/**
|
||||
*
|
||||
* insertInto('table')->values([1,2]) => "INSERT INTO table VALUES(1,2)"
|
||||
* insertInto('table')->values(['a'=>1, 'b'=>Sql::native('now()')]) => "INSERT INTO table(a,b) VALUES(1,now())"
|
||||
* @param unknown $values
|
||||
* @return \phprs\ezsql\rules\basic\ExecRule
|
||||
*/
|
||||
public function values($values) {
|
||||
ValuesImpl::values($this->context, $values);
|
||||
return new ExecRule($this->context);
|
||||
}
|
||||
}
|
||||
36
codes/agent/game/api/lib/phprs/ezsql/rules/replace.php
Normal file
36
codes/agent/game/api/lib/phprs/ezsql/rules/replace.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
namespace phprs\ezsql\rules\replace;
|
||||
|
||||
use phprs\ezsql\rules\basic\BasicRule;
|
||||
use phprs\ezsql\rules\basic\ExecRule;
|
||||
use phprs\ezsql\impls\ReplaceImpl;
|
||||
use phprs\ezsql\impls\ValuesImpl;
|
||||
|
||||
require_once dirname(__DIR__).'/impls.php';
|
||||
require_once __DIR__.'/basic.php';
|
||||
|
||||
class ReplaceIntoRule extends BasicRule
|
||||
{
|
||||
/**
|
||||
* replaceInto('table')->values([1,2]) => "REPLACE INTO table VALUES(1,2)"
|
||||
* @param string $table
|
||||
* @return \phprs\ezsql\rules\replace\ValuesRule
|
||||
*/
|
||||
public function replaceInto($table) {
|
||||
ReplaceImpl::replaceInto($this->context, $table);
|
||||
return new ValuesRule($this->context);
|
||||
}
|
||||
}
|
||||
class ValuesRule extends BasicRule
|
||||
{
|
||||
/**
|
||||
* replaceInto('table')->values([1,2]) => "REPLACE INTO table VALUES(1,2)"
|
||||
* replaceInto('table')->values(['a'=>1, 'b'=>Sql::native('now()')]) => "REPLACE INTO table(a,b) VALUES(1,now())"
|
||||
* @param unknown $values
|
||||
* @return \phprs\ezsql\rules\basic\ExecRule
|
||||
*/
|
||||
public function values($values) {
|
||||
ValuesImpl::values($this->context, $values);
|
||||
return new ExecRule($this->context);
|
||||
}
|
||||
}
|
||||
271
codes/agent/game/api/lib/phprs/ezsql/rules/select.php
Normal file
271
codes/agent/game/api/lib/phprs/ezsql/rules/select.php
Normal file
@@ -0,0 +1,271 @@
|
||||
<?php
|
||||
/**
|
||||
* $Id: select.php 246 2015-10-21 04:48:09Z yangmin.cao $
|
||||
* @author caoym(caoyangmin@gmail.com)
|
||||
*/
|
||||
namespace phprs\ezsql\rules\select;
|
||||
use phprs\ezsql\rules\basic\BasicRule;
|
||||
use phprs\ezsql\impls\ExecImpl;
|
||||
use phprs\ezsql\impls\SelectImpl;
|
||||
use phprs\ezsql\impls\FromImpl;
|
||||
use phprs\ezsql\impls\JoinImpl;
|
||||
use phprs\ezsql\impls\JoinOnImpl;
|
||||
use phprs\ezsql\impls\WhereImpl;
|
||||
use phprs\ezsql\impls\GroupByImpl;
|
||||
use phprs\ezsql\impls\OrderByImpl;
|
||||
use phprs\ezsql\impls\LimitImpl;
|
||||
use phprs\ezsql\impls\ForUpdateOfImpl;
|
||||
use phprs\ezsql\impls\ForUpdateImpl;
|
||||
|
||||
require_once dirname(__DIR__).'/impls.php';
|
||||
require_once __DIR__.'/basic.php';
|
||||
|
||||
class SelectRule extends BasicRule
|
||||
{
|
||||
/**
|
||||
* select('column0, column1') => "SELECT column0, column1"
|
||||
* select('column0', 'column1') => "SELECT column0, column1"
|
||||
* @param string $columns
|
||||
* @return \phprs\ezsql\rules\select\FromRule
|
||||
*/
|
||||
public function select($columns) {
|
||||
SelectImpl::select($this->context, $columns);
|
||||
return new FromRule($this->context);
|
||||
}
|
||||
}
|
||||
|
||||
class GetRule extends BasicRule
|
||||
{
|
||||
/**
|
||||
* Execute sql and get responses
|
||||
* @param \PDO $db
|
||||
* @param $errExce whether throw exceptions
|
||||
* @return array
|
||||
*/
|
||||
public function get($db, $asDict=false,$errExce=true) {
|
||||
return ExecImpl::get($this->context, $db, $asDict,$errExce);
|
||||
}
|
||||
}
|
||||
class FromRule extends GetRule
|
||||
{
|
||||
/**
|
||||
* from('table') => "FROM table"
|
||||
* @param string $table
|
||||
* @return \phprs\ezsql\rules\select\JoinRule
|
||||
*/
|
||||
public function from($table){
|
||||
FromImpl::from($this->context, $table);
|
||||
return new JoinRule($this->context);
|
||||
}
|
||||
}
|
||||
class ForUpdateOfRule extends GetRule
|
||||
{
|
||||
/**
|
||||
* forUpdate()->of('column') => 'FOR UPDATE OF column'
|
||||
* @param string $column
|
||||
* @return \phprs\ezsql\rules\select\GetRule
|
||||
*/
|
||||
public function of($column){
|
||||
ForUpdateOfImpl::of($this->context, $column);
|
||||
return new GetRule($this->context);
|
||||
}
|
||||
}
|
||||
class ForUpdateRule extends GetRule
|
||||
{
|
||||
/**
|
||||
* forUpdate() => 'FOR UPDATE'
|
||||
* @return \phprs\ezsql\rules\select\ForUpdateOfRule
|
||||
*/
|
||||
public function forUpdate(){
|
||||
ForUpdateImpl::forUpdate($this->context);
|
||||
return new ForUpdateOfRule($this->context);
|
||||
}
|
||||
}
|
||||
|
||||
class LimitRule extends ForUpdateRule
|
||||
{
|
||||
/**
|
||||
* limit(0,1) => "LIMIT 0,1"
|
||||
* @param int $start
|
||||
* @param int $size
|
||||
* @return \phprs\ezsql\rules\select\ForUpdateRule
|
||||
*/
|
||||
public function limit($start, $size) {
|
||||
LimitImpl::limitWithOffset($this->context, $start, $size);
|
||||
return new ForUpdateRule($this->context);
|
||||
}
|
||||
}
|
||||
|
||||
class OrderByRule extends LimitRule
|
||||
{
|
||||
public function __construct($context){
|
||||
parent::__construct($context);
|
||||
$this->order = new OrderByImpl();
|
||||
}
|
||||
/**
|
||||
* orderBy('column') => "ORDER BY column"
|
||||
* orderBy('column', Sql::$ORDER_BY_ASC) => "ORDER BY column ASC"
|
||||
* orderBy('column0')->orderBy('column1') => "ORDER BY column0, column1"
|
||||
*
|
||||
* @param string $column
|
||||
* @param string $order Sql::$ORDER_BY_ASC or Sql::$ORDER_BY_DESC
|
||||
* @return \phprs\ezsql\rules\select\OrderByRule
|
||||
*/
|
||||
public function orderBy($column, $order=null) {
|
||||
$this->order->orderBy($this->context, $column, $order);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* orderByArgs(['column0', 'column1'=>Sql::$ORDER_BY_ASC]) => "ORDER BY column0,column1 ASC"
|
||||
* @param array $args
|
||||
* @return \phprs\ezsql\rules\select\OrderByRule
|
||||
*/
|
||||
public function orderByArgs($args) {
|
||||
$this->order->orderByArgs($this->context, $args);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* @var OrderByImpl
|
||||
*/
|
||||
private $order;
|
||||
}
|
||||
|
||||
class HavingRule extends OrderByRule
|
||||
{
|
||||
/**
|
||||
*
|
||||
* having('SUM(a)=?', 1) => "HAVING SUM(a)=1"
|
||||
* having('a>?', Sql::native('now()')) => "HAVING a>now()"
|
||||
* having('a IN (?)', [1, 2]) => "HAVING a IN (1,2)"
|
||||
*
|
||||
* @param string $expr
|
||||
* @param string $_
|
||||
* @return \phprs\ezsql\rules\select\OrderByRule
|
||||
*/
|
||||
public function having($expr, $_=null) {
|
||||
WhereImpl::having($this->context, $expr, array_slice(func_get_args(), 1));
|
||||
return new OrderByRule($this->context);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* havingArgs([
|
||||
* 'a'=>1,
|
||||
* 'b'=>['IN'=>[1,2]]
|
||||
* 'c'=>['BETWEEN'=>[1,2]]
|
||||
* 'd'=>['<>'=>1]
|
||||
* ])
|
||||
*
|
||||
* =>
|
||||
* "HAVING a=1 AND b IN(1,2) AND c BETWEEN 1 AND 2 AND d<>1"
|
||||
*
|
||||
*
|
||||
* @param array $args
|
||||
* @return \phprs\ezsql\rules\select\OrderByRule
|
||||
*/
|
||||
public function havingArgs($args) {
|
||||
WhereImpl::havingArgs($this->context, $args);
|
||||
return new OrderByRule($this->context);
|
||||
}
|
||||
}
|
||||
class GroupByRule extends OrderByRule
|
||||
{
|
||||
/**
|
||||
* groupBy('column') => "GROUP BY column"
|
||||
* @param string $column
|
||||
* @return \phprs\ezsql\rules\select\HavingRule
|
||||
*/
|
||||
public function groupBy($column) {
|
||||
GroupByImpl::groupBy($this->context, $column);
|
||||
return new HavingRule($this->context);
|
||||
}
|
||||
}
|
||||
class WhereRule extends GroupByRule
|
||||
{
|
||||
/**
|
||||
*
|
||||
* where('a=?', 1) => "WHERE a=1"
|
||||
* where('a=?', Sql::native('now()')) => "WHERE a=now()"
|
||||
* where('a IN (?)', [1, 2]) => "WHERE a IN (1,2)"
|
||||
*
|
||||
* @param string $expr
|
||||
* @param mixed $_
|
||||
* @return \phprs\ezsql\rules\select\GroupByRule
|
||||
*/
|
||||
public function where($expr, $_=null) {
|
||||
WhereImpl::where($this->context, $expr, array_slice(func_get_args(), 1));
|
||||
return new GroupByRule($this->context);
|
||||
}
|
||||
/**
|
||||
* whereArgs([
|
||||
* 'a'=>1,
|
||||
* 'b'=>['IN'=>[1,2]]
|
||||
* 'c'=>['BETWEEN'=>[1,2]]
|
||||
* 'd'=>['<>'=>1]
|
||||
* ])
|
||||
*
|
||||
* =>
|
||||
* "WHERE a=1 AND b IN(1,2) AND c BETWEEN 1 AND 2 AND d<>1"
|
||||
* @param array $args
|
||||
* @return\phprs\ezsql\rules\select\GroupByRule
|
||||
*/
|
||||
public function whereArgs($args) {
|
||||
WhereImpl::whereArgs($this->context,$args);
|
||||
return new GroupByRule($this->context);
|
||||
}
|
||||
}
|
||||
|
||||
class JoinRule extends WhereRule
|
||||
{
|
||||
/**
|
||||
* join('table1')->on('table0.id=table1.id') => "JOIN table1 ON table0.id=table1.id"
|
||||
* @param string $table
|
||||
* @return \phprs\ezsql\rules\select\JoinOnRule
|
||||
*/
|
||||
public function join($table){
|
||||
JoinImpl::join($this->context,null, $table);
|
||||
return new JoinOnRule($this->context);
|
||||
}
|
||||
/**
|
||||
* leftJoin('table1')->on('table0.id=table1.id') => "LEFT JOIN table1 ON table0.id=table1.id"
|
||||
* @param string $table
|
||||
* @return \phprs\ezsql\rules\select\JoinOnRule
|
||||
*/
|
||||
public function leftJoin($table){
|
||||
JoinImpl::join($this->context,'LEFT', $table);
|
||||
return new JoinOnRule($this->context);
|
||||
}
|
||||
/**
|
||||
* rightJoin('table1')->on('table0.id=table1.id') => "RIGHT JOIN table1 ON table0.id=table1.id"
|
||||
* @param string $table
|
||||
* @return \phprs\ezsql\rules\select\JoinOnRule
|
||||
*/
|
||||
public function rightJoin($table) {
|
||||
JoinImpl::join($this->context,'RIGHT', $table);
|
||||
return new JoinOnRule($this->context);
|
||||
}
|
||||
/**
|
||||
* innerJoin('table1')->on('table0.id=table1.id') => "INNER JOIN table1 ON table0.id=table1.id"
|
||||
* @param string $table
|
||||
* @return \phprs\ezsql\rules\select\JoinOnRule
|
||||
*/
|
||||
public function innerJoin($table) {
|
||||
JoinImpl::join($this->context,'INNER', $table);
|
||||
return new JoinOnRule($this->context);
|
||||
}
|
||||
}
|
||||
|
||||
class JoinOnRule extends BasicRule
|
||||
{
|
||||
/**
|
||||
* join('table1')->on('table0.id=table1.id') => "JOIN table1 ON table0.id=table1.id"
|
||||
* @param string $condition
|
||||
* @return \phprs\ezsql\rules\select\WhereRule
|
||||
*/
|
||||
public function on($condition){
|
||||
JoinOnImpl::on($this->context, $condition);
|
||||
return new JoinRule($this->context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
59
codes/agent/game/api/lib/phprs/ezsql/rules/update.php
Normal file
59
codes/agent/game/api/lib/phprs/ezsql/rules/update.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/**
|
||||
* $Id: update.php 131 2015-10-10 02:25:57Z yangmin.cao $
|
||||
* @author caoym(caoyangmin@gmail.com)
|
||||
*/
|
||||
namespace phprs\ezsql\rules\update;
|
||||
use phprs\ezsql\rules\basic\BasicRule;
|
||||
use phprs\ezsql\rules\basic\WhereRule;
|
||||
use phprs\ezsql\impls\UpdateSetImpl;
|
||||
use phprs\ezsql\impls\UpdateImpl;
|
||||
|
||||
|
||||
require_once dirname(__DIR__).'/impls.php';
|
||||
require_once __DIR__.'/basic.php';
|
||||
|
||||
class UpdateRule extends BasicRule
|
||||
{
|
||||
/**
|
||||
* update('table')->set('a', 1) => "UPDATE table SET a=1"
|
||||
* @param string $table
|
||||
* @return \phprs\ezsql\rules\update\UpdateSetRule
|
||||
*/
|
||||
public function update($table) {
|
||||
UpdateImpl::update($this->context, $table);
|
||||
return new UpdateSetRule($this->context);
|
||||
}
|
||||
}
|
||||
|
||||
class UpdateSetRule extends WhereRule
|
||||
{
|
||||
public function __construct($context){
|
||||
parent::__construct($context);
|
||||
$this->impl = new UpdateSetImpl();
|
||||
}
|
||||
/**
|
||||
* update('table')->set('a', 1) => "UPDATE table SET a=1"
|
||||
* update('table')->set('a', 1)->set('b',Sql::native('now()')) => "UPDATE table SET a=1,b=now()"
|
||||
* @param string $column
|
||||
* @param mixed $value
|
||||
* @return \phprs\ezsql\rules\update\UpdateSetRule
|
||||
*/
|
||||
public function set($column, $value) {
|
||||
$this->impl->set($this->context, $column, $value);
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* update('table')->set(['a'=>1, 'b'=>Sql::native('now()')]) => "UPDATE table SET a=1,b=now()"
|
||||
* @param array $values
|
||||
* @return \phprs\ezsql\rules\update\UpdateSetRule
|
||||
*/
|
||||
public function setArgs($values) {
|
||||
$this->impl->setArgs($this->context, $values);
|
||||
return $this;
|
||||
}
|
||||
private $impl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
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