入口

Laravel5.8 入口文件为 public/index.php

  1. $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
  2. $response = $kernel->handle(
  3. $request = Illuminate\Http\Request::capture()
  4. );
  5. $response->send();
  6. $kernel->terminate($request, $response);

创建了一个 Kernel 对象,调用 handler 处理请求,获取返回结果。将返回结果输出到客户端,处理 terminate 操作。

Kernel 中如何处理请求

容器里绑定的是 App\Http\Kernel, 继承于 Illuminate\Foundation\Http\Kernel。

  1. /**
  2. * Handle an incoming HTTP request.
  3. *
  4. * @param \Illuminate\Http\Request $request
  5. * @return \Illuminate\Http\Response
  6. */
  7. public function handle($request)
  8. {
  9. try {
  10. $request->enableHttpMethodParameterOverride();
  11. $response = $this->sendRequestThroughRouter($request);
  12. } catch (Exception $e) {
  13. $this->reportException($e);
  14. $response = $this->renderException($request, $e);
  15. } catch (Throwable $e) {
  16. $this->reportException($e = new FatalThrowableError($e));
  17. $response = $this->renderException($request, $e);
  18. }
  19. $this->app['events']->dispatch(
  20. new Events\RequestHandled($request, $response)
  21. );
  22. return $response;
  23. }

Kernel 中调用 sendRequestThroughRouter 方法,将请求传递到路由处理当中。

  1. /**
  2. * Send the given request through the middleware / router.
  3. *
  4. * @param \Illuminate\Http\Request $request
  5. * @return \Illuminate\Http\Response
  6. */
  7. protected function sendRequestThroughRouter($request)
  8. {
  9. $this->app->instance('request', $request);
  10. Facade::clearResolvedInstance('request');
  11. $this->bootstrap();
  12. return (new Pipeline($this->app))
  13. ->send($request)
  14. ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
  15. ->then($this->dispatchToRouter());
  16. }

在 sendRequestThroughRouter 当中,在 app 中绑定了 request 实例,并解绑掉其他 request 实例对象。这样在程序其他地方都能通过 app()->make(‘request’) 获取到 request 实例对象。

调用 bootstrap 方法,加载引导类。

创建一个 Pipeline 对象,将路由调度与中间件放入调用链当中。所有 request 先经过全局的中间件,然后在通过路由分发。

  1. /**
  2. * Get the route dispatcher callback.
  3. *
  4. * @return \Closure
  5. */
  6. protected function dispatchToRouter()
  7. {
  8. return function ($request) {
  9. $this->app->instance('request', $request);
  10. return $this->router->dispatch($request);
  11. };
  12. }

因为 Piepline 调用链都是一个个的回调方法,所以在 dispatchToRouter 返回了一个匿名回调函数。使用 Kernel 的 route 属性进行调度。

Kernel 的 route 是一个 Illuminate\Routing\Router 对象。

路由调度

  1. //Illuminate\Routing\Router
  2. /**
  3. * Dispatch the request to the application.
  4. *
  5. * @param \Illuminate\Http\Request $request
  6. * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
  7. */
  8. public function dispatch(Request $request)
  9. {
  10. $this->currentRequest = $request;
  11. return $this->dispatchToRoute($request);
  12. }
  13. /**
  14. * Dispatch the request to a route and return the response.
  15. *
  16. * @param \Illuminate\Http\Request $request
  17. * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
  18. */
  19. public function dispatchToRoute(Request $request)
  20. {
  21. return $this->runRoute($request, $this->findRoute($request));
  22. }
  23. /**
  24. * Return the response for the given route.
  25. *
  26. * @param \Illuminate\Http\Request $request
  27. * @param \Illuminate\Routing\Route $route
  28. * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse
  29. */
  30. protected function runRoute(Request $request, Route $route)
  31. {
  32. $request->setRouteResolver(function () use ($route) {
  33. return $route;
  34. });
  35. $this->events->dispatch(new Events\RouteMatched($route, $request));
  36. return $this->prepareResponse($request,
  37. $this->runRouteWithinStack($route, $request)
  38. );
  39. }

从上面的方法可以看出,最终通过 findRoute 查找当前匹配的路由对象,并调用 runRoute 处理请求返回结果。

怎么找到路由的

  1. //Illuminate\Routing\Router
  2. /**
  3. * Find the route matching a given request.
  4. *
  5. * @param \Illuminate\Http\Request $request
  6. * @return \Illuminate\Routing\Route
  7. */
  8. protected function findRoute($request)
  9. {
  10. $this->current = $route = $this->routes->match($request);
  11. $this->container->instance(Route::class, $route);
  12. return $route;
  13. }

对路由的匹配,是通过 routes 这个路由 Collections 去匹配的。

  1. //Illuminate\Routing\RouteCollection
  2. /**
  3. * Find the first route matching a given request.
  4. *
  5. * @param \Illuminate\Http\Request $request
  6. * @return \Illuminate\Routing\Route
  7. *
  8. * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
  9. */
  10. public function match(Request $request)
  11. {
  12. $routes = $this->get($request->getMethod());
  13. // First, we will see if we can find a matching route for this current request
  14. // method. If we can, great, we can just return it so that it can be called
  15. // by the consumer. Otherwise we will check for routes with another verb.
  16. $route = $this->matchAgainstRoutes($routes, $request);
  17. if (! is_null($route)) {
  18. return $route->bind($request);
  19. }
  20. // If no route was found we will now check if a matching route is specified by
  21. // another HTTP verb. If it is we will need to throw a MethodNotAllowed and
  22. // inform the user agent of which HTTP verb it should use for this route.
  23. $others = $this->checkForAlternateVerbs($request);
  24. if (count($others) > 0) {
  25. return $this->getRouteForMethods($request, $others);
  26. }
  27. throw new NotFoundHttpException;
  28. }
  29. /**
  30. * Determine if a route in the array matches the request.
  31. *
  32. * @param array $routes
  33. * @param \Illuminate\Http\Request $request
  34. * @param bool $includingMethod
  35. * @return \Illuminate\Routing\Route|null
  36. */
  37. protected function matchAgainstRoutes(array $routes, $request, $includingMethod = true)
  38. {
  39. [$fallbacks, $routes] = collect($routes)->partition(function ($route) {
  40. return $route->isFallback;
  41. });
  42. return $routes->merge($fallbacks)->first(function ($value) use ($request, $includingMethod) {
  43. return $value->matches($request, $includingMethod);
  44. });
  45. }

先通过请求的方法获取当前方法下可用的路由集合,在从这些集合中去遍历获取第一个匹配的路由。集合中每个 item 是一个 Illuminate\Routing\Router 对象。因此最终判断路由与请求是否匹配调用的是 Illuminate\Routing\Router 中的 matches 方法。

  1. //Illuminate\Routing\Router
  2. /**
  3. * Determine if the route matches given request.
  4. *
  5. * @param \Illuminate\Http\Request $request
  6. * @param bool $includingMethod
  7. * @return bool
  8. */
  9. public function matches(Request $request, $includingMethod = true)
  10. {
  11. $this->compileRoute();
  12. foreach ($this->getValidators() as $validator) {
  13. if (! $includingMethod && $validator instanceof MethodValidator) {
  14. continue;
  15. }
  16. if (! $validator->matches($this, $request)) {
  17. return false;
  18. }
  19. }
  20. return true;
  21. }
  22. /**
  23. * Get the route validators for the instance.
  24. *
  25. * @return array
  26. */
  27. public static function getValidators()
  28. {
  29. if (isset(static::$validators)) {
  30. return static::$validators;
  31. }
  32. // To match the route, we will use a chain of responsibility pattern with the
  33. // validator implementations. We will spin through each one making sure it
  34. // passes and then we will know if the route as a whole matches request.
  35. return static::$validators = [
  36. new UriValidator, new MethodValidator,
  37. new SchemeValidator, new HostValidator,
  38. ];
  39. }

在 Illuminate\Routing\Router 提供了四个默认的验证器,当四个验证器通过的时候才会匹配成功。四个验证器分别是 UriValidator 验证访问路径,MethodValidator 验证请求方法,SchemeValidator 验证访问协议,HostValidator 验证域名。其中对 uri 的验证内部是使用正则表达式验证。

路由调度怎么处理请求

  1. //Illuminate\Routing\Router
  2. /**
  3. * Run the given route within a Stack "onion" instance.
  4. *
  5. * @param \Illuminate\Routing\Route $route
  6. * @param \Illuminate\Http\Request $request
  7. * @return mixed
  8. */
  9. protected function runRouteWithinStack(Route $route, Request $request)
  10. {
  11. $shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
  12. $this->container->make('middleware.disable') === true;
  13. $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route);
  14. return (new Pipeline($this->container))
  15. ->send($request)
  16. ->through($middleware)
  17. ->then(function ($request) use ($route) {
  18. return $this->prepareResponse(
  19. $request, $route->run()
  20. );
  21. });
  22. }
  23. /**
  24. * Run the route action and return the response.
  25. *
  26. * @return mixed
  27. */
  28. public function run()
  29. {
  30. $this->container = $this->container ?: new Container;
  31. try {
  32. if ($this->isControllerAction()) {
  33. return $this->runController();
  34. }
  35. return $this->runCallable();
  36. } catch (HttpResponseException $e) {
  37. return $e->getResponse();
  38. }
  39. }

路由对请求的处理也是返回一个 Pipeline, 先将请求通过中间件,然后在执行路由的 run 方法。在 run 方法里面判断当前是执行控制器方法还是回调方法,根据不同类型分开执行。

怎么执行

  1. /**
  2. * Checks whether the route's action is a controller.
  3. *
  4. * @return bool
  5. */
  6. protected function isControllerAction()
  7. {
  8. return is_string($this->action['uses']);
  9. }
  10. /**
  11. * Run the route action and return the response.
  12. *
  13. * @return mixed
  14. */
  15. protected function runCallable()
  16. {
  17. $callable = $this->action['uses'];
  18. return $callable(...array_values($this->resolveMethodDependencies(
  19. $this->parametersWithoutNulls(), new ReflectionFunction($this->action['uses'])
  20. )));
  21. }
  22. /**
  23. * Run the route action and return the response.
  24. *
  25. * @return mixed
  26. *
  27. * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
  28. */
  29. protected function runController()
  30. {
  31. return $this->controllerDispatcher()->dispatch(
  32. $this, $this->getController(), $this->getControllerMethod()
  33. );
  34. }

通过当前路由的 action 配置判断是否是控制器或者回调方法。从代码中可以看到,其实就是我们路由配置中的第二个参数对应到 action[‘user’]。当我们第二参数是一个字符串的时候则认为是控制器方法,将请求转发到控制器里去处理。否则执行回调函数处理。

到这里,我们的请求就真的到达了我们的控制器的方法中,开始执行我们写的代码了。