1

接着上篇$app->run();
--------------------分割线------------------------

$app是Application的实例,但是Application.php文件中找不到run方法
在类内部看到use ConcernsRoutesRequests,打开找到了run方法,
run方法注释写的是主要用于运行应用以及发送响应

    /**
     * Run the application and send the response.
     *
     * @param  SymfonyRequest|null  $request
     * @return void
     */
    public function run($request = null)
    {
        $response = $this->dispatch($request);
        if ($response instanceof SymfonyResponse) {
            $response->send();
        } else {
            echo (string) $response;
        }

        if (count($this->middleware) > 0) {
            $this->callTerminableMiddleware($response);
        }
    }

主要看dispatch方法

    /**
     * Dispatch the incoming request.
     *
     * @param  SymfonyRequest|null  $request
     * @return Response
     */
    public function dispatch($request = null)
    {
        list($method, $pathInfo) = $this->parseIncomingRequest($request);
        try {
            return $this->sendThroughPipeline($this->middleware, function () use ($method, $pathInfo) {
                if (isset($this->router->getRoutes()[$method.$pathInfo])) {
                    return $this->handleFoundRoute([true, $this->router->getRoutes()[$method.$pathInfo]['action'], []]);
                }

                return $this->handleDispatcherResponse(
                    $this->createDispatcher()->dispatch($method, $pathInfo)
                );
            });
        } catch (Exception $e) {
            return $this->prepareResponse($this->sendExceptionToHandler($e));
        } catch (Throwable $e) {
            return $this->prepareResponse($this->sendExceptionToHandler($e));
        }
    }

因为请求的URL是 api.com/index.php/
$method.$pathInfo 打印出来是 array(1) { [0]=> string(4) "GET/" }
$this->router->getRoutes()是获取在web.php定义的所有路由,返回的是一个数组形式的数据:

array (size=3)
  'GET/' => 
    array (size=3)
      'method' => string 'GET' (length=3)
      'uri' => string '/' (length=1)
      'action' => 
        array (size=1)
          0 => 
            object(Closure)[10]
              public 'static' => 
                array (size=1)
                  'router' => 
                    object(Laravel\Lumen\Routing\Router)[6]
                      public 'app' =>
                                ......

所以isset($this->router->getRoutes()[$method.$pathInfo]) 的结果就是 true
接着调用handleFoundRoute方法

    /**
     * Handle a route found by the dispatcher.
     *
     * @param  array  $routeInfo
     * @return mixed
     */
    protected function handleFoundRoute($routeInfo)
    {
        $this->currentRoute = $routeInfo;

        $this['request']->setRouteResolver(function () {
            return $this->currentRoute;
        });

        $action = $routeInfo[1];

        // Pipe through route middleware...
        if (isset($action['middleware'])) {
            $middleware = $this->gatherMiddlewareClassNames($action['middleware']);

            return $this->prepareResponse($this->sendThroughPipeline($middleware, function () {
                return $this->callActionOnArrayBasedRoute($this['request']->route());
            }));
        }

        return $this->prepareResponse(
            $this->callActionOnArrayBasedRoute($routeInfo)
        );
    }
    

如果有配置中间件的话还会有中间件的处理(后面再写中间件的学习)
匹配到web.php里面的这个路由后
看到这段代码$this->callActionOnArrayBasedRoute($routeInfo)

    /**
     * Call the Closure on the array based route.
     *
     * @param  array  $routeInfo
     * @return mixed
     */
    protected function callActionOnArrayBasedRoute($routeInfo)
    {
        $action = $routeInfo[1];

        if (isset($action['uses'])) {
            return $this->prepareResponse($this->callControllerAction($routeInfo));
        }

        foreach ($action as $value) {
            if ($value instanceof Closure) {
                $closure = $value->bindTo(new RoutingClosure);
                break;
            }
        }

        try {
            return $this->prepareResponse($this->call($closure, $routeInfo[2]));
        } catch (HttpResponseException $e) {
            return $e->getResponse();
        }
    }

里面就是处理路由对应的响应方式,并且组装响应数据准备返回

定义的路由是

$router->get('/', function () use ($router) {
    return $router->app->version();
});

匹配到路由之后对应的处理是

function () use ($router) {
    return $router->app->version(); //'Lumen (5.5.2) (Laravel Components 5.5.*)'
}

所以$this->call($closure, $routeInfo[2])
得到的就是上一节的'Lumen (5.5.2) (Laravel Components 5.5.*)'
然后把处理后得到的数据传进$this->prepareResponse()方法中组装响应请求的数据
组装完毕后,回到最初的run方法接着往下$response->send()
追踪到SymfonyComponentHttpFoundationResponse这个类里面(组装响应数据也是这里面)

    /**
     * Sends content for the current web response.
     *
     * @return $this
     */
    public function sendContent()
    {
        echo $this->content;

        return $this;
    }

    /**
     * Sends HTTP headers and content.
     *
     * @return $this
     */
    public function send()
    {
        $this->sendHeaders();
        $this->sendContent();

        if (function_exists('fastcgi_finish_request')) {
            fastcgi_finish_request();
        } elseif ('cli' !== PHP_SAPI) {
            static::closeOutputBuffers(0, true);
        }

        return $this;
    }

从这里看出,那段'Lumen (5.5.2) (Laravel Components 5.5.*)'就是在这里echo出来的

到此为止,可以大概知道了框架从请求到响应的基本流程
其中还有一些中间件,事件监听等知识后续学习补上,有什么不对记得指出,互相学习~


hano
192 声望16 粉丝

WEB后端开发, PHP&GO