Facade 模式在Laravel中的代码解析
Facade是外观门面的意思Laravel社区叫门面模式,也称为外观模式。它隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。
facade
英 /fə'sɑːd/
美 /fə'sɑd/
n. 正面;表面;外观
应用场景 1、去医院看病,可能要去挂号、门诊、划价、取药,让患者或患者家属觉得很复杂,如果有提供接待人员,只让接待人员来处理,就很方便
优点: 1、减少系统相互依赖。 2、提高灵活性。 3、提高了安全性。
缺点:不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。
再来看看Laravel 中解析的.
<?php
namespace Overtrue\LaravelWechat;
use Illuminate\Support\Facades\Facade;
class Log extends Facade
{
public static function getFacadeAccessor()
{
return "log";
}
}
根据代码来看,Log只有一个方法getFacadeAccessor返回一个string(3) log,
那么要调用Log::draw()的话怎么解析到这个方法的呢?
我们来看他的父类Facade
Illuminate\Support\Facades\Facade
* 由于太长了我只截取需要代码
<?php
namespace Illuminate\Support\Facades;
abstract class Facade
{
protected static $app;
protected static $resolvedInstance;
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
/**
*如果不实现 getFacadeAccessor 会抛出异常~
*/
protected static function getFacadeAccessor()
{
throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
}
public static function clearResolvedInstance($name)
{
unset(static::$resolvedInstance[$name]);
}
public static function clearResolvedInstances()
{
static::$resolvedInstance = [];
}
public static function getFacadeApplication()
{
return static::$app;
}
public static function setFacadeApplication($app)
{
static::$app = $app;
}
//Log::draw() 函数通过魔术方法__callStatic来调用
public static function __callStatic($method, $args)
{
$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);
}
}
public static function getFacadeRoot()
{
return static::resolveFacadeInstance(static::getFacadeAccessor());
}
protected static function resolveFacadeInstance($name)
{
if (is_object($name)) {
return $name;
}
if (isset(static::$resolvedInstance[$name])) {
return static::$resolvedInstance[$name];
}
//层层抽丝剥茧找到了static::$app['log']
//其实相当于 (object)static::$app->log
return static::$resolvedInstance[$name] = static::$app[$name];
}
}
由于$app是静态属性,且不是数组形式,那么 static::$app[$name]进行访问 按道理来说会报错,还有就是$app到底从哪来,内容是什么?
public static function setFacadeApplication($app)
{
static::$app = $app;//这里把$app赋值了
}
调用这个方法的地方肯定会传入这个$app
<?php
namespace Illuminate\Foundation\Bootstrap;
use Illuminate\Support\Facades\Facade;
use Illuminate\Foundation\AliasLoader;
use Illuminate\Contracts\Foundation\Application;
class RegisterFacades
{
/**
* Bootstrap the given application.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function bootstrap(Application $app)
{
Facade::clearResolvedInstances();
Facade::setFacadeApplication($app);//这里进行了调用,
//传递了$app参数,它是Application的实例
AliasLoader::getInstance($app->make('config')->get('app.aliases'))->register();
}
}
那么追本逐源
在 Illuminate\Foundation\Http\Kernel.php 有这样一个数组
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',
];
protected function bootstrappers()
{
return $this->bootstrappers;
}
public function bootstrap()
{
if (! $this->app->hasBeenBootstrapped()) {
$this->app->bootstrapWith($this->bootstrappers());
}
}
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());
}
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) {
$e = new FatalThrowableError($e);
$this->reportException($e);
$response = $this->renderException($request, $e);
}
$this->app['events']->fire('kernel.handled', [$request, $response]);
return $response;
}
//最后 在public/index.php
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(//调用上面的handle方法
$request = Illuminate\Http\Request::capture()
);
/******
在Illuminate\Foundation\application.php中
public function bootstrapWith(array $bootstrappers)
{
$this->hasBeenBootstrapped = true;
foreach ($bootstrappers as $bootstrapper) {
$this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);
$this->make($bootstrapper)->bootstrap($this);
//在这里相当于
// new RegisterFacades()->bootstrap()
$this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
}
}
**********/
总结:
在项目启动的时候,注册kernel到容器中,然后调用handle方法,
$this->sendRequestThroughRouter
再调用$this->bootstrap();
最后实现了RegisterFacades类的bootstrap()方法
从而注入了$app
第二个问题:static::$app[$name] 为什么可以这么写呢?
原因就是:
1 php的数组式访问
1> ArrayAccess【http://php.net/manual/zh/class.arrayaccess.php】
2> 因为 $app 在这里是 依赖注入的时候是Illuminate\Foundation\Application 的一个实例
而这个类又继承了
Illuminate\Container\Container 而它又继承(implements) ArrayAccess
2 那它又是什么时候被注入到$app里的呢?
在config/app.php里面有个数组叫aliases
其中有个元素为:
'Log' => Illuminate\Support\Facades\Log::class,
在调用this->bootstrap()
class RegisterFacades
{
/**
* Bootstrap the given application.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function bootstrap(Application $app)
{
Facade::clearResolvedInstances();
Facade::setFacadeApplication($app);
AliasLoader::getInstance($app->make('config')->get('app.aliases'))
->register();
}
}
namespace Illuminate\Foundation;
class AliasLoader
{
public static function getInstance(array $aliases = [])
{
if (is_null(static::$instance)) {
return static::$instance = new static($aliases);
}
$aliases = array_merge(static::$instance->getAliases(), $aliases);
static::$instance->setAliases($aliases);
return static::$instance;
}
public function register()
{
if (! $this->registered) {
$this->prependToLoaderStack();
$this->registered = true;
}
}
protected function prependToLoaderStack()
{
spl_autoload_register([$this, 'load'], true, true);
}
public function load($alias)
{
if (isset($this->aliases[$alias])) {
return class_alias($this->aliases[$alias], $alias);
//最终是这里
}
}
}