经过实现laravel 框架功能,以便深刻理解laravel框架的先进思想。php
服务容器是用来管理类依赖与运行依赖注入的工具。Laravel框架中就是使用服务容器来实现 控制反转 和 依赖注入 。laravel
控制反转(IoC) 就是说把建立对象的 控制权 进行转移,之前建立对象的主动权和建立时机是由本身把控的,而如今这种权力转移到第三方,也就是 Laravel 中的容器。闭包
依赖注入(DI)则是帮助容器实如今运行中动态的为对象提供提依赖的资源。app
概念容易不太容易让人理解,举个栗子:框架
//咱们构建一我的的类和一个狗的类 class People { public $dog = null; public function __construct() { $this->dog = new Dog(); } public function putDog(){ return $this->dog->dogCall(); } } class Dog{ public function dogCall(){ return '汪汪汪'; } }
这我的在遛狗,忽然遇到了死对头,他因而放狗咬人函数
$people = new People(); $people->putDog();
在这个操做中,people类要执行 putDog()
这个方法,须要依赖Dog类,通常咱们像上面同样,在people中利用构造函数来添加这个Dog依赖。若是使用控制反转 依赖注入则是这个样子工具
class People { public $dog = null; public function __construct(Dog $Dog) { $this->dog = $dog; } public function putDog(){ return $this->dog->dogCall(); } }
People类经过构造参数声明本身须要的 依赖类,由容器自动注入。这样就实现了程序的有效解耦,好处在这就很少说了。ui
闭包(匿名函数):this
匿名函数(Anonymous functions),也叫闭包函数(closures),容许 临时建立一个没有指定名称的函数debug
反射:PHP 5 以上版本具备完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释
lass Container { /** * 容器绑定,用来装提供的实例或者 提供实例的回调函数 * @var array */ public $building = []; /** * 注册一个绑定到容器 */ public function bind($abstract, $concrete = null, $shared = false) { if(is_null($concrete)){ $concrete = $abstract; } if(!$concrete instanceOf Closure){ $concrete = $this->getClosure($abstract, $concrete); } $this->building[$abstract] = compact("concrete", "shared"); } //注册一个共享的绑定 单例 public function singleton($abstract, $concrete, $shared = true){ $this->bind($abstract, $concrete, $shared); } /** * 默认生成实例的回调闭包 * * @param $abstract * @param $concrete * @return Closure */ public function getClosure($abstract, $concrete) { return function($c) use($abstract, $concrete){ $method = ($abstract == $concrete)? 'build' : 'make'; return $c->$method($concrete); }; } /** * 生成实例 */ public function make($abstract) { $concrete = $this->getConcrete($abstract); if($this->isBuildable($concrete, $abstract)){ $object = $this->build($concrete); }else{ $object = $this->make($concrete); } return $object; } /** * 获取绑定的回调函数 */ public function getConcrete($abstract) { if(! isset($this->building[$abstract])){ return $abstract; } return $this->building[$abstract]['concrete']; } /** * 判断 是否 能够建立服务实体 */ public function isBuildable($concrete, $abstract) { return $concrete === $abstract || $concrete instanceof Closure; } /** * 根据实例具体名称实例具体对象 */ public function build($concrete) { if($concrete instanceof Closure){ return $concrete($this); } //建立反射对象 $reflector = new ReflectionClass($concrete); if( ! $reflector->isInstantiable()){ //抛出异常 throw new \Exception('没法实例化'); } $constructor = $reflector->getConstructor(); if(is_null($constructor)){ return new $concrete; } $dependencies = $constructor->getParameters(); $instance = $this->getDependencies($dependencies); return $reflector->newInstanceArgs($instance); } //经过反射解决参数依赖 public function getDependencies(array $dependencies) { $results = []; foreach( $dependencies as $dependency ){ $results[] = is_null($dependency->getClass()) ?$this->resolvedNonClass($dependency) :$this->resolvedClass($dependency); } return $results; } //解决一个没有类型提示依赖 public function resolvedNonClass(ReflectionParameter $parameter) { if($parameter->isDefaultValueAvailable()){ return $parameter->getDefaultValue(); } throw new \Exception('出错'); } //经过容器解决依赖 public function resolvedClass(ReflectionParameter $parameter) { return $this->make($parameter->getClass()->name); } }
接着上面遛狗的例子:
//实例化容器类 $app = new Container(); //向容器中填充Dog $app->bind('Dog','App\Dog'); //填充People $app->bind('People', 'App\People'); //经过容器实现依赖注入,完成类的实例化; $people = $app->make('People'); //调用方法 echo $people->putDog();
上面示例中咱们先实例化容器类,而后使用 bind()
方法 绑定接口和 生成相应的实例的闭包函数。而后使用 make()
函数生成实例对象,在 make()
中会调用 isBuildable($concrete, $abstract)
来判断 给定的服务实体( $concrete
参数)是否能够建立,能够建立 就会调用 build($concrete)
函数 , build($concrete)
函数会判断传的参数是 是 闭包 仍是 具体类名 ,若是是闭包则直接运行,若是是具体类名的话,则经过反射获取该类的构造函数所需的依赖,完成实例化。
重点理解 下面这几个函数中 反射的用法,应该就很好理解了
build($concrete) getDependencies(array $dependencies) resolvedNonClass(ReflectionParameter $parameter) resolvedClass(ReflectionParameter $parameter)
IoC 理解起来是有点难度,可能文中描述让你感受不是很清楚,能够将文中代码 在php中用debug观察 运行状态。
理解了容器的具体实现原理,再去看Laravel中的相关实现,就会感受豁然开朗。