仿照laravel实现一个最简容器 2
一、闭包实现bind
由此我们实现了容器的核心功能
<?php
class Ioc{
protected $bindings = [];
function bind($abstract,$concrete = null,$shared = 0){
echo "step bind abstract : [$abstract] <br/>";
if (!$concrete instanceof Closure){
$concrete = $this->getClosure($abstract,$concrete);
}
// $this->bindings[$abstract] = compact($concrete,$share);
$this->bindings[$abstract] = compact('concrete','shared');
}
function getClosure($abstract,$concrete){
return function ($c) use ($abstract,$concrete){
echo "step getClosure abstract : [$abstract] , concrete : [$concrete] <br/>";
$method = ($abstract == $concrete) ? 'build' : 'make';
return $c->$method($concrete);
};
}
function make($abstract){
$concrete = $this->getConcrete($abstract);
echo "step make abstract : [$abstract] , abstract : [$abstract] <br/>";
if ($this->isBuildable($concrete,$abstract)){
$object = $this->build($concrete);
} else {
$object = $this->make($concrete);
}
return $object;
}
public function getConcrete($abstract){
echo "step getConcrete abstract : [$abstract] <br/>";
if (isset($this->bindings[$abstract])){
// var_dump($this->bindings[$abstract]['concrete']);
// 这里会返回bind的时候绑定的闭包,但是闭包未被调用
return $this->bindings[$abstract]['concrete'];
}
return $abstract;
}
public function isBuildable($concrete,$abstract){
return $abstract === $concrete || $concrete instanceof Closure;
}
function build($concrete){
if ($concrete instanceof Closure){
echo "step build <br/>";
} else {
echo "step build concrete : [$concrete] <br/>";
}
if ($concrete instanceof Closure){
//到这里getClosure 才第一次被执行
return $concrete($this);
}
//下面的情况应该是对应 $abstract == $concrete
$reflector = new ReflectionClass($concrete);
if (! $reflector->isInstantiable()){
echo $message = "target [$concrete] is not isInstantiable";
}
$constructor = $reflector->getConstructor();
if (is_null($constructor)){
return new $concrete;
}
$dependencies = $constructor->getParameters();
$instances = $this->getDependencies($dependencies);
return $reflector->newInstanceArgs($instances);
}
function getDependencies($parameters){
$dependencies = [];
foreach ($parameters as $parameter) {
$dependency = $parameter->getClass();
if (is_null($dependency)){
$dependencies[] = null;
} else {
$dependencies[] = $this->resolveClass($parameter);
}
}
return (array)$dependencies;
}
function resolveClass( ReflectionParameter $parameter){
return $this->make($parameter->getClass()->name);
}
}
interface Tool{
function go();
}
class Train implements Tool{
function go(){
echo "drive to tibet";
}
}
$app = new ioc();
$app->bind('tool','Train');
$tool = $app->make('tool');
二、分析下执行流程
上面的代码执行之后会得到如下结果
step bind abstract : [tool]
step getConcrete abstract : [tool]
step make abstract : [tool] , abstract : [tool]
step build
step getClosure abstract : [tool] , concrete : [Train]
step getConcrete abstract : [Train]
step make abstract : [Train] , abstract : [Train]
step build concrete : [Train]
1.首先是Bind()
我们bind的是字符串,所以会调用getClosure
,
return function ($c) use ($abstract,$concrete){
$method = ($abstract == $concrete) ? 'build' : 'make';
return $c->$method($concrete);
};
但是getClosure
返回的是一个闭包,闭包函数不会被立刻调用,所以我们暂时不知道$c
这个参数是什么,但是我们知道$abstrac
,$concrete
分别对应'tool'
和'Train'
这两个字符串
2.然后是第一次make()
这是第一次调用make()
,参数的值应该是'tool'
,getConcrete()
会返回之前bind()
的那个闭包,此处闭包仍然没有被执行。
isBuildable()
返回true
,于是我们来到第一次build()
,传入的参数是之前bind()
的那个闭包,if ($concrete instanceof Closure)
会返回true
,于是执行了这一句
return $concrete($this);
闭包终于被执行了,还记得第一步中bind()
的那几个参数吗,闭包中的
return $c->$method($concrete);
就等效于
return $this->make('Train');
3.之后就是第二次make()
这次就简单了,getConcrete()
会返回'Train'
,isBuildable()
返回true
,于是我们来到第二次build()
,传入的参数是'Train'
4.使用反射来搞定依赖注入
在build()
中可以看到,因为Train
这个类没有构造函数,所以直接new
了一个出来并返回,如果Train
这个类有构造函数,它会根据构造函数中的参数去容器中再次make()
并注入到Train
里面。