Facades为应用程序的 服务容器 中可用的类提供了一个 静态接口 。
- 最直观的好处
就是需记住必须手动注入或配置的长长的类名。因此有人也理解Facades就是一个“快捷别名”
怎么变得更快捷呢?
# 使用make 去访问注册日志对象的info方法
$container->make('log')->info('message')
# 使用arrayaccess的方式
$container["log"]->info('message')
# 使用Facade访问Logger对象的info方法, 不需要使用容器去获取一个对象。
Log::info('message');
- 主要风险
会引起类作用范围的膨胀。因为 Facades 使用起来非常简单而且不需要注入,就会使得我们在不经意间在单个类中使用许多 Facades,从而导致类变的越来越大。
而使用依赖注入的时候,使用的类越多,构造方法就会越长,在视觉上就会引起注意,提醒你这个类有点庞大了。
- 在laravel中有许多辅助函数的功能都有与之对应的 Facade。
例如,下面这个 Facade 的调用和辅助函数的作用是一样的:
return View::make('profile');
return view('profile');
- Facades的工作原理
文件位于IlluminateSupportFacades目录下
我们分析Cache 类
<?php
namespace AppHttpControllers;
use IlluminateSupportFacadesCache;
use AppHttpControllersController;
class UserController extends Controller
{
/**
* 显示给定用户的信息。
*
* @param int $id
* @return Response
*/
public function showProfile($id)
{
$user = Cache::get('user:'.$id);
return view('profile', ['user' => $user]);
}
}
我们打开IlluminateSupportFacadesCache 这个类,你会发现类中根本没有 get 这个静态方法:
<?php
namespace IlluminateSupportFacades;
/**
* @see IlluminateCacheCacheManager
* @see IlluminateCacheRepository
*/
class Cache extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'cache';
}
}
既然没有那么我们去他的父类看看,父类也没有get方法,但是有个魔术方法__callStatic(),当找到不method的时候自动调用这个方法,顺着这个方法继续探究
Facades.php 的核心代码分析
/**
* Get the root object behind the facade.
*
* @return mixed
*/
public static function getFacadeRoot()
{
//2. 这里调用了getFacadeAccessor() 也就是Cache.php中的这个方法,返回了字符串‘cache’
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];
}
// 3. 调用了当前类的静态属性,其实最终获取的就是$app['cache'],那么这个属性是怎么初始化的呢,原来在laravel启动的时候调用了setFacadeApplication()
return static::$resolvedInstance[$name] = static::$app[$name];
}
/**
* Get the application instance behind the facade.
*
* @return IlluminateContractsFoundationApplication
*/
public static function getFacadeApplication()
{
return static::$app;
}
/**
* Set the application instance.
*
* @param IlluminateContractsFoundationApplication $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)
{
//1. 当找不到method的时候,调用__callStatic()
$instance = static::getFacadeRoot();
//4. 通过分析返回的$instance就是容器中的$app['cache']实例
if (! $instance) {
throw new RuntimeException('A facade root has not been set.');
}
//5。 调用上面实例的方法
return $instance->$method(...$args);
}
继续分析上面标注4得到的$app['cache'],我们找到IlluminateCacheCacheServiceProvider.php
public function register()
{
$this->app->singleton('cache', function ($app) {
return new CacheManager($app);
});
$this->app->singleton('cache.store', function ($app) {
return $app['cache']->driver();
});
$this->app->singleton('memcached.connector', function () {
return new MemcachedConnector;
});
}
会发现绑定了单例,返回了new CacheManager($app)对象,想进一步探究可以查看这个CacheManager类,再次不再继续探究。
通过以上我们其实已经明白了门面的套路。