Laravel源码(4):Facade是个啥?
作者:互联网
laravel框架给人第一感觉是什么?我想绝大多数人都会说晦涩难懂。作者泰勒把php的很多特性都进行了封装,并给出优雅的接口,同时也创早了很多新词来描述这种封装。从某种角度看,这其实代表了作者的某种设计哲学。Facade应该算是对类和对象的一种封装。今天就来看看Faades到底是个啥。
1 官方文档的定义
“Facades 为应用的 服务容器 提供了一个「静态」 接口。Laravel 自带了很多 Facades,可以访问绝大部分功能。Laravel Facades 实际是服务容器中底层类的 「静态代理」 ,相对于传统静态方法,在使用时能够提供更加灵活、更加易于测试、更加优雅的语法。”
如何使用Facades?
#所有的 Laravel Facades 都定义在 Illuminate\Support\Facades 命名空间下。
#所以,我们可以轻松的使用 Facade :
use Illuminate\Support\Facades\Cache;
Route::get('/cache', function () {
return Cache::get('key');
});
这个实例导入了缓存类的Facade,这个Facade是一个定义在Illuminate\Support\Facades命名空间下的Cache类。关于缓存类的具体实现还有待深挖。当然Facade的更通用的用法是省略前缀Illuminate\Support\Facades,直接use Cache进来,或者直接在代码中以\Cache::get()的方式调用,因为在config目录下的app.php文件中已经为我们导入了类别名。
'aliases' => [
'App' => Illuminate\Support\Facades\App::class,
'Artisan' => Illuminate\Support\Facades\Artisan::class,
'Auth' => Illuminate\Support\Facades\Auth::class,
'Blade' => Illuminate\Support\Facades\Blade::class,
'Broadcast' => Illuminate\Support\Facades\Broadcast::class,
'Bus' => Illuminate\Support\Facades\Bus::class,
'Cache' => Illuminate\Support\Facades\Cache::class,
'Config' => Illuminate\Support\Facades\Config::class,
'Cookie' => Illuminate\Support\Facades\Cookie::class,
'Crypt' => Illuminate\Support\Facades\Crypt::class,
'DB' => Illuminate\Support\Facades\DB::class,
'Eloquent' => Illuminate\Database\Eloquent\Model::class,
'Event' => Illuminate\Support\Facades\Event::class,
'File' => Illuminate\Support\Facades\File::class,
'Gate' => Illuminate\Support\Facades\Gate::class,
'Hash' => Illuminate\Support\Facades\Hash::class,
'Lang' => Illuminate\Support\Facades\Lang::class,
'Log' => Illuminate\Support\Facades\Log::class,
'Mail' => Illuminate\Support\Facades\Mail::class,
'Notification' => Illuminate\Support\Facades\Notification::class,
'Password' => Illuminate\Support\Facades\Password::class,
'Queue' => Illuminate\Support\Facades\Queue::class,
'Redirect' => Illuminate\Support\Facades\Redirect::class,
'Redis' => Illuminate\Support\Facades\Redis::class,
'Request' => Illuminate\Support\Facades\Request::class,
'Response' => Illuminate\Support\Facades\Response::class,
'Route' => Illuminate\Support\Facades\Route::class,
'Schema' => Illuminate\Support\Facades\Schema::class,
'Session' => Illuminate\Support\Facades\Session::class,
'Storage' => Illuminate\Support\Facades\Storage::class,
'URL' => Illuminate\Support\Facades\URL::class,
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
'Excel' => Maatwebsite\Excel\Facades\Excel::class,
'Input' => Illuminate\Support\Facades\Input::class,
'EasyWeChat' => Overtrue\LaravelWeChat\Facade::class,
'Image' => Intervention\Image\Facades\Image::class,
'QrCode' => SimpleSoftwareIO\QrCode\Facades\QrCode::class,
],
2 Facade的底层细节
这里以缓存类的Facade为例来看下底层细节。找到Illuminate\Support\Facades\Cache类文件。
<?php
namespace Illuminate\Support\Facades;
/**
* @method static \Illuminate\Contracts\Cache\Repository store(string|null $name = null)
* @method static bool has(string $key)
* @method static mixed get(string $key, mixed $default = null)
* @method static mixed pull(string $key, mixed $default = null)
* @method static void put(string $key, $value, \DateTimeInterface|\DateInterval|float|int $minutes)
* @method static bool add(string $key, $value, \DateTimeInterface|\DateInterval|float|int $minutes)
* @method static int|bool increment(string $key, $value = 1)
* @method static int|bool decrement(string $key, $value = 1)
* @method static void forever(string $key, $value)
* @method static mixed remember(string $key, \DateTimeInterface|\DateInterval|float|int $minutes, \Closure $callback)
* @method static mixed sear(string $key, \Closure $callback)
* @method static mixed rememberForever(string $key, \Closure $callback)
* @method static bool forget(string $key)
* @method static \Illuminate\Contracts\Cache\Store getStore()
*
* @see \Illuminate\Cache\CacheManager
* @see \Illuminate\Cache\Repository
*/
class Cache extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'cache';
}
}
Cache类继承了抽象类:Illuminate\Support\Facades\Facade,并重写了getFacadeAccessor方法,重写的方法只是简单的返回字符串“cache”,并没有做其他事情,更多的事情交给了父类定义的方法来做。看下抽象类Facade是怎样实现的:
<?php
namespace Illuminate\Support\Facades;
use Mockery;
use RuntimeException;
use Mockery\MockInterface;
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;
/**
* Convert the facade into a Mockery spy.
*
* @return \Mockery\MockInterface
*/
public static function spy()
{
if (! static::isMock()) {
$class = static::getMockableClass();
return tap($class ? Mockery::spy($class) : Mockery::spy(), function ($spy) {
static::swap($spy);
});
}
}
/**
* Initiate a mock expectation on the facade.
*
* @return \Mockery\Expectation
*/
public static function shouldReceive()
{
$name = static::getFacadeAccessor();
$mock = static::isMock()
? static::$resolvedInstance[$name]
: static::createFreshMockInstance();
return $mock->shouldReceive(...func_get_args());
}
/**
* Create a fresh mock instance for the given class.
*
* @return \Mockery\Expectation
*/
protected static function createFreshMockInstance()
{
return tap(static::createMock(), function ($mock) {
static::swap($mock);
$mock->shouldAllowMockingProtectedMethods();
});
}
/**
* Create a fresh mock instance for the given class.
*
* @return \Mockery\MockInterface
*/
protected static function createMock()
{
$class = static::getMockableClass();
return $class ? Mockery::mock($class) : Mockery::mock();
}
/**
* Determines whether a mock is set as the instance of the facade.
*
* @return bool
*/
protected static function isMock()
{
$name = static::getFacadeAccessor();
return isset(static::$resolvedInstance[$name]) &&
static::$resolvedInstance[$name] instanceof MockInterface;
}
/**
* Get the mockable class for the bound instance.
*
* @return string|null
*/
protected static function getMockableClass()
{
if ($root = static::getFacadeRoot()) {
return get_class($root);
}
}
/**
* Hotswap the underlying instance behind the facade.
*
* @param mixed $instance
* @return void
*/
public static function swap($instance)
{
static::$resolvedInstance[static::getFacadeAccessor()] = $instance;
if (isset(static::$app)) {
static::$app->instance(static::getFacadeAccessor(), $instance);
}
}
/**
* 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];
}
/**
* Clear a resolved facade instance.
*
* @param string $name
* @return void
*/
public static function clearResolvedInstance($name)
{
unset(static::$resolvedInstance[$name]);
}
/**
* Clear all of the resolved instances.
*
* @return void
*/
public static function clearResolvedInstances()
{
static::$resolvedInstance = [];
}
/**
* Get the application instance behind the facade.
*
* @return \Illuminate\Contracts\Foundation\Application
*/
public static function getFacadeApplication()
{
return static::$app;
}
/**
* Set the application instance.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public static function setFacadeApplication($app)
{
static::$app = $app;
}
/**
* 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调用静态方法的时候,方法不存在,转而调用PHP的魔术方法__callStatic,传入方法名和参数,在这个魔术方法中获得一个$instance变量,这个变量就是实际执行该方法的对象。这个对象是怎么来的?顺着getFacadeRoot方法,可以看到这个对象是从服务容器:Illuminate\Foundation\Application对象中解析出来的。Laravel使用这个服务容器作为联结各种服务的“胶水”或者说是容器。我们调用辅助函数dd(),打印一下这个对象:
dd(app());
在输出中找到abstractAliases和instances属性,这时instances数组里有31个类实例。abstractAliases数组里可以看到“cache”字符串映射的类:
#instances: array:31 [▶]
#aliases: array:66 [▶]
#abstractAliases: array:35 [▼
"app" => array:4 [▶]
"auth" => array:2 [▶]
"auth.driver" => array:1 [▶]
"blade.compiler" => array:1 [▶]
"cache" => array:2 [▼
0 => "Illuminate\Cache\CacheManager"
1 => "Illuminate\Contracts\Cache\Factory"
]
然后调用下Cache门面后打印:
\Cache::get(“name”);
dd(app());
#instances: array:32 [▶]
#aliases: array:66 [▶]
#abstractAliases: array:35 [▼
"app" => array:4 [▶]
"auth" => array:2 [▶]
"auth.driver" => array:1 [▶]
"blade.compiler" => array:1 [▶]
"cache" => array:2 [▼
0 => "Illuminate\Cache\CacheManager"
1 => "Illuminate\Contracts\Cache\Factory"
]
这个时候我们发现instances(在容器类中,instances属性保存的是共享实例)属性多了一个值,这个值就是实现缓存功能的对象,即Illuminate\Cache\CacheManager类的实例。
“cache” => CacheManager {#266 ▼
#app: Application {#2}
#stores: array:1 [▶]
#customCreators: []
}
总结:看到这里就明白了,缓存的Facade,Cache类的实现只是告诉服务容器,我需要的服务名字叫“cache”,你把之前绑定的类实例化后返回给我用。至于“cache”服务是何时绑定到容器中的,这就涉及框架的启动过程以及Application类的内部实现了。
标签:Laravel,Illuminate,Support,Facades,源码,static,Facade,return,class 来源: https://blog.csdn.net/ldy_2017/article/details/96153044