内容简介:这块代码是在展开完整的服务提供者后面在使用中会涉及这里注册的对象,红框内就是注册的绑定关系。
这块代码是在 Application
的构造函数中加载的
public function __construct($basePath = null) { ... $this->registerBaseServiceProviders(); ... } 复制代码
protected function registerBaseServiceProviders() { ... $this->register(new RoutingServiceProvider($this)); ... } 复制代码
展开完整的服务提供者
<?php namespace Illuminate\Routing; use Illuminate\Support\ServiceProvider; use Psr\Http\Message\ResponseInterface; use Zend\Diactoros\Response as PsrResponse; use Psr\Http\Message\ServerRequestInterface; use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory; use Illuminate\Contracts\View\Factory as ViewFactoryContract; use Illuminate\Contracts\Routing\ResponseFactory as ResponseFactoryContract; use Illuminate\Routing\Contracts\ControllerDispatcher as ControllerDispatcherContract; class RoutingServiceProvider extends ServiceProvider { public function register() { $this->registerRouter(); $this->registerUrlGenerator(); $this->registerRedirector(); $this->registerPsrRequest(); $this->registerPsrResponse(); $this->registerResponseFactory(); $this->registerControllerDispatcher(); } protected function registerRouter() { $this->app->singleton('router', function ($app) { return new Router($app['events'], $app); }); } protected function registerUrlGenerator() { $this->app->singleton('url', function ($app) { $routes = $app['router']->getRoutes(); $app->instance('routes', $routes); $url = new UrlGenerator( $routes, $app->rebinding( 'request', $this->requestRebinder() ), $app['config']['app.asset_url'] ); $url->setSessionResolver(function () { return $this->app['session']; }); $url->setKeyResolver(function () { return $this->app->make('config')->get('app.key'); }); $app->rebinding('routes', function ($app, $routes) { $app['url']->setRoutes($routes); }); return $url; }); } protected function requestRebinder() { return function ($app, $request) { $app['url']->setRequest($request); }; } protected function registerRedirector() { $this->app->singleton('redirect', function ($app) { $redirector = new Redirector($app['url']); if (isset($app['session.store'])) { $redirector->setSession($app['session.store']); } return $redirector; }); } protected function registerPsrRequest() { $this->app->bind(ServerRequestInterface::class, function ($app) { return (new DiactorosFactory)->createRequest($app->make('request')); }); } protected function registerPsrResponse() { $this->app->bind(ResponseInterface::class, function () { return new PsrResponse; }); } protected function registerResponseFactory() { $this->app->singleton(ResponseFactoryContract::class, function ($app) { return new ResponseFactory($app[ViewFactoryContract::class], $app['redirect']); }); } protected function registerControllerDispatcher() { $this->app->singleton(ControllerDispatcherContract::class, function ($app) { return new ControllerDispatcher($app); }); } } 复制代码
后面在使用中会涉及这里注册的对象,红框内就是注册的绑定关系。
启动在这里并没有完成,这仅仅是启动系统的基础路由,在 app.php
中还有一个路由服务提供者 RouteServiceProvider
<?php namespace App\Providers; use Illuminate\Support\Facades\Route; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; class RouteServiceProvider extends ServiceProvider { protected $namespace = 'App\Http\Controllers'; public function boot() { // boot() 方法是在服务提供者所有 register() 方法执行完成之后在统一执行的 // 这段代码最后会调用 $this->map(); parent::boot(); } public function map() { $this->mapApiRoutes(); $this->mapWebRoutes(); } // 这一块的逻辑非常复杂就不展开了,主要功能就是优先加载 cache/routes.php,如果不存在 // 则从给定的路径加载路由文件 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')); } } 复制代码
内核启动
注册完成之后就是开始处理,是从内核的 handle()
方法开始处理请求
protected function sendRequestThroughRouter($request) { ... return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); } 复制代码
这段代码在 【Laravel-海贼王系列】第七章,Pipeline 类解析 解析过了 不了解执行逻辑请先看上一篇哦~
这里会在运行完中间件之后最后运行 $this->dispatchToRouter()
这个方法。
$this->router
对象是在内核的构造函数注入的 \Illuminate\Routing\Router
对象
protected function dispatchToRouter() { return function ($request) { $this->app->instance('request', $request); return $this->router->dispatch($request); }; } 复制代码
那么我们接着看 dispatch
方法
public function dispatch(Request $request) { $this->currentRequest = $request; return $this->dispatchToRoute($request); } 复制代码
转发一个请求给路由返回一个响应对象
public function dispatchToRoute(Request $request) { return $this->runRoute($request, $this->findRoute($request)); } 复制代码
找到路由
我的理解: router
代表路由器, route
则是代表一次路由的对象,
所有路由器的功能就是执行,派发路由对象。所以我们需要先通过请求来拿到一个路由对象
protected function findRoute($request) { $this->current = $route = $this->routes->match($request); // 绑定最新的 $route 对象到容器 $this->container->instance(Route::class, $route); // 返回路由 return $route; } 复制代码
继续分析 $this->routes->match($request);
,
这里的 $this->routes
是构造函数注入的 Illuminate\Routing\RouteCollection
对象
public function match(Request $request) { $routes = $this->get($request->getMethod()); $route = $this->matchAgainstRoutes($routes, $request); if (! is_null($route)) { return $route->bind($request); } $others = $this->checkForAlternateVerbs($request); if (count($others) > 0) { return $this->getRouteForMethods($request, $others); } throw new NotFoundHttpException; } 复制代码
$routes
对象这里面的值来自与路由缓存文件或者路由文件解析结果
继续看 $route = $this->matchAgainstRoutes($routes, $request);
执行结果从请求中匹配对应路由并返回
如果没有匹配的路由则使用请求方法以外的方法继续匹配
public static $verbs = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']; protected function checkForAlternateVerbs($request) { $methods = array_diff(Router::$verbs, [$request->getMethod()]); $others = []; foreach ($methods as $method) { if (! is_null($this->matchAgainstRoutes($this->get($method), $request, false))) { $others[] = $method; } } return $others; } 复制代码
执行完成返回 $other
数组,如果还是没有则抛出 throw new NotFoundHttpException;
这里不详细叙述了,如果匹配成功我们将得到一个 Illuminate\Routing\Route
对象传递下去。
派发路由
当我们得到路由对象之后就是派发它了,根据给定的路由返回响应对象
protected function runRoute(Request $request, Route $route) { // 将这个闭包设置到 request 对象的 $this->routeResolver 成员上 $request->setRouteResolver(function () use ($route) { return $route; }); // 执行路由匹配的事件,框架刚启动的时候这里什么都不做 $this->events->dispatch(new Events\RouteMatched($route, $request)); return $this->prepareResponse($request, $this->runRouteWithinStack($route, $request) ); } 复制代码
获取响应
执行到这里就已经到了最后的部分了
return $this->prepareResponse($request, $this->runRouteWithinStack($route, $request) ); 复制代码
这个方法就是将 $request
和 $response
根据里面的属性封装好数据返回而已。
public function prepareResponse($request, $response) { return static::toResponse($request, $response); } 复制代码
重点看 $this->runRouteWithinStack($route, $request)
这段话才是将请求传递到控制器关键!
protected function runRouteWithinStack(Route $route, Request $request) { $shouldSkipMiddleware = $this->container->bound('middleware.disable') && $this->container->make('middleware.disable') === true; $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route); return (new Pipeline($this->container)) ->send($request) ->through($middleware) ->then(function ($request) use ($route) { return $this->prepareResponse( $request, $route->run() ); }); } 复制代码
又到了这种用法,不理解执行逻辑请看第七章,
根据 Pipeline
的使用远离我们最后在通过所有 $middleware
之后会将 $requeset
传递给闭包来结束
所以这是终点!
function ($request) use ($route) { return $this->prepareResponse( $request, $route->run() ); } 复制代码
刚才说过了 $this-prepareResponse()
这个方法没什么亮点就是
将请求和响应对象封装返回,所有我们应该知道了,$route->run() 将返回 response
对象!
控制器闪亮登场
来吧,经历了无数令人发指的封装希望后面一片坦途, run()
public function run() { $this->container = $this->container ?: new Container; try { if ($this->isControllerAction()) { return $this->runController(); } return $this->runCallable(); } catch (HttpResponseException $e) { return $e->getResponse(); } } 复制代码
总算看到了 runController()
方法了,想必路由跨过山和大海最总的归宿也到这儿了
$this-isControllerAction()
是判断路由是闭包还是字符串
如果是字符串向上图红框中的内容则执行
protected function runController() { return $this->controllerDispatcher()->dispatch( $this, $this->getController(), $this->getControllerMethod() ); } 复制代码
这里是调用 Illuminate\Routing\ControllerDispatcher
的 dispatch
方法
public function dispatch(Route $route, $controller, $method) { $parameters = $this->resolveClassMethodDependencies( $route->parametersWithoutNulls(), $controller, $method ); // 从容器获取当前类构造函数依赖和方法依赖参数 if (method_exists($controller, 'callAction')) { return $controller->callAction($method, $parameters); } return $controller->{$method}(...array_values($parameters)); } 复制代码
callAction
来自所有控制器基础的 Illuminate\Routing\Controller
public function callAction($method, $parameters) { return call_user_func_array([$this, $method], $parameters); } 复制代码
没什么好讲的其实就是调用控制器对应的方法。
如果路由是闭包形式,则直接抽取路由对象中的闭包进行调用
protected function runCallable() { $callable = $this->action['uses']; // 通过容器抽取依赖的参数传入闭包运行 return $callable(...array_values($this->resolveMethodDependencies( $this->parametersWithoutNulls(), new ReflectionFunction($this->action['uses']) ))); } 复制代码
结语
总算结束了, Laravel
路由在启动阶段注册了非常多的类,
1. Application
构造阶段 $this->register(new RoutingServiceProvider($this));
2. Kernel handle() bootstrap()
阶段加载服务提供者的时候包含了 App\Providers\RouteServiceProvider::class,
这两个阶段注册加上加载的逻辑是非常复杂,但是目的也很简单从就是从路由文件转成路由对象的过程,没有力气分析进去。
其他的就是最后一直调用到控制器的过程,其中最后的 resolveClassMethodDependencies
, resolveMethodDependencies
也是非常值得研究的代码。
以上所述就是小编给大家介绍的《【Laravel-海贼王系列】第十三章,路由&控制器解析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 【Laravel-海贼王系列】第八章, Provider 功能解析
- 【Laravel-海贼王系列】第十四章,Session 解析
- 【Laravel-海贼王系列】第十二章,Facade 模式解析
- 【Laravel-海贼王系列】第十章,Job&队列存储端实现
- 【Laravel-海贼王系列】第十七章,Laravel 那些骚操作
- 慢雾安全 海贼王:从DApp亡灵军团,细说区块链安全
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。