如下是Laravel官方文檔的介紹laravel
Facades 爲應用程序的 服務容器 中可用的類提供了一個「靜態」接口。Laravel 自己附帶許多的 facades,甚至你可能在不知情的情況下已經在使用他們!Laravel 「facades」做爲在服務容器內基類的「靜態代理」,擁有簡潔、易表達的語法優勢,同時維持着比傳統靜態方法更高的可測試性和靈活性。bootstrap
從介紹中能夠看出,Facades 好處就是讓代碼更加簡介,優雅,這也是Laravel追求的特性,如何使用Facades這裏就不介紹了,能夠參考Laravel文檔(中文) ,本文介紹一下Facades是如何知道和建立你須要的類實例。app
以 log Facade 爲例,咱們看下是如何經過log這個字符串找到 \Illuminate\Log\Writer 這個類的 先看 \Illuminate\Support\Facades\Log 門面ide
class Log extends Facade { /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'log'; } }
這個類很是簡單,只有一個靜態方法 getFacadeAccessor(), 返回了一個log字符串。 而後看Log的父類 Facade, Facede中有不少方法,這裏關注其中兩個:測試
abstract class Facade { /** * The application instance being facaded. * * @var \Illuminate\Contracts\Foundation\Application */ protected static $app; /** * The resolved object instances. * * @var array */ protected static $resolvedInstance; /** * Resolve the facade root instance from the container. * * @param string|object $name * @return mixed */ protected static function resolveFacadeInstance($name) { if (is_object($name)) { // 若是$name已是一個對象,則直接返回該對象 return $name; } if (isset(static::$resolvedInstance[$name])) { // 若是是已經解析過的對象,直接從$resolvedInstance中返回該對象 return static::$resolvedInstance[$name]; } return static::$resolvedInstance[$name] = static::$app[$name]; // 從容器中尋找$name對象,並放入$resolvedInstance 中以便下次使用 } /** * Handle dynamic, static calls to the object. * * @param string $method * @param array $args * @return mixed * * @throws \RuntimeException */ public static function __callStatic($method, $args) // 魔術方法,當使用Log::error($msg) 的時候會調用該方法 { $instance = static::getFacadeRoot(); if (! $instance) { throw new RuntimeException('A facade root has not been set.'); } switch (count($args)) { case 0: return $instance->$method(); case 1: return $instance->$method($args[0]); case 2: return $instance->$method($args[0], $args[1]); case 3: return $instance->$method($args[0], $args[1], $args[2]); case 4: return $instance->$method($args[0], $args[1], $args[2], $args[3]); default: return call_user_func_array([$instance, $method], $args); } } }
經過以上分析知道最終Facede找的是容器中綁定的實例,因此接下來咱們找一下log是在何時被註冊的,this
這時候須要關注 \Illuminate\Foundation\Http\Kernel 類,Kernel類中包括如下幾個方法:spa
/** * Handle an incoming HTTP request. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function handle($request) { try { $request->enableHttpMethodParameterOverride(); $response = $this->sendRequestThroughRouter($request); } catch (Exception $e) { $this->reportException($e); $response = $this->renderException($request, $e); } catch (Throwable $e) { $this->reportException($e = new FatalThrowableError($e)); $response = $this->renderException($request, $e); } $this->app['events']->fire('kernel.handled', [$request, $response]); return $response; } /** * Send the given request through the middleware / router. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ 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()); } /** * Bootstrap the application for HTTP requests. * * @return void */ public function bootstrap() { if (! $this->app->hasBeenBootstrapped()) { $this->app->bootstrapWith($this->bootstrappers()); } }
handle方法接收一個Request請求,並返回一個$response,$response->sendRequestThroughRouter()的時候調用了bootstrap()方法,繼續看bootstrap方法裏面加載了已經定義好的幾個類:(這些定義都在Kernel類中)代理
/** * The bootstrap classes for the application. * * @var array */ protected $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', ];
而後咱們看ConfigureLogging中的registerLogger()方法code
/** * Register the logger instance in the container. * * @param \Illuminate\Contracts\Foundation\Application $app * @return \Illuminate\Log\Writer */ protected function registerLogger(Application $app) { $app->instance('log', $log = new Writer( // 這裏把Writer註冊到了容器中 new Monolog($app->environment()), $app['events']) ); return $log; }
到此爲止,咱們已經知道Facede是如何找到想要使用的類了。 Facedes看起來挺高大上,但實現起來的原理挺簡單的,實際上也是一種單例模式,只不過在調用處包裝了一下component