添加后台代理代码
This commit is contained in:
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(); // 正在创建的类,用于检测循环依赖
|
||||
}
|
||||
Reference in New Issue
Block a user