【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返回實例。
複製代碼
相關文章
相關標籤/搜索