Laravel5.5解析-RouteServiceProvider

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();
	});

这里插入两个细节

  1. 在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启动结束

感受:有点乱,主要是名字乱,也真是不好其名称啊!不过总比我看同事代码要容易的多😏😏😏

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值