Laravel的Facade是什麼?
Facade
其實是一個容器中類的靜態代理,他可以讓你以靜態的方式來呼叫存放在容器中任何物件的任何方法。舉個例子:
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
實際上,Cache
類並沒有一個get
靜態方法,但是卻可以被呼叫,這就是Facade
的靜態代理功能。
如何使用Facade?
假如我們自定義了一個類並且放入容器中後,該如何以Facade
的方式來呼叫呢?很簡單:
use Illuminate\Support\Facades\Facade;
class Cache extends Facade
{
/**
* 獲取元件註冊名稱
*
* @return string
*/
protected static function getFacadeAccessor() {
return 'cache';
}
}
只要定義一個類讓他繼承自Illuminate\Support\Facades\Facade
,並且實現一個抽象方法getFacadeAccessor
即可。這個方法只要返回一個字串,就是返回服務容器繫結類的別名。其實,通過原始碼可以知道,物件不一定要放到容器中,可以直接在這裡返回也是可以的,下面會說道:
use Illuminate\Support\Facades\Facade;
use Cache;
class Cache extends Facade
{
/**
* 獲取元件註冊名稱
*
* @return string
*/
protected static function getFacadeAccessor() {
return new Cache;
}
}
如何實現的呢?
我們來看看Illuminate\Support\Facades\Facade
的原始碼:
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;
/**
* Get the root object behind the facade.
*
* @return mixed
*/
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
/**
* Get the registered name of the component.
*
* @return string
*
* @throws \RuntimeException
*/
protected static function getFacadeAccessor()
{
throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
}
/**
* Resolve the facade root instance from the container.
*
* @param string|object $name
* @return mixed
*/
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];
}
/**
* Handle dynamic, static calls to the object.
*
* @param string $method
* @param array $args
* @return mixed
*
* @throws \RuntimeException
*/
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
的程式碼不止以上這些,我抽出了核心的部分。直接來看看__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); //呼叫方法
}
這是PHP中的一個魔術方法,當以靜態的方式呼叫一個不存在的方法時,該方法會被呼叫。程式碼很簡單,就是解析例項呼叫方法。不過這裡要注意一個就是這裡使用的是static
關鍵字而不是self
關鍵字,這涉及到一個後期的靜態繫結,可以看看文件:http://php.net/manual/zh/language.oop5.lat...
再來看看getFacadeRoot
的程式碼,這個方法就是從容器中解析出物件:
public static function getFacadeRoot(){
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
protected static function getFacadeAccessor(){
throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
}
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];
}
其中的getFacadeAccessor
這個方法必須被重寫,否者就會丟擲異常。然後在resolveFacadeInstance
這個方法中會先判斷是否是一個物件,如果是的話就直接返回。所以上文說的getFacadeAccessor
這個方法直接返回一個物件也是可以的,奧祕就在這。
然後會去判斷需要解析的物件是否已經解析過了,如果解析過了就直接返回,否則會從容器中去解析再返回,這樣不僅僅實現了單例,而且還可以提升效能。
得到物件後,就是直接通過物件來呼叫方法了:
$instance->$method(...$args); //呼叫方法
總結
其實Laravel
的Facade
原理不難,但是在研究的過程是可以發現很多文件沒有提供的內容的,也能進一步提高我們閱讀原始碼的能力。
本作品採用《CC 協議》,轉載必須註明作者和本文連結