Laravel中的Facade代码解析

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);
//最终是这里
}
}
}

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注