<?php
namespace App\Ioc;
use Closure;
use ReflectionClass;
use ReflectionParameter;
/**
* 容器类用于装实例和提供实例的回调函数
* Class Container 容器类
* @package App\Ioc
*/
class Container {
//没有用的变量,调试使用
public static $debug_count = 0;
//声明数组,用于装实例的回调函数,真正的容器还会装实例等其他内容 从而实现单例等高级功能
protected $bindings = [];
//绑定接口 和 生成相应实例的回调函数
public function bind( $abstract , $concrete = null , $shared = false) {
Container::$debug_count++;
echo '步骤:'.Container::$debug_count.',进入"bind(绑定)"方法:';
echo '$abstract="'.$abstract.'" , ';
echo $concrete instanceof Closure?'$concrete 是一个回调函数 ': '$concrete不是回调函数:$concrete="'.$concrete.'" ';
echo '<br>';
echo '判断:当前$concrete是否已经是回调函数,如果是直接绑定,如果不是则调用getClosure获取闭包';
echo '<br>';
//判断当前$concrete是否已经是回调函数,如果是直接绑定,如果不是则调用getClosure获取回调函数后再绑定,
//注意:getClosure获取的回调函数不是直接可以使用功能的回调函数,这里面还要去make或者build能使用正常功能的回调函数。
if (!$concrete instanceof Closure) {
echo '当前$concrete不是闭包函数,即将进入getClosure函数生成闭包 <br>';
$concrete = $this->getClosure($abstract, $concrete);
}
echo '$concrete已是闭包,存入$bindings数组 <br>';
//先将concrete 和 shared 组合为数组,然后又将数组存入 bindings[$abstract]数组。
$this->bindings[$abstract] = compact('concrete', 'shared');
}
//默认生成实例的回调函数
protected function getClosure($abstract, $concrete) {
Container::$debug_count++;
echo '步骤:'.Container::$debug_count.',进入到"getClosure(获取闭包函数)"方法';
echo '$abstract="'.$abstract.'" , ';
echo '$concrete="'.$concrete.'" , ';
echo '将\'带有make/build的\'回调函数绑定到$abstract';
echo '<br>';
echo '返回闭包函数<br>';
//到这里绑定的动作完成。 等make的时候才会调用函数体里面
return function ($c) use ($abstract, $concrete) {
Container::$debug_count++;
echo '步骤:'.Container::$debug_count.' 进入"闭包函数里面记录:"';
echo '$abstract="'.$abstract.'"';
echo '$concrete="'.$concrete.'"';
echo '<br>';
$method = ($abstract == $concrete) ? 'build' : 'make';
echo '即将调用$container的函数:$container->"'.$method.'"('.$concrete.')';
echo '<br>';
//如果$abstract和$concrete相等调用: $c->build($concrete) , 不相等调用:$c->make($concrete)
return $c -> $method($concrete);
};
}
/**************************绑定的时候调用上面的函数,要往外取出实例的时候调用下面的函数************************/
public function make($abstract)
{
echo '进入make方法:$abstract别名='.$abstract.' , make('.$abstract.')';
echo '<br>';
echo '去获取闭包函数 调用 ->getConcrete()';
echo '<br>';
//获取绑定的闭包函数
$concrete = $this->getConcrete($abstract);
//判断是用build还是用make 如果$concrete===$abstract或者$concrete是闭包函数就用build
if ($this->isBuildable($concrete, $abstract)) {
echo '($concrete是闭包 或者 $concrete===$abstract). 在make方法里使用build';
echo $concrete instanceof Closure?' <br> $concrete是一个闭包函数 <br>': '<br> $concrete="'.$concrete.'" <br>';
$object = $this->build($concrete);
} else {
echo '$concrete不是闭包,$concrete和$abstract也不相等,在make方法里使用make<br>';
$object = $this->make($concrete);
}
return $object;
}
protected function isBuildable($concrete, $abstract)
{
return $concrete === $abstract || $concrete instanceof Closure;
}
//获取回调函数里面 make/build 后的实现
protected function getConcrete($abstract)
{
//判断实现的时候判断一下有没有绑定到bindings数组里。如果没有绑直接返回$abstract
if (!isset($this->bindings[$abstract])) {
echo '没有获取到闭包函数,直接返回:'.$abstract;
echo '<br>';
return $abstract;
}
echo ' getConcrete方法里 获取到闭包函数'.' 返回闭包 <br >';
return $this->bindings[$abstract]['concrete'];
}
//实例化对象
public function build($concrete)
{
Container::$debug_count++;
echo '步骤:'.Container::$debug_count.' 进入build,首先判断$concrete是不是闭包,是闭包就调用自己。<br>';
//判断$concrete是不是闭包,如果是闭包函数再递归调用make方法,直到$abstract是类名位置
if ($concrete instanceof Closure) {
echo '是闭包,调用自己<br>';
return $concrete($this);
}
echo '不是闭包,创建反射类<br>';
try{
echo '创建'.$concrete.'的反射类<br>';
$reflector = new ReflectionClass($concrete);
//判断$concrete类是否可实例化
if (!$reflector->isInstantiable()) {
echo $message = "Target [$concrete] is not instantiable";
}
//获取类的构造函数
$constructor = $reflector->getConstructor();
if (is_null($constructor)) {
//如果$concrete类没有构造函数直接在这里new 然后返回实例
echo '要实例化的类没有构造函数。直接new 返回<br>';
return new $concrete;
}
echo '要实例化的类有构造函数,要去获取依赖 <br>';
//如果$concrete类有构造函数先获取构造函数的参数
$dependencies = $constructor->getParameters();
//获取参数的依赖,比如用没用其他类对象作为参数传递
$instances = $this->getDependencies($dependencies);
return $reflector->newInstanceArgs($instances);
}catch (\ReflectionException $exception){
echo "异常<br>".$exception->getMessage()."<br>";
}
}
//解决通过反射机制实例化对象时的依赖
protected function getDependencies($parameters)
{
Container::$debug_count++;
echo '步骤'.Container::$debug_count.' 获取依赖 <br>';
$dependencies = [];
//循环依赖类 有几个依赖的类就循环几次
foreach ($parameters as $parameter) {
//获取依赖的类
$dependency = $parameter->getClass();
if (is_null($dependency)) {
$dependencies[] = NULL;
} else {
//如果依赖了哪些类,就先实例化这些类的对象,存入数组,new实例的时候要作为参数传递
$dependencies[] = $this->resolveClass($parameter);
}
}
//返回依赖类的对象些
return (array)$dependencies;
}
/**
* 创建实例时需要检测依赖,如果有依赖的类,就会先创建依赖类的对象。这里就是创建依赖类的对象
* @param ReflectionParameter $parameter
* @return mixed|object
*/
protected function resolveClass(ReflectionParameter $parameter) {
//$parameter->getClass()->name,就是类型提示类 ,就是要实例化类的构造函数里的那个参数类的名字
return $this->make($parameter->getClass()->name);
}
}
非特殊说明,本文版权归 陈阳的博客 所有,转载请注明出处.