【Laravel-海贼王系列】第三章,Container 类解析

容器类是laravel的一个核心功能类,经过分解将了解核心功能。

Container 头部声明

namespace Illuminate\Container;

use Closure; // "PHP 默认闭包类"
use Exception; // "异常处理类"
use ArrayAccess; // "对象按数组方式调用类"
use LogicException; 
use ReflectionClass; // "反射类"
use ReflectionParameter; // "反射参数类"
use Illuminate\Support\Arr; // "边界数组访问类"
use Illuminate\Contracts\Container\BindingResolutionException; // "绑定结果异常类"
use Illuminate\Contracts\Container\Container as ContainerContract; // "容器契约"

class Container implements ArrayAccess, ContainerContract
复制代码

容器实现了ArrayAccess接口兼容以数组调用的方式来读取属性。laravel

成员变量

Container 的成员
    protected static $instance; // "Application 实例"
    protected $resolved = []; // "已解析的类型的数组"
    protected $bindings = []; // "容器中的绑定数组"
    protected $methodBindings = []; // "容器中的方法绑定"
    protected $instances = []; // "容器中的共享实例"
    protected $aliases = []; // "类的别名"
    protected $abstractAliases = []; // "类的别名"
    protected $extenders = []; // 
    protected $tags = []; // "全部注册的标签"
    protected $buildStack = []; // "build进行中的具体类"
    protected $with = []; // "build中须要的构造函数参数"
    public $contextual = []; // "构建类的上下文环境"
    protected $reboundCallbacks = []; 
    protected $globalResolvingCallbacks = []; // "容器全局须要在解析对象过程当中调用的回调"
    protected $globalAfterResolvingCallbacks = []; // "容器全局须要在解析后调用的回调"
    protected $resolvingCallbacks = [];  // "针对指定的类在解析的时候调用的回调"
    protected $afterResolvingCallbacks = [];// "针对指定的类在解析后的时候调用的回调"
复制代码

主要方法解析

绑定抽象名和具体类的方法
public function bind($abstract, $concrete = null, $shared = false)
{
    // "从 $instances$aliases 清理旧的绑定信息"
    $this->dropStaleInstances($abstract);

    // "若是没有传入具体类,则将抽象名赋值给具体类"
    if (is_null($concrete)) {
        $concrete = $abstract; 
    }

    // "若是具体类不是闭包对象,则进行一层包装"
    if (! $concrete instanceof Closure) {
        $concrete = $this->getClosure($abstract, $concrete); 
    }

    // "将闭包对象$concrete$shared赋值到bindings中的抽象名下"
    $this->bindings[$abstract] = compact('concrete', 'shared');
    
    // "若是抽象名已经被解析过实例,则进行从新绑定"
    if ($this->resolved($abstract)) {
        $this->rebound($abstract);
    }
}
复制代码
根据给定的类获取实例
public function build($concrete)
{
    // "若是是闭包则直接执行本身获取实例"
    if ($concrete instanceof Closure) {
        return $concrete($this, $this->getLastParameterOverride());
    }

    // "获取具体类的反射"
    $reflector = new ReflectionClass($concrete);

    // "若是类不能够实例化则抛出异常"
    if (! $reflector->isInstantiable()) {
        return $this->notInstantiable($concrete);
    }

    // "将类放入构建堆栈中"
    $this->buildStack[] = $concrete;

    // "获取类的构造函数"
    $constructor = $reflector->getConstructor();

    // "若是没有构造函数,直接从 buildStack 堆栈中 pop 出的同时返回新对象"
    if (is_null($constructor)) {
        array_pop($this->buildStack);
        return new $concrete;
    }

    // "获取构造函数依赖变量"
    $dependencies = $constructor->getParameters();

    // "解析全部须要的变量包括对象 (DI就是这么实现的!)"
    $instances = $this->resolveDependencies(
        $dependencies
    );

    // "弹出堆栈中待构建的类"
    array_pop($this->buildStack);

    // "根据参数实例化类并返回"
    return $reflector->newInstanceArgs($instances);
}
复制代码
根据传入的抽象名和参数解析出相对的具体类
protected function resolve($abstract, $parameters = [])
{
    // "若是存在别名则返回,不然返回抽象"
    $abstract = $this->getAlias($abstract);

    // "存在参数或者给定抽象的上下文绑定则复制给变量"
    // "上下文绑定实现指 $abstractAliases 中的绑定关系"

    $needsContextualBuild = ! empty($parameters) || ! is_null(
        $this->getContextualConcrete($abstract)
    );
    
    // "若是是个单例实例则能够直接返回已经存在的实例。"
    if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
        return $this->instances[$abstract]; 
    }

    // "将须要的参数放入 $with 中"
    $this->with[] = $parameters; 

    // "从$bindings中获取抽象对应的具体类"
    $concrete = $this->getConcrete($abstract); 

    if ($this->isBuildable($concrete, $abstract)) {
        // "若是具体类是闭包或者和抽像同名的时候直接实例化这个类"
        $object = $this->build($concrete);
    } else {
        // "递归套嵌的依赖,知道全部的依赖被解析实例化。"
        $object = $this->make($concrete);
    }

    // "若是此类定义了拓展,则将该构建中的对象进行拓展和修饰。这容许更改配置或者修饰对象"
    foreach ($this->getExtenders($abstract) as $extender) {
        $object = $extender($object, $this);
    }
    
    // "若是是单例类,直接将实例放入内存,防止后续重复实例化。"
    if ($this->isShared($abstract) && ! $needsContextualBuild) {
        $this->instances[$abstract] = $object;
    }

    // "调用全部全局待解析的回调和指定抽象的回调"
    $this->fireResolvingCallbacks($abstract, $object);

    // "标识此抽象已经被解析完毕。"
    $this->resolved[$abstract] = true; 

    // "with中的数据出栈"
    array_pop($this->with);

    // "返回解析完毕的实例"
    return $object;
}
复制代码

总结

目前只是分析比较核心的方法数组

bind() 方法负责把抽象和具体类绑定到$bindings成员中。
复制代码
build() 方法负责具体类中反射出须要的参数构造实例并返回。
复制代码
resolve()方法则是将抽象从$aliases中查找别名,

而后从$bindings获取具体类,最后调用build()方法来构造类,

或者继续递归本身来构造类的依赖。其中还包含拓展类以及执行全部回调。

同时若是是单例的类则在构造完成后存入$instances,

下次调用则直接从$instances返回实例。
复制代码
相关文章
相关标签/搜索