RouteServiceProvider相关核心类即实现思路
入口
- App\Providers\RouteServiceProvider
namespace App\Providers;
use Illuminate\Support\Facades\Route;//Facade
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class RouteServiceProvider extends ServiceProvider
{
protected $namespace = 'App\Http\Controllers';//设置控制器根命名空间
public function boot()
{
parent::boot();
}
public function map()//加载路由基础信息及路由自定义信息
{
$this->mapApiRoutes();
$this->mapWebRoutes();
}
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
}
- Illuminate\Foundation\Support\Providers\RouteServiceProvider
<?php
namespace Illuminate\Foundation\Support\Providers;
use Illuminate\Routing\Router;
use Illuminate\Support\ServiceProvider;
use Illuminate\Contracts\Routing\UrlGenerator;
class RouteServiceProvider extends ServiceProvider
{
protected $namespace;
public function boot()
{
$this->setRootControllerNamespace();//设置控制器根命名空间
if ($this->app->routesAreCached()) {
$this->loadCachedRoutes();//加载缓存路由文件
} else {
$this->loadRoutes();//设置路由基础信息加载api.php与web.php
//注册匿名函数
$this->app->booted(function () {
$this->app['router']->getRoutes()->refreshNameLookups();
$this->app['router']->getRoutes()->refreshActionLookups();
});
}
}
protected function setRootControllerNamespace()
{
if (! is_null($this->namespace)) {
$this->app[UrlGenerator::class]->setRootControllerNamespace($this->namespace);
}
}
protected function loadRoutes()
{
if (method_exists($this, 'map')) {
$this->app->call([$this, 'map']);
}
}
...
}
依赖服务
- Illuminate\Routing\RoutingServiceProvider
//继承Illuminate\Support\ServiceProvider
class RoutingServiceProvider extends ServiceProvider
{
...
}
绑定容器
- ‘router’ => Illuminate\Routing\Router
public function __construct(Dispatcher $events, Container $container = null)
{
$this->events = $events;
$this->routes = new RouteCollection;
$this->container = $container ?: new Container;
}
- ‘routes’ => Illuminate\Routing\RouteCollection
class RouteCollection
{
...
}
- ‘url’ => Illuminate\Routing\UrlGenerator
public function __construct(RouteCollection $routes, Request $request)
{
$this->routes = $routes;
$this->setRequest($request);
}
public function setRequest(Request $request)
{
$this->request = $request;
$this->cachedRoot = null;
$this->cachedSchema = null;
$this->routeGenerator = null;
}
- ‘request’ => Illuminate\Http\Request
Facade
- Illuminate\Support\Facades\Route
路由信息存储基本单元
- Illuminate\Routing\Route
路由属性记录表
- Illuminate\Routing\RouteRegistrar
class RouteRegistrar
{
protected $attributes = [];
protected $passthru = [
'get', 'post', 'put', 'patch', 'delete', 'options', 'any',
];
protected $allowedAttributes = [
'as', 'domain', 'middleware', 'name', 'namespace', 'prefix',
];
protected $aliases = [
'name' => 'as',
];
...
}
实现思路
-
设置根控制器命名空间
通过服务容器bindings[‘url’]获取Illuminate\Routing\RoutingServiceProvider注册的匿名函数,创建实例并添加在服务容器instances内并返回实例。然后设置控制器命名空间 -
引入路由文件
执行apiMapRoutes()与webMapRoutes(),通过门面方式获取$router实例,__callStatic()执行相关动作,动作优先级执行通用属性设置,相关动作为被保护属性,故而执行Router __call(),__call()创建 RouterRegistrar实例,进而通过RouteRegistrar __call()继续设置通用属性,通用属性设定完毕后执行group()引入api.php/web.php,然后执行文件内的路由解析动作,该文件同样通过门面获取Router实例,不同之处在于设定请求方式的时候,进行了registerRouter()将控制权交给Router实例,并执行对应请求方法,创建Route实例,存储具体的路由信息,包含前缀、命名空间、群组、中间件、控制器、回调函数等属性。并将当前Route实例存储在$routes中。
实现过程
入口:Illuminate\Foundation\Support\Providers\RouteServiceProvider boot()
public function boot()
{
//设置根控制器命名空间
$this->setRootControllerNamespace();
//加载路由配置,默认不加在缓存路由文件
$this->loadRoutes();
//注册匿名函数
$this->app->booted(function () {
$this->app['router']->getRoutes()->refreshNameLookups();
$this->app['router']->getRoutes()->refreshActionLookups();
});
}
设置根控制器命名空间setRootControllerNamespace()
protected function setRootControllerNamespace()
{
if (! is_null($this->namespace)) {
$this->app[UrlGenerator::class]->setRootControllerNamespace($this->namespace);
}
}
服务容器获取实例
- 数组操作对象触发offsetGet()
- offsetGet()执行make(),再熟悉不过了,如果你还不知道make()的具体执行流程,那么请你回去看看$kernel的创建吧!
- $abstract由’Illuminate\Contracts\Routing\UrlGenerator’ 依据服务容器aliases属性映射为 ‘url’
- $concrete由服务容器bindings属性获取$abstract映射的匿名函数
- 执行class RoutingServiceProvider registerUrlGenerator()注册的函数
protected function registerUrlGenerator()
{
$this->app->singleton('url', function ($app) {
//由$router获取再初始化时绑定routes属性,即RouteCollection实例
$routes = $app['router']->getRoutes();
//绑定到服务容器的instances中
$app->instance('routes', $routes);
//$this->requestRebinder()返回匿名函数
/**
* function ($app, $request) {
* $app['url']->setRequest($request);
* };
*/
//$app->rebinding():向服务容器reboundCallbacks['request'][]属性中添加匿名函数,返回服务容器中instances的$request实例
//创建UrlGenerator实例,将$routes与$request分别绑定到对应属性
$url = new UrlGenerator(
$routes, $app->rebinding(
'request', $this->requestRebinder()
)
);
//$url->setSessionResolver():赋值$url->sessionResolver为setSessionResolver()匿名函数参数
$url->setSessionResolver(function () {
return $this->app['session'];
});
//$app->rebinding():向服务容器reboundCallbacks['routes'][]属性中添加匿名函数,返回服务容器中instances的$routes实例
$app->rebinding('routes', function ($app, $routes) {
$app['url']->setRoutes($routes);
});
return $url;
});
}
执行设置根控制器命名空间动作
//class UrlGenerator
public function setRootControllerNamespace($rootNamespace)
{
//$rootNamespace是 App\Providers\RouteServiceProvider protected $namespace = 'App\Http\Controllers';
$this->rootNamespace = $rootNamespace;
return $this;
}
加载路由
//class Illuminate\Foundation\Support\Providers\RouteServiceProvider
protected function loadRoutes()
{
//通过服务容器执行当前类map()
if (method_exists($this, 'map')) {
$this->app->call([$this, 'map']);
}
}
//class App\Providers\RouteServiceProvider
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
}
//class App\Providers\RouteServiceProvider
protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
//class App\Providers\RouteServiceProvider
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
使用门面获取$router,触发__callStatic()方法执行prefix(),Router prefix()具有protected属性,无法通过实例执行该方法,触发__call()
- Router __call():创建RouteRegistrar实例,RouteRegistrar初始化赋值router属性为$router,
public function __call($method, $parameters)
{
...
//$method = 'prefix'
if ($method == 'middleware') {
return (new RouteRegistrar($this))->attribute($method, is_array($parameters[0]) ? $parameters[0] : $parameters);
}
//创建RouteRegistrar实例并赋值attributes['prefix'] = 'api',返回RouteRegistrar实例;
return (new RouteRegistrar($this))->attribute($method, $parameters[0]);
}
/**
* Route::prefix('api') ################################### attribute['prefix'] = 'api'
* ->middleware('api')
* ->namespace($this->namespace)
* ->group(base_path('routes/api.php'));
*/
- RouteRegistrar __call()
public function __call($method, $parameters)
{
//如果是方法属于请求类型,则注册Router
if (in_array($method, $this->passthru)) {
return $this->registerRoute($method, ...$parameters);
}
//如果方法属于属性设置,则更新属性
if (in_array($method, $this->allowedAttributes)) {
if ($method == 'middleware') {
return $this->attribute($method, is_array($parameters[0]) ? $parameters[0] : $parameters);
}
return $this->attribute($method, $parameters[0]);
}
}
/**
* Route::prefix('api') ################################### attribute['prefix'] = 'api'
* ->middleware('api') ############################### attribute['middle'] = 'api'
* ->namespace($this->namespace) ##################### attribute['namespace'] = 'App\Http\Controllers'
* ->group(base_path('routes/api.php')); ############# 执行当前实例group()
*/
- RouteRegistrar group():apiRoute基础信息录入完毕,加载执行api.php
public function group($callback)
{
//$router->group();
$this->router->group($this->attributes, $callback);
}
//class Router
public function group(array $attributes, $routes)
{
//$router->groupStack[] = $attributes;
$this->updateGroupStack($attributes);
//require '/routes/api.php'
$this->loadRoutes($routes);
//groupStack出栈
array_pop($this->groupStack);
}
/routes/api.php
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
这里插入两个细节
- 在mapApiRoutes()使用Facade模式由Route关键字获取Router实例,实际上在文件头部使用了use关键字,也就是说并不是使用我们注册的AliasLoader load()进行实现文件加载,而是composer阶段注册的自动加载函数,而这里使用require方式加载的文件,默认命名空间根,所以在此使用Route关键是则使用AliasLoader load()进行class_alias处理
2.Facade使用的Router实例是存储在关联容器中的,而每一次的__call()都会 new RouteRegistrar(),这是一个新的实例,与之前无关
解读api.php
- middleware方法创建RouteRegistrar实例、赋值attributes[‘middleware’] = [‘auth:api’],并返回RouteRegistrar实例
- get方法不在进行attribute赋值,而进行registerRoute($method, …$parameters)
protected function registerRoute($method, $uri, $action = null)
{
//说明一下...$parameters 的方式表示将该方法后续的参数依次存储在$parameters中,在Facade __callStatic()也适用该方式传递参数
//当前参数$method = 'get',$uri = '/user',$action = Closure
//处理$action = ['middleware'=>'auth:api','uses'=>$action];
if (! is_array($action)) {
$action = array_merge($this->attributes, $action ? ['uses' => $action] : []);
}
//执行$router->get('/user',Closure)
return $this->router->{$method}($uri, $this->compileAction($action));
}
//class Router
public function get($uri, $action = null)
{
return $this->addRoute(['GET', 'HEAD'], $uri, $action);
}
protected function addRoute($methods, $uri, $action)
{
return $this->routes->add($this->createRoute($methods, $uri, $action));
}
createRoute() : newRouter()
protected function createRoute($methods, $uri, $action)
{
...
//$this->prefix($uri)拼接前缀 return 'api/user'
$route = $this->newRoute(
$methods, $this->prefix($uri), $action
);//$route instanceof Route
...
}
protected function newRoute($methods, $uri, $action)
{
return (new Route($methods, $uri, $action))
->setRouter($this)
->setContainer($this->container);
}
Route()
//class Route
//初始化赋值$uri,$methods,$action
public function __construct($methods, $uri, $action)
{
$this->uri = $uri;
$this->methods = (array) $methods;
$this->action = $this->parseAction($action);//仍返回当前$action
...
}
public function setRouter(Router $router)
{
$this->router = $router;
return $this;
}
public function setContainer(Container $container)
{
$this->container = $container;
return $this;
}
createRoute() : mergeGroupAttributesIntoRoute($route)
protected function createRoute($methods, $uri, $action)
{
...
//$this->prefix($uri) return 'api/user'
$route = $this->newRoute(
$methods, $this->prefix($uri), $action
);//$route instanceof Route
if ($this->hasGroupStack()) {//true
$this->mergeGroupAttributesIntoRoute($route);
}
...
return $route;
}
protected function mergeGroupAttributesIntoRoute($route)
{
//mergeWithLastGroup()方法整合$route->action与$router->attributes属性
$mergeWithLastGroup = $this->mergeWithLastGroup($route->getAction());
//$mergeWithLastGroup
/**array:5 [▼
* "middleware" => array:2 [▼
* 0 => "api"
* 1 => "auth:api"
* ]
* "uses" => Closure {#213 ▼
* class: "Illuminate\Routing\Router"
* this: Router {#25 …}
* parameters: {▶}
* file: "/usr/local/var/www/auth/routes/api.php"
* line: "15 to 17"
* }
* "namespace" => "App\Http\Controllers"
* "prefix" => "api"
* "where" => []
* ]
*/
//更新$route->action = $mergeWithLastGroup;
$route->setAction($mergeWithLastGroup);
}
createRoute() : addWhereClausesToRoute($route)
protected function createRoute($methods, $uri, $action)
{
...
//$this->prefix($uri) return 'api/user'
$route = $this->newRoute(
$methods, $this->prefix($uri), $action
);//$route instanceof Route
if ($this->hasGroupStack()) {//true
$this->mergeGroupAttributesIntoRoute($route);
}
$this->addWhereClausesToRoute($route);
return $route;
}
protected function addWhereClausesToRoute($route)
{
//赋值$route->wheres[$key] = $route->action['where']
$route->where(array_merge(
$this->patterns, $route->getAction()['where'] ?? []
));
return $route;
}
回到Router addRoute($methods, $uri, $action),执行Route add()
protected function addRoute($methods, $uri, $action)
{
return $this->routes->add($this->createRoute($methods, $uri, $action));
}
//class RouteCollection
public function add(Route $route)
{
$this->addToCollections($route);
$this->addLookups($route);
return $route;
}
//addTocollections($route)
protected function addToCollections($route)
{
$domainAndUri = $route->getDomain().$route->uri(); // api/user
foreach ($route->methods() as $method) {
$this->routes[$method][$domainAndUri] = $route;
}
$this->allRoutes[$method.$domainAndUri] = $route;
}
//赋值$routes->routes['GET']['api/user'] = $route
//赋值$routes->routes['HEAD']['api/user'] = $route
//赋值$routes->allRoutes['HEADapi/user'] = $route
//addLookups()
protected function addLookups($route)
{
$action = $route->getAction();
//设置路由别名
if (isset($action['as'])) {
$this->nameList[$action['as']] = $route;
}
//设置路由对应的控制器
if (isset($action['controller'])) {
$this->addToActionList($action, $route);
}
}
OK,mapApiRoutes()执行完毕,mapWebRoutes()执行流程与之相同,其实api.php文件中的每一个路由信息被存储在一个$route实例中,$route又被存储在$routes->routes数组列表中,以[‘request_action’][‘url’] = $route存储,同时$routes又挂载在$router->routes上
注册匿名函数
//class Illuminate\Foundation\Support\Providers\RouteServiceProvider
public function boot()
{
$this->setRootControllerNamespace();
if ($this->app->routesAreCached()) {
$this->loadCachedRoutes();
} else {
$this->loadRoutes();
//there ...
$this->app->booted(function () {
$this->app['router']->getRoutes()->refreshNameLookups();//别名内容存储于$this->nameList
$this->app['router']->getRoutes()->refreshActionLookups();//控制器内容存储于$this->actionList
});
}
}
//class Container
public function booted($callback)
{
//向服务容器bootedCallbacks中添加匿名函数
$this->bootedCallbacks[] = $callback;
if ($this->isBooted()) {//false,在services.php中['eager']全部启动完毕后$this->booted更新为true,才会执行注册的匿名函数
$this->fireAppCallbacks([$callback]);
}
}
尾声:到此RouterServiceProvider启动结束
感受:有点乱,主要是名字乱,也真是不好其名称啊!不过总比我看同事代码要容易的多😏😏😏