325 lines
7.0 KiB
PHP
325 lines
7.0 KiB
PHP
<?php
|
|
/**
|
|
* The following code, none of which has BUG.
|
|
*
|
|
* @author: BD<liuxingwu@duoguan.com>
|
|
* @date: 2019/10/12 17:03
|
|
*/
|
|
|
|
namespace xin\container;
|
|
|
|
use Psr\Container\ContainerExceptionInterface;
|
|
use Psr\Container\ContainerInterface;
|
|
use Psr\Container\NotFoundExceptionInterface;
|
|
use xin\helper\Str;
|
|
|
|
abstract class Container implements ContainerInterface{
|
|
|
|
/**
|
|
* 绑定列表
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $binds = [];
|
|
|
|
/**
|
|
* 实现器
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $implementers = [];
|
|
|
|
/**
|
|
* 实例列表
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $instances = [];
|
|
|
|
/**
|
|
* 获取对象类型的参数值
|
|
*
|
|
* @access protected
|
|
* @param string $className 类名
|
|
* @param array $vars 参数
|
|
* @return mixed
|
|
*/
|
|
protected function getObjectParam($className, &$vars){
|
|
$array = $vars;
|
|
$value = array_shift($array);
|
|
|
|
if($value instanceof $className){
|
|
$result = $value;
|
|
array_shift($vars);
|
|
}else{
|
|
$result = $this->get($className);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* 绑定参数
|
|
*
|
|
* @access protected
|
|
* @param \ReflectionMethod|\ReflectionFunction $reflect 反射类
|
|
* @param array $vars 参数
|
|
* @return array
|
|
* @throws \ReflectionException
|
|
*/
|
|
protected function bindParams($reflect, $vars = []){
|
|
if($reflect->getNumberOfParameters() == 0){
|
|
return [];
|
|
}
|
|
|
|
// 判断数组类型 数字数组时按顺序绑定参数
|
|
reset($vars);
|
|
$type = key($vars) === 0 ? 1 : 0;
|
|
|
|
// 获取函数和类方法的参数列表
|
|
$params = $reflect->getParameters();
|
|
|
|
$args = [];
|
|
foreach($params as $param){
|
|
$class = $param->getClass();
|
|
|
|
$name = $param->getName();
|
|
$lowerName = Str::snake($name, '_', false);
|
|
|
|
if($class){
|
|
$args[] = $this->getObjectParam($class->getName(), $vars);
|
|
}elseif(1 == $type && !empty($vars)){
|
|
$args[] = array_shift($vars);
|
|
}elseif(0 == $type && isset($vars[$name])){
|
|
$args[] = $vars[$name];
|
|
}elseif(0 == $type && isset($vars[$lowerName])){
|
|
$args[] = $vars[$lowerName];
|
|
}elseif($param->isDefaultValueAvailable()){
|
|
$args[] = $param->getDefaultValue();
|
|
}else{
|
|
throw new \InvalidArgumentException('method param miss:'.$name);
|
|
}
|
|
}
|
|
|
|
return $args;
|
|
}
|
|
|
|
/**
|
|
* 调用反射执行类的实例化 支持依赖注入
|
|
*
|
|
* @access public
|
|
* @param string $class 类名
|
|
* @param array $vars 参数
|
|
* @return mixed
|
|
* @throws \ReflectionException
|
|
*/
|
|
public function invokeClass($class, $vars = []){
|
|
$reflect = new \ReflectionClass($class);
|
|
|
|
if($reflect->hasMethod('__make')){
|
|
$method = new \ReflectionMethod($class, '__make');
|
|
|
|
if($method->isPublic() && $method->isStatic()){
|
|
$args = $this->bindParams($method, $vars);
|
|
return $method->invokeArgs(null, $args);
|
|
}
|
|
}
|
|
|
|
$constructor = $reflect->getConstructor();
|
|
|
|
$args = $constructor ? $this->bindParams($constructor, $vars) : [];
|
|
|
|
return $reflect->newInstanceArgs($args);
|
|
}
|
|
|
|
/**
|
|
* 调用函数
|
|
*
|
|
* @param callable $function
|
|
* @param array $vars
|
|
* @return mixed
|
|
* @throws \ReflectionException
|
|
*/
|
|
protected function invokeFunction($function, $vars = []){
|
|
$reflect = new \ReflectionFunction($function);
|
|
|
|
$args = $this->bindParams($reflect, $vars);
|
|
|
|
return call_user_func_array($function, $args);
|
|
}
|
|
|
|
/**
|
|
* 调用反射执行类的方法 支持参数绑定
|
|
*
|
|
* @access public
|
|
* @param mixed $method 方法
|
|
* @param array $vars 参数
|
|
* @return mixed
|
|
* @throws \ReflectionException
|
|
*/
|
|
protected function invokeMethod($method, $vars = []){
|
|
if(is_array($method)){
|
|
$class = is_object($method[0]) ? $method[0] : $this->invokeClass($method[0]);
|
|
$reflect = new \ReflectionMethod($class, $method[1]);
|
|
}else{
|
|
// 静态方法
|
|
$reflect = new \ReflectionMethod($method);
|
|
}
|
|
|
|
$args = $this->bindParams($reflect, $vars);
|
|
|
|
return $reflect->invokeArgs(isset($class) ? $class : null, $args);
|
|
}
|
|
|
|
/**
|
|
* 调用反射执行callable 支持参数绑定
|
|
*
|
|
* @access public
|
|
* @param mixed $callable
|
|
* @param array $vars 参数
|
|
* @return mixed
|
|
* @throws \ReflectionException
|
|
*/
|
|
public function invoke($callable, $vars = []){
|
|
if($callable instanceof \Closure){
|
|
return $this->invokeFunction($callable, $vars);
|
|
}
|
|
|
|
return $this->invokeMethod($callable, $vars);
|
|
}
|
|
|
|
/**
|
|
* 调用反射执行类的方法 支持参数绑定
|
|
*
|
|
* @access public
|
|
* @param object $instance 对象实例
|
|
* @param mixed $reflect 反射类
|
|
* @param array $vars 参数
|
|
* @return mixed
|
|
* @throws \ReflectionException
|
|
*/
|
|
public function invokeReflectMethod($instance, $reflect, $vars = []){
|
|
$args = $this->bindParams($reflect, $vars);
|
|
return $reflect->invokeArgs($instance, $args);
|
|
}
|
|
|
|
/**
|
|
* 多实例绑定
|
|
*
|
|
* @param string $id
|
|
* @param mixed $implementer
|
|
* @return $this
|
|
*/
|
|
public function bind($id, $implementer){
|
|
$this->binds[$id] = 0;
|
|
$this->implementers[$id] = $implementer;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 单实例绑定
|
|
*
|
|
* @param string $id
|
|
* @param mixed $implementer
|
|
* @return $this
|
|
*/
|
|
public function singleton($id, $implementer){
|
|
$this->binds[$id] = 1;
|
|
$this->implementers[$id] = $implementer;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* 创建实例
|
|
*
|
|
* @param string $id
|
|
* @return mixed
|
|
* @throws \ReflectionException
|
|
*/
|
|
public function make($id){
|
|
$bind = $this->binds[$id];
|
|
$impl = &$this->implementers[$id];
|
|
|
|
if(is_array($impl) || is_callable($impl)){
|
|
$instance = $this->invoke($impl);
|
|
}else{
|
|
$instance = $this->invokeClass($impl);
|
|
}
|
|
|
|
if($bind === 1){
|
|
$this->instances[$id] = $instance;
|
|
}
|
|
|
|
return $instance;
|
|
}
|
|
|
|
/**
|
|
* Finds an entry of the container by its identifier and returns it.
|
|
*
|
|
* @param string $id Identifier of the entry to look for.
|
|
* @return mixed Entry.
|
|
* @throws ContainerExceptionInterface Error while retrieving the entry.
|
|
* @throws NotFoundExceptionInterface No entry was found for **this** identifier.
|
|
*/
|
|
public function get($id){
|
|
if(!isset($this->binds[$id])){
|
|
throw new NotFoundException("未注册的依赖:{$id}");
|
|
}
|
|
|
|
$bind = &$this->binds[$id];
|
|
if($bind === 0){
|
|
try{
|
|
return $this->make($id);
|
|
}catch(\ReflectionException $e){
|
|
throw new ContainerException($e->getMessage(), $e->getCode(), $e);
|
|
}
|
|
}
|
|
|
|
if(!isset($this->instances[$id])){
|
|
try{
|
|
$this->make($id);
|
|
}catch(\ReflectionException $e){
|
|
throw new ContainerException($e->getMessage(), $e->getCode(), $e);
|
|
}
|
|
}
|
|
|
|
return $this->instances[$id];
|
|
}
|
|
|
|
/**
|
|
* Returns true if the container can return an entry for the given identifier.
|
|
* Returns false otherwise.
|
|
* `has($id)` returning true does not mean that `get($id)` will not throw an exception.
|
|
* It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`.
|
|
*
|
|
* @param string $id Identifier of the entry to look for.
|
|
* @return bool
|
|
*/
|
|
public function has($id){
|
|
return isset($this->binds[$id]);
|
|
}
|
|
|
|
/**
|
|
* 获取一个属性
|
|
*
|
|
* @param string $id
|
|
* @return mixed
|
|
*/
|
|
public function __get($id){
|
|
return $this->get($id);
|
|
}
|
|
|
|
/**
|
|
* 设置一个属性
|
|
*
|
|
* @param string $id
|
|
* @param mixed $value
|
|
*/
|
|
public function __set($id, $value){
|
|
$this->singleton($id, $value);
|
|
}
|
|
|
|
}
|