321 lines
9.9 KiB
PHP
321 lines
9.9 KiB
PHP
<?php
|
||
// +----------------------------------------------------------------------
|
||
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
|
||
// +----------------------------------------------------------------------
|
||
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
|
||
// +----------------------------------------------------------------------
|
||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||
// +----------------------------------------------------------------------
|
||
// | Author: liu21st <liu21st@gmail.com>
|
||
// +----------------------------------------------------------------------
|
||
|
||
namespace think;
|
||
|
||
use Closure;
|
||
use InvalidArgumentException;
|
||
use ReflectionClass;
|
||
use ReflectionFunction;
|
||
use ReflectionMethod;
|
||
|
||
class Container
|
||
{
|
||
/**
|
||
* 容器对象实例
|
||
* @var Container
|
||
*/
|
||
protected static $instance;
|
||
|
||
/**
|
||
* 容器中的对象实例
|
||
* @var array
|
||
*/
|
||
protected $instances = [];
|
||
|
||
/**
|
||
* 容器绑定标识
|
||
* @var array
|
||
*/
|
||
protected $bind = [];
|
||
|
||
/**
|
||
* 获取当前容器的实例(单例)
|
||
* @access public
|
||
* @return static
|
||
*/
|
||
public static function getInstance()
|
||
{
|
||
if (is_null(static::$instance)) {
|
||
static::$instance = new static;
|
||
}
|
||
|
||
return static::$instance;
|
||
}
|
||
|
||
/**
|
||
* 获取容器中的对象实例
|
||
* @access public
|
||
* @param string $abstract 类名或者标识
|
||
* @param array|true $vars 变量
|
||
* @param bool $newInstance 是否每次创建新的实例
|
||
* @return object
|
||
*/
|
||
public static function get($abstract, $vars = [], $newInstance = false)
|
||
{
|
||
return static::getInstance()->make($abstract, $vars, $newInstance);
|
||
}
|
||
|
||
/**
|
||
* 绑定一个类、闭包、实例、接口实现到容器
|
||
* @access public
|
||
* @param string $abstract 类标识、接口
|
||
* @param mixed $concrete 要绑定的类、闭包或者实例
|
||
* @return Container
|
||
*/
|
||
public static function set($abstract, $concrete = null)
|
||
{
|
||
return static::getInstance()->bind($abstract, $concrete);
|
||
}
|
||
|
||
/**
|
||
* 绑定一个类、闭包、实例、接口实现到容器
|
||
* @access public
|
||
* @param string|array $abstract 类标识、接口
|
||
* @param mixed $concrete 要绑定的类、闭包或者实例
|
||
* @return $this
|
||
*/
|
||
public function bind($abstract, $concrete = null)
|
||
{
|
||
if (is_array($abstract)) {
|
||
$this->bind = array_merge($this->bind, $abstract);
|
||
} elseif ($concrete instanceof Closure) {
|
||
$this->bind[$abstract] = $concrete;
|
||
} elseif (is_object($concrete)) {
|
||
$this->instances[$abstract] = $concrete;
|
||
} else {
|
||
$this->bind[$abstract] = $concrete;
|
||
}
|
||
|
||
return $this;
|
||
}
|
||
|
||
/**
|
||
* 绑定一个类实例当容器
|
||
* @access public
|
||
* @param string $abstract 类名或者标识
|
||
* @param object $instance 类的实例
|
||
* @return $this
|
||
*/
|
||
public function instance($abstract, $instance)
|
||
{
|
||
if (isset($this->bind[$abstract])) {
|
||
$abstract = $this->bind[$abstract];
|
||
}
|
||
|
||
$this->instances[$abstract] = $instance;
|
||
|
||
return $this;
|
||
}
|
||
|
||
/**
|
||
* 判断容器中是否存在类及标识
|
||
* @access public
|
||
* @param string $abstract 类名或者标识
|
||
* @return bool
|
||
*/
|
||
public function bound($abstract)
|
||
{
|
||
return isset($this->bind[$abstract]) || isset($this->instances[$abstract]);
|
||
}
|
||
|
||
/**
|
||
* 判断容器中是否存在类及标识
|
||
* @access public
|
||
* @param string $name 类名或者标识
|
||
* @return bool
|
||
*/
|
||
public function has($name)
|
||
{
|
||
return $this->bound($name);
|
||
}
|
||
|
||
/**
|
||
* 创建类的实例
|
||
* @access public
|
||
* @param string $abstract 类名或者标识
|
||
* @param array|true $args 变量
|
||
* @param bool $newInstance 是否每次创建新的实例
|
||
* @return object
|
||
*/
|
||
public function make($abstract, $vars = [], $newInstance = false)
|
||
{
|
||
if (true === $vars) {
|
||
// 总是创建新的实例化对象
|
||
$newInstance = true;
|
||
$vars = [];
|
||
}
|
||
|
||
if (isset($this->instances[$abstract]) && !$newInstance) {
|
||
$object = $this->instances[$abstract];
|
||
} else {
|
||
if (isset($this->bind[$abstract])) {
|
||
$concrete = $this->bind[$abstract];
|
||
|
||
if ($concrete instanceof Closure) {
|
||
$object = $this->invokeFunction($concrete, $vars);
|
||
} else {
|
||
$object = $this->make($concrete, $vars, $newInstance);
|
||
}
|
||
} else {
|
||
$object = $this->invokeClass($abstract, $vars);
|
||
}
|
||
|
||
if (!$newInstance) {
|
||
$this->instances[$abstract] = $object;
|
||
}
|
||
}
|
||
|
||
return $object;
|
||
}
|
||
|
||
/**
|
||
* 执行函数或者闭包方法 支持参数调用
|
||
* @access public
|
||
* @param string|array|\Closure $function 函数或者闭包
|
||
* @param array $vars 变量
|
||
* @return mixed
|
||
*/
|
||
public function invokeFunction($function, $vars = [])
|
||
{
|
||
//$vars 带入的参数
|
||
$reflect = new ReflectionFunction($function);
|
||
//整理参数
|
||
$args = $this->bindParams($reflect, $vars);
|
||
// 调用(执行)反射的方法并将其参数作为数组传递
|
||
return $reflect->invokeArgs($args);
|
||
}
|
||
|
||
/**
|
||
* 调用反射执行类的方法 支持参数绑定
|
||
* @access public
|
||
* @param string|array $method 方法
|
||
* @param array $vars 变量
|
||
* @return mixed
|
||
*/
|
||
public 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
|
||
*/
|
||
public function invoke($callable, $vars = [])
|
||
{
|
||
// $callable 数组或者字符串,匿名函数,或者类命名空间 或者静态调用方法
|
||
// $vars 传递的参数
|
||
//是否为闭包,闭包则执行函数,否则执行类方法
|
||
if ($callable instanceof Closure) {
|
||
//执行闭包函数
|
||
$result = $this->invokeFunction($callable, $vars);
|
||
|
||
} else {
|
||
//执行类 方法操作
|
||
$result = $this->invokeMethod($callable, $vars);
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
|
||
/**
|
||
* 调用反射执行类的实例化 支持依赖注入
|
||
* @access public
|
||
* @param string $class 类名
|
||
* @param array $vars 变量
|
||
* @return mixed
|
||
*/
|
||
public function invokeClass($class, $vars = [])
|
||
{
|
||
//给定命名空间或者类名,返回对应的实例对象
|
||
// $vars 实例时需要传递的值
|
||
$reflect = new ReflectionClass($class);
|
||
// 获取类的构造函数,返回类命名空间以及构造函数
|
||
$constructor = $reflect->getConstructor();
|
||
|
||
if ($constructor) {
|
||
//处理传递的参数
|
||
$args = $this->bindParams($constructor, $vars);
|
||
} else {
|
||
$args = [];
|
||
}
|
||
|
||
//返回创建的新实例 从给出的参数创建一个新的类实例
|
||
return $reflect->newInstanceArgs($args);
|
||
}
|
||
|
||
/**
|
||
* 绑定参数
|
||
* @access protected
|
||
* @param \ReflectionMethod|\ReflectionFunction $reflect 反射类
|
||
* @param array $vars 变量
|
||
* @return array
|
||
*/
|
||
protected function bindParams($reflect, $vars = [])
|
||
{
|
||
$args = [];
|
||
|
||
// $reflect 代表ReflectionFunction 或者 ReflectionMethod 实例
|
||
// getNumberOfParameters() 获取的是函数或者方法需要的参数个数 如:function($1,$2,$3)
|
||
if ($reflect->getNumberOfParameters() > 0) {
|
||
// 判断数组类型 数字数组时按顺序绑定参数
|
||
// 将数组的内部指针指向第一个单元
|
||
reset($vars);
|
||
$type = key($vars) === 0 ? 1 : 0;
|
||
|
||
//获取函数或者方法内 参数额变量名
|
||
$params = $reflect->getParameters();
|
||
|
||
foreach ($params as $param) {
|
||
//变量名称
|
||
$name = $param->getName();
|
||
//所属类
|
||
$class = $param->getClass();
|
||
|
||
//判断类是否有值
|
||
if ($class) {
|
||
//获取类的命名空间名称
|
||
$className = $class->getName();
|
||
$args[] = $this->make($className);
|
||
} elseif (1 == $type && !empty($vars)) {
|
||
//删除数组中的第一个元素
|
||
$args[] = array_shift($vars);
|
||
} elseif (0 == $type && isset($vars[$name])) {
|
||
$args[] = $vars[$name];
|
||
} elseif ($param->isDefaultValueAvailable()) {
|
||
$args[] = $param->getDefaultValue();
|
||
} else {
|
||
throw new InvalidArgumentException('method param miss:' . $name);
|
||
}
|
||
}
|
||
}
|
||
|
||
return $args;
|
||
}
|
||
|
||
}
|