Laravel核心解讀Facades

來源:互聯網
上載者:User
這篇文章主要介紹了關於Laravel核心解讀Facades,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下

什麼是Facades

Facades是我們在Laravel應用開發中使用頻率很高的一個組件,叫組件不太合適,其實它們是一組靜態類介面或者說代理,讓開發人員能簡單的訪問綁定到服務容器裡的各種服務。Laravel文檔中對Facades的解釋如下:

Facades 為應用程式的 服務容器 中可用的類提供了一個「靜態」介面。Laravel 本身附帶許多的 facades,甚至你可能在不知情的狀況下已經在使用他們!Laravel 「facades」作為在服務容器內基類的「靜態代理」,擁有簡潔、易表達的文法優點,同時維持著比傳統靜態方法更高的可測試性和靈活性。

我們經常用的Route就是一個Facade, 它是\Illuminate\Support\Facades\Route類的別名,這個Facade類代理的是註冊到服務容器裡的router服務,所以通過Route類我們就能夠方便地使用router服務中提供的各種服務,而其中涉及到的服務解析完全是隱式地由Laravel完成的,這在一定程度上讓應用程式代碼變的簡潔了不少。下面我們會大概看一下Facades從被註冊進Laravel架構到被應用程式使用這中間的流程。Facades是和ServiceProvider緊密配合的所以如果你瞭解了中間的這些流程對開發自訂Laravel組件會很有協助。

註冊Facades

說到Facades註冊又要回到再介紹其它核心組建時提到過很多次的Bootstrap階段了,在讓請求通過中介軟體和路由之前有一個啟動應用程式的過程:

//Class: \Illuminate\Foundation\Http\Kernel protected function sendRequestThroughRouter($request){    $this->app->instance('request', $request);    Facade::clearResolvedInstance('request');    $this->bootstrap();    return (new Pipeline($this->app))                    ->send($request)                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)                    ->then($this->dispatchToRouter());}//引導啟動Laravel應用程式public function bootstrap(){    if (! $this->app->hasBeenBootstrapped()) {        /**依次執行$bootstrappers中每一個bootstrapper的bootstrap()函數         $bootstrappers = [               'Illuminate\Foundation\Bootstrap\DetectEnvironment',             'Illuminate\Foundation\Bootstrap\LoadConfiguration',              'Illuminate\Foundation\Bootstrap\ConfigureLogging',             'Illuminate\Foundation\Bootstrap\HandleExceptions',             'Illuminate\Foundation\Bootstrap\RegisterFacades',             'Illuminate\Foundation\Bootstrap\RegisterProviders',             'Illuminate\Foundation\Bootstrap\BootProviders',            ];*/            $this->app->bootstrapWith($this->bootstrappers());    }}

在啟動應用的過程中Illuminate\Foundation\Bootstrap\RegisterFacades這個階段會註冊應用程式裡用到的Facades。

class RegisterFacades{    /**     * Bootstrap the given application.     *     * @param  \Illuminate\Contracts\Foundation\Application  $app     * @return void     */    public function bootstrap(Application $app)    {        Facade::clearResolvedInstances();        Facade::setFacadeApplication($app);        AliasLoader::getInstance(array_merge(            $app->make('config')->get('app.aliases', []),            $app->make(PackageManifest::class)->aliases()        ))->register();    }}

在這裡會通過AliasLoader類的執行個體將為所有Facades註冊別名,Facades和別名的對應關係存放在config/app.php檔案的$aliases數組中

'aliases' => [    'App' => Illuminate\Support\Facades\App::class,    'Artisan' => Illuminate\Support\Facades\Artisan::class,    'Auth' => Illuminate\Support\Facades\Auth::class,    ......    'Route' => Illuminate\Support\Facades\Route::class,    ......]

看一下AliasLoader裡是如何註冊這些別名的

// class: Illuminate\Foundation\AliasLoaderpublic 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;    }}protected function prependToLoaderStack(){    // 把AliasLoader::load()放入自動載入函數隊列中,共置於隊列頭部    spl_autoload_register([$this, 'load'], true, true);}

通過上面的程式碼片段可以看到AliasLoader將load方法註冊到了SPL __autoload函數隊列的頭部。看一下load方法的源碼:

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

在load方法裡$aliases配置裡的Facade類建立了對應的別名,比如當我們使用別名類Route時PHP會通過AliasLoader的load方法為把Illuminate\Support\Facades\Route::class類建立一個別名類Route,所以我們在程式裡使用別Route其實使用的就是`Illuminate\Support\Facades\Route類。

解析Facade代理的服務

把Facades註冊到架構後我們在應用程式裡就能使用其中的Facade了,比如註冊路由時我們經常用Route::get('/uri', 'Controller@action);,那麼Route是怎麼代理到路由服務的呢,這就涉及到在Facade裡服務的隱式解析了, 我們看一下Route類的源碼:

class Route extends Facade{    /**     * Get the registered name of the component.     *     * @return string     */    protected static function getFacadeAccessor()    {        return 'router';    }}

只有簡單的一個方法,並沒有get, post, delete等那些路由方法, 父類裡也沒有,不過我們知道調用類不存在的靜態方法時會觸發PHP的__callStatic靜態方法

public static function __callStatic($method, $args){    $instance = static::getFacadeRoot();    if (! $instance) {        throw new RuntimeException('A facade root has not been set.');    }    return $instance->$method(...$args);}//擷取Facade根對象public static function getFacadeRoot(){    return static::resolveFacadeInstance(static::getFacadeAccessor());}/** * 從服務容器裡解析出Facade對應的服務 */protected static function resolveFacadeInstance($name){    if (is_object($name)) {        return $name;    }    if (isset(static::$resolvedInstance[$name])) {        return static::$resolvedInstance[$name];    }    return static::$resolvedInstance[$name] = static::$app[$name];}

通過在子類Route Facade裡設定的accessor(字串router), 從服務容器中解析出對應的服務,router服務是在應用程式初始化時的registerBaseServiceProviders階段(具體可以看Application的構造方法)被\Illuminate\Routing\RoutingServiceProvider註冊到服務容器裡的:

class RoutingServiceProvider extends ServiceProvider{    /**     * Register the service provider.     *     * @return void     */    public function register()    {        $this->registerRouter();        ......    }    /**     * Register the router instance.     *     * @return void     */    protected function registerRouter()    {        $this->app->singleton('router', function ($app) {            return new Router($app['events'], $app);        });    }    ......}

router服務對應的類就是\Illuminate\Routing\Router, 所以Route Facade實際上代理的就是這個類,Route::get實際上調用的是\Illuminate\Routing\Router對象的get方法

/** * Register a new GET route with the router. * * @param  string  $uri * @param  \Closure|array|string|null  $action * @return \Illuminate\Routing\Route */public function get($uri, $action = null){    return $this->addRoute(['GET', 'HEAD'], $uri, $action);}

補充兩點:

  1. 解析服務時用的static::$app是在最開始的RegisterFacades裡設定的,它引用的是服務容器。

  2. static::$app['router'];以數組訪問的形式能夠從服務容器解析出router服務是因為服務容器實現了SPL的ArrayAccess介面, 對這個沒有概念的可以看下官方文檔ArrayAccess

總結

通過梳理Facade的註冊和使用流程我們可以看到Facade和服務提供者(ServiceProvider)是緊密配合的,所以如果以後自己寫Laravel自訂服務時除了通過組件的ServiceProvider將服務註冊進服務容器,還可以在組件中提供一個Facade讓應用程式能夠方便的訪問你寫的自訂服務。

以上就是本文的全部內容,希望對大家的學習有所協助,更多相關內容請關注topic.alibabacloud.com!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.