草稿 8 Laravel Facade 門面類載入原理

baotong發表於2019-12-18

Illuminte\Foundation\Bootstrap\RegisterFacades.php

public function bootstrap(Application $app)
{
        Facade::clearResovedInstances();
        Facade::setFacadeApplication($app);
        AliasLoader::getInstance($app->make('config')->get('app.aliases'))->register();
}

Illuminate\Foundation\AliasLoader.php

// 外觀別名類陣列
protected $aliases;

public static function getInstance( array $aliases = [])
{
        if(is_null(static::$instance)){
            return static::$instance = new static($aliases);
        }
        $aliases = array_merge(static::$instance->getAliases(), $aliases);
        static::$instance->setAliases($aliases);
        return static::$instance;
}

public function register()
{
        if(!$this->registered){
            $this->prependToLoaderStack();
            $this->registered = true;
        }
}

public function prependToLoaderStack()
{
        spl_autoload_register([$this, 'load'], true, true);
}

public function load($alias)
{
        if(isset($this->aliases[$alias])){
            class_alias($this->aliases[$alias], $alias);
        }       
}

class_alias ( string $original , string $alias [, bool $autoload = TRUE ] ) : bool。為一個類建立別名。比如給Illuminate\Support\Facades\Route類起個Route的別名,class_alias('Illuminate\Support\Facades\Route', 'Route')這樣就行了。這麼做的好處是根域名中的Route就表示名稱空間為Illuminate\Support\Facades的Route類。

外觀註冊是通過RegisterFacades類的bootstrap()函式實現的。實現門面類其實分為兩個步驟。1.是例項話外觀自動載入類Illuminate\Foundation\Aliasloader::class並把外觀別名陣列新增到該例項中,。2.完成自動載入類中的自動載入函式的新增。用getInstance()單例模式例項話外觀自動載入類。再用$app->make('config')->get('app.aliases')來獲取外觀類陣列。這句程式碼就用到了配置載入的內容,即獲取載入的app.php的配置檔案,返回陣列的鍵為”aliases”的陣列值。
在自動載入類例項話以後,會呼叫register()函式向自動載入棧的開始處加入一個新的載入函式,即通過程式碼spl_autoload_register([$this, 'load'], true, true)實現。所有類的自動載入都先經過這個函式。這個函式就是AliasLoader類例項的load()函式。此函式的作用是為外觀類設定一個別名。即外觀別名,當使用者通過外觀別名訪問類時,實際上訪問的是對應的外觀類。下面我們就看看通過外觀別名怎實現類似Route::get('路徑','響應函式')這樣的函式呼叫。

首先程式會呼叫Route類,由於註冊了外觀別名。那麼會自動載入第一個函式是AliasLoader類的load()函式。該函式會查詢別名對應類。於是便找到Route的外觀別名是Illuminate\Support\Facades\Route類,並載入這個類接著呼叫該類的get()靜態函式。可是這個類沒有對應的函式。不過它繼承了Illuminate\Support\Facades\Facade這個類。該類也沒有對應的靜態函式,不過該類有__callstatic()函式。這個函式是在沒有查詢到對應的靜態函式時候會被呼叫。__callstatic()函式裡面有個$instance = static::getFacadeRoot()來獲取對應類的例項,其中getFacadeRoot()靜態函式是根據Illuminate\Support\Facades\Route類的getFacadeAccessor()函式來例項話對應的Route類例項。最後呼叫Route例項的get函式,流程完畢。下面是相應的程式碼。

Illuminate\Support\Facades\Facade.php

public function getFacadeRoot()
{
        return static::resolveFacadeInstance(static::getFacadeAccessor());
}

public function resolveFacadeInstance($name)
{
        // 這裡不用看
        if(is_object($name)){
            return $name;
        }

        if(isset(static::resolvedInstance[$name])){
            return static::resolvedInstance[$name];
        }       
        // 主要看這裡,static::$app[$name]這個是以陣列的形式例項話類。等同於,$app->make($name);
        return static::$resolvedInstance[$name] = static::$app[$name]
}

public static function __callStatic($method, $args)
{
        $instance = static::getFacadeRoot();
        if (! $instance) {
            throw new RuntimeException('A facade root has not been set.');
      }
//    return call_user_func_array([$instance, $method], $args);
        return $instance->$method(...$args);
}

相關文章