今天感觉对ioc的掌握不是很好,所以决定实现一遍看看。
1.最基本的bind 和 make
容器类最少有两个方法,一个
bind
将类绑定容器中,一个make
从容器中取得这个类
bind()
$mysql = new mysql();
$Ioc::bind('db',$mysql);
当然上面的new操作也可以用闭包来实现,一个效果,闭包的好处在于这里的new不会立刻执行
$Ioc::bind('db',function($Ioc){
return new mysql();
});
make()
$mysql= Ioc::make('db');
2.最初版本的容器实现
根据上面的描述,那么最初的Ioc类差不多长这样,此处为了更加直白,所以本应该是static的方法被写成了动态
<?php
class Ioc{
function bind($alias,$concrete){
$this->$alias = $concrete;
}
function make($alias){
return $this->$alias;
}
}
有几个问题
- 所有服务要一个一个bind进去、能否根据配置文件自动bind
- 每次用make使用一个新服务都会重新new一次
- 组件无法注入依赖
- (继续第三点)组件是否可以自动注入依赖?
- 是否可以绑定一个闭包而不是实例来实现延迟加载。
3.Ioc初始化时候就把所有服务bind进去
此处可以用配置文件的方法,此处为了简单直接将配置放在构造函数中
class Ioc {
public function __construct(){
//这了只写了一个服务
$services = [
'db' => 'mysql'
];
foreach ($services as $alias => $concrete_name) {
$concrete = new $concrete_name;
$this->bind($alias,$concrete);
}
}
function bind(){
... //跟上面一样
}
function make(){
... //跟上面一样
}
}
class mysql {
function __construct(){
echo "具体实现就不写了,这应该是个pdo的二次封装";
}
}
$Ioc = new Ioc();
$Ioc->make('db');
4.用闭包的方法重写bind方法
将$concrete
处理成一个返回实例的闭包,并将ioc容器实例注入到每个闭包中。
function bind($abstract,$concrete,$share){
// $this->$alias = $concrete;
if (! $concrete instanceof Closure) {
$concrete = $this->getClosure($abstract,$concrete);
}
$this->bindings[$abstract] = compact('concrete','shared');
}!
function getClosure($abstract,$concrete){
//$c为Ioc容器对象
return function($c) use ($abstract,$concrete){
$method = ($abstract == $concrete) ? 'build' : 'make';
return $c->$method($concrete);
};
}
5.让组件内部可以使用容器的其他服务
重写make
方法,判断是否符合isBuildable
的条件,要么$concrete == $abstract
,要么$concrete
本身已经是闭包,如果不符合那么则调用build
方法使之符合isBuildable
。
//生成实例对象 解决接口和实例化类之间依赖关系
public function make($abstract){
// return $this->$abstract;
$concrete = $this->getConcrete($abstract);
if ($this->isBuildable($concrete,$abstract)){
$object = $this->build($concrete);
} else {
$object = $this->make($concrete);
}
return $object;
}
public function isBuildable($concrete,$abstract){
return $concrete == $abstract || $concrete instanceof Closure;
}
build
使用反射获取构造函数的依赖关系,并调用getPendencies
获取依赖实例。
public function build($concrete){
if ($concrete instanceof Closure){
//Ioc容器实例注入
return $concrete($this);
}
//反射对象
$reflector = new ReflectionClass($concrete);
//检查这个类是否可实例化
if (!$reflector->isInstantiable()){
echo $message = "Target [$concrete] is not instantiable.";
}
$constructor = $reflector->getConstructor();
if (is_null($constructor)){
return new $constructor;
}
$dependencies = $constructor->getParameters();
$instances = $this->getPendencies($dependencies);
return $reflector->newInstanceArgs($instances);
}
getDependencies 实现
public function getDependencies($parameters){
$dependencies = [];
foreach ($dependencies as $parameter) {
$denpendency = $parameter->getClass();
if (is_null($denpendency)){
$denpendencies[] = NULL;
} else {
$dependencies[] = $this->resolveClass($parameter);
}
}
return (array)$dependencies;
}
public function resolveClass(ReflectionParameter $parameter){
return $this->make($parameter->getClass()->name);
}
6.将单例的服务单独bind
使用反射自动bind
使用facade