Laravel源码剖析之请求的处理中(五)

建议先看前几篇文章,不然可能会看不懂,这是我的Laravel专栏

 Laravel_Attitude_do_it的博客-CSDN博客

感兴趣的可以去瞅一瞅。

正文开始

继续上篇的内容,我们分析下框架是怎么分配路由的。

我们先定位到dispatchToRouter方法

   /**
     * Get the route dispatcher callback.
     * 获取路由调度器回调
     * @return \Closure
     */
    protected function dispatchToRouter()
    {
        return function ($request) {
            //绑定request实例到容器
            $this->app->instance('request', $request);
            //往下面看dispatch方法
            return $this->router->dispatch($request);
        };
    }

    /**
     * Dispatch the request to the application.
     * 将请求分派到应用程序 
     * @param  \Illuminate\Http\Request  $request
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function dispatch(Request $request)
    {
        $this->currentRequest = $request;
        //往下面看dispatchToRoute方法
        return $this->dispatchToRoute($request);
    }

    /**
     * Dispatch the request to a route and return the response.
     * 分派请求到路由并且放回一个响应结果
     * @param  \Illuminate\Http\Request  $request
     * @return \Symfony\Component\HttpFoundation\Response
     */
    public function dispatchToRoute(Request $request)
    {
        //往下面看runRoute和findRoute方法
        return $this->runRoute($request, $this->findRoute($request));
    }

    /**
     * Find the route matching a given request.
     * 根据请求找到匹配到的一个路由
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Routing\Route
     */
    protected function findRoute($request)
    {
        // 匹配路由
        $this->current = $route = $this->routes->match($request);
        //把匹配到的路由绑定到容器
        $this->container->instance(Route::class, $route);

        return $route;
    }

    /*
     * Find the first route matching a given request.
     * 根据请求找到匹配到的一个路由
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Routing\Route
     *
     * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
     */
    public function match(Request $request)
    {
        //根据请求方法(post,get...)获取路由数组
        $routes = $this->get($request->getMethod());

        // First, we will see if we can find a matching route for this current request
        // method. If we can, great, we can just return it so that it can be called
        // by the consumer. Otherwise we will check for routes with another verb.
        // 首先,我们将看到如果我们为当前的请求方法可以匹配到路由。如果我们可以,很好,我们可以
        // 仅仅返回它那么它可以通过客户消费者被调用。否则,我们将使用另一个动词检查路由
        // 往下看matchAgainstRoutes方法
        $route = $this->matchAgainstRoutes($routes, $request);
        
        if (! is_null($route)) {
            //将路由绑定到给定的执行请求。
            return $route->bind($request);
        }

        // If no route was found we will now check if a matching route is specified by
        // another HTTP verb. If it is we will need to throw a MethodNotAllowed and
        // inform the user agent of which HTTP verb it should use for this route.
        $others = $this->checkForAlternateVerbs($request);

        if (count($others) > 0) {
            return $this->getRouteForMethods($request, $others);
        }

        throw new NotFoundHttpException;
    }

    /**
     * Determine if a route in the array matches the request.
     * 确定数组中的路由是否与请求匹配
     * @param  array  $routes
     * @param  \Illuminate\Http\Request  $request
     * @param  bool  $includingMethod
     * @return \Illuminate\Routing\Route|null
     */
    protected function matchAgainstRoutes(array $routes, $request, $includingMethod = true)
    {
        [$fallbacks, $routes] = collect($routes)->partition(function ($route) {
            return $route->isFallback;
        });
        //放回匹配到的第一个路由,这也是为什么重复路由,后边路由不生效的原因
        return $routes->merge($fallbacks)->first(function ($value) use ($request, $includingMethod) {
            return $value->matches($request, $includingMethod);
        });
    }

最后会执行runRoute方法:

   /**
     * Return the response for the given route.
     * 为路由返回一个响应结果
     * @param  \Illuminate\Http\Request  $request 请求实例
     * @param  \Illuminate\Routing\Route  $route  刚刚匹配到的路由实例
     * @return \Symfony\Component\HttpFoundation\Response
     */
    protected function runRoute(Request $request, Route $route)
    {
        //设置路由的回调方法
        $request->setRouteResolver(function () use ($route) {
            return $route;
        });
        
        //触发路由绑定的事件
        $this->events->dispatch(new RouteMatched($route, $request));
        //注意这里会返回Response实例
        return $this->prepareResponse($request,
            $this->runRouteWithinStack($route, $request)
        );
    }

    /*
     * Run the given route within a Stack "onion" instance.
     * 
     * @param  \Illuminate\Routing\Route  $route
     * @param  \Illuminate\Http\Request  $request
     * @return mixed
     */
    protected function runRouteWithinStack(Route $route, Request $request)
    {
      
        $shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
                                $this->container->make('middleware.disable') === true;
        //获取路由绑定的中间件
        $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route);

        //这个过程在上篇中有详解此处不再详细描述,只不过这里的then方法调用跟上篇的有区别,
        //跟之前一样,then方法的参数同样是一个闭包,但是这个闭包中调用到了Route类的run方法
        //在自定义路由时,路由绑定的方法也是通过这个方法被调用的,下边咱们来看一下run方法中
        //都做了什么,注意这里会返回Response实例
        return (new Pipeline($this->container))
                        ->send($request)
                        ->through($middleware)
                        ->then(function ($request) use ($route) {
                           //prepareResponse方法最终会调用到Symfony\Component\HttpFoundation\Response类的prepare方法,会返回一个Resoponse实例
                            return $this->prepareResponse(
                                $request, $route->run()
                            );
                        });
    }

    /**
     * Run the route action and return the response.
     * 运行路由绑定的方法并且返回响应结果
     * @return mixed
     */
    public function run()
    {
        $this->container = $this->container ?: new Container;

        try {
            //往下看isControllerAction方法
            if ($this->isControllerAction()) {
                //如果是控制器方法,则执行这个方法,往下看runController方法
              // Route::get('/', 'xxxController@index');或者Route::get('/', [xxxController::class,'index']);这种路由会调用这里
                return $this->runController();
            }
            //如果不是控制器方法,则直接调用闭包,往下看runCallable方法
                  // Route::get('/', function () {
                //            return view('welcome');
                //    });
            //这种路由会直接调用function
            return $this->runCallable();
        } catch (HttpResponseException $e) {
            return $e->getResponse();
        }
    }

    /**
     * Checks whether the route's action is a controller.
     * 检查路由的操作是否为控制器
     * @return bool
     */
    protected function isControllerAction()
    {
       // ​对$this->action['uses']不理解的小伙伴,可以先看这边文章 
       // https://blog.csdn.net/Attitude_do_it/article/details/121543993
        return is_string($this->action['uses']);
    }

    /**
     * Run the route action and return the response.
     *
     * @return mixed
     *
     * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
     */
    protected function runController()
    {
        //这里会解析出类和类的方法并执行返回结果,往下看controllerDispatcher,dispatch,getController,getControllerMethod方法
        return $this->controllerDispatcher()->dispatch(
            $this, $this->getController(), $this->getControllerMethod()
        );
    }

    /**
     * Get the dispatcher for the route's controller.
     * 这里会返回ControllerDispatcher类的实例
     * @return \Illuminate\Routing\Contracts\ControllerDispatcher
     */
    public function controllerDispatcher()
    {
        if ($this->container->bound(ControllerDispatcherContract::class)) {
            return $this->container->make(ControllerDispatcherContract::class);
        }

        return new ControllerDispatcher($this->container);
    }

    /**
     * Get the controller instance for the route.
     * 从route实例得到控制器
     * @return mixed
     */
    public function getController()
    {
        if (! $this->controller) {
            //往下看parseControllerCallback
            $class = $this->parseControllerCallback()[0];

            $this->controller = $this->container->make(ltrim($class, '\\'));
        }

        return $this->controller;
    }

    /**
     * Get the controller method used for the route.
     * 从route实例中得到控制器方法
     * @return string
     */
    protected function getControllerMethod()
    {
        //往下看parseControllerCallback
        return $this->parseControllerCallback()[1];
    }

    /**
     * Parse the controller.
     * 解析出控制器
     * @return array
     */
    protected function parseControllerCallback()
    {
        return Str::parseCallback($this->action['uses']);
    }

    /**
     * Parse a Class[@]method style callback into class and method.
     * 解析出控制器和方法
     * @param  string  $callback
     * @param  string|null  $default
     * @return array<int, string|null>
     */
    public static function parseCallback($callback, $default = null)
    {    
        //如果包含@则分割成数组
        return static::contains($callback, '@') ? explode('@', $callback, 2) : [$callback, $default];
    }


    /**
     * 这里调用的是\Illuminate\Routing\Contracts\ControllerDispatcher的dispatch方法
     * Dispatch a request to a given controller and method.
     * 分派请求到控制器和方法
     * @param  \Illuminate\Routing\Route  $route
     * @param  mixed  $controller
     * @param  string  $method
     * @return mixed
     */
    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));
    }

最终在dispatch方法的最后一行中,我们看到了想要的类方法的执行,到这里请求的处理大多半就算是完成了,下篇我们来讲Laravel是怎么进行请求完的后置操作的

猜你喜欢

转载自blog.csdn.net/Attitude_do_it/article/details/121540066