您的位置:首页 > 编程语言 > PHP开发

Laravel Facades 门面模式的实现

2019-05-30 20:39 267 查看

以下是Laravel官方文档的介绍

Facades 为应用程序的 服务容器 中可用的类提供了一个「静态」接口。Laravel 本身附带许多的 facades,甚至你可能在不知情的状况下已经在使用他们!Laravel 「facades」作为在服务容器内基类的「静态代理」,拥有简洁、易表达的语法优点,同时维持着比传统静态方法更高的可测试性和灵活性。

从介绍中可以看出,Facades 好处就是让代码更加简介,优雅,这也是Laravel追求的特性,如何使用Facades这里就不介绍了,可以参考Laravel文档(中文) ,本文介绍一下Facades是如何知道和创建你需要的类实例。

以 log Facade 为例,我们看下是如何通过log这个字符串找到 \Illuminate\Log\Writer 这个类的
先看 \Illuminate\Support\Facades\Log 门面

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是在什么时候被注册的,

这时候需要关注 \Illuminate\Foundation\Http\Kernel 类,Kernel类中包括以下几个方法:

/**
* 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,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()方法

/**
* 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看起来挺高大上,但实现起来的原理挺简单的,实际上也是一种单例模式,只不过在调用处包装了一下

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: