Route如何用静态方法来处理路由的? Facade

主要涉及类:

'Illuminate\Foundation\Bootstrap\RegisterFacades'

流程梳理

从Kernel 开始

1
2
3
4
5
6
7
8
9
10
11
12
13
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());
}

在将请求送往路由器之前,需要做一些几本功能的启动:

$this->bootstrap();

看它做了啥:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public function bootstrap()
{
if (! $this->app->hasBeenBootstrapped()) {
$this->app->bootstrapWith($this->bootstrappers());
}
}

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',
];

跟进 Application 下的bootstrapWith() 方法:

1
2
3
4
5
6
7
8
9
10
11
12
public function bootstrapWith(array $bootstrappers)
{
$this->hasBeenBootstrapped = true;

foreach ($bootstrappers as $bootstrapper) {
$this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);

$this->make($bootstrapper)->bootstrap($this);

$this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
}
}

原来是迭代调用Kernel 类内$bootstrappers 数组中各个类的bootstrap() 方法。

在数组中我们看到一个:

'Illuminate\Foundation\Bootstrap\RegisterFacades'

这就是我们今天要研究的 Facade 入口。

RegisterFacades

RegisterFacades 类内只有一个方法,其他方法都继承自 Facade

1
2
3
4
5
6
7
8
9
10
public function bootstrap(Application $app)
{
//清除已解析的实例 static::$resolvedInstance = [];
Facade::clearResolvedInstances();
//设置 $app 实例。static::$app = $app;
Facade::setFacadeApplication($app);

//注册
AliasLoader::getInstance($app->make('config')->get('app.aliases'))->register();
}

前两句比较简单,做了下环境的初始化,直接看第三句:

AliasLoader::getInstance($app->make('config')->get('app.aliases'))->register();

首先,获取aliases配置

$app->make('config')->get('app.aliases')

执行后,我们将得到在 cofnig/app.php 内定义的 aliases 数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
'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,
'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,
'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,

]

然后,别名数组作为参数传入AliasLoader::getInstance() 中,顾名思义,获取AliasLoader 的实例:

1
2
3
4
5
6
7
8
9
10
11
12
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;
}

这一过程,将传入的数组并入本类的静态参数$this->aliases 内,并根据是否被实例化过做了不同的处理,无需细说。

回到 bootstrap() 函数 ,最后,调用了register() 方法,这里是有趣的开始:

AliasLoader::getInstance($app->make('config')->get('app.aliases'))->register();

register() 实现:

1
2
3
4
5
6
7
8
9
public function register()
{
if (! $this->registered) {
$this->prependToLoaderStack();

//标记为已注册过。
$this->registered = true;
}
}

未注册过时,将调用prependToLoaderStack() 函数,prependToLoaderStack() 实现 :

1
2
3
4
protected function prependToLoaderStack()
{
spl_autoload_register([$this, 'load'], true, true);
}

原来是将本类load 函数作为__autoload 的实现,并且处于解析队列之首。当我们无法在某个命名空间下找到类时,将首先调用AliasLoader 实例的 load() 方法。

接着看 load() 的实现:

1
2
3
4
5
6
public function load($alias)
{
if (isset($this->aliases[$alias])) {
return class_alias($this->aliases[$alias], $alias);
}
}

很简单,会通过 class_alias 方法定义别名并返回处理结果,此时别名和原有类将完全相同。

此时,我已经可以愉快的调用 Router 而不报错,但如果我想调用 Router::get,仅仅这样是不行的, Illuminate\Support\Facades\Route 本身并不带任何路由处理的方法的实现,甚至根本找不到get() 方法,这里说到了找不到,类找不到可以调用__autoload ,静态方法找不到,同样可以调用__callStatic 方法,看一下源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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);
}
}

原来是获取了实例后调用的get 方法,离真相越来越近了。看一下获取实例的方法static::getFacadeRoot() 的实现:

该实现在Route 的父类Facade 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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];
}

return static::$resolvedInstance[$name] = static::$app[$name];
}

getFacadeAccessor方法在Route 类中被重写:

1
2
3
4
protected static function getFacadeAccessor()
{
return 'router';
}

Route 为例,在调用getFacadeRoot() 方法后,会调用Routestatic::getFacadeAccessor() 方法,得到router 传入resolveFacadeInstance() 方法中,如果解析过、或者已经是对象,则直接返回对象,如果不是,则调用Application 实例的make 方法进行实例化,即static::$app[$name] ,而$app['router'] 恰恰是我们之前已经注册过的路由。

1
2
3
4
5
6
protected function registerRouter()
{
$this->app['router'] = $this->app->share(function ($app) {
return new Router($app['events'], $app);
});
}