thinkphp/thinkphp/library/think/Container.php

321 lines
9.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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;
}
}