Laravel Facades 門面模式的實現

如下是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

相關文章
相關標籤/搜索