index.php
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);//处理请求$response = $kernel->handle( //请求实例的创建 $request = Illuminate\Http\Request::capture());
\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php
/** * Handle an incoming HTTP request. *处理一个http请求 * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function handle($request) { try { //在请求过程中会添加CSRF保护,服务端会发送一个csrf令牌给客户端(cookie) $request->enableHttpMethodParameterOverride(); //请求处理,通过路由传输请求实例 $response = $this->sendRequestThroughRouter($request); } catch (Exception $e) { $this->reportException($e); $response = $this->renderException($request, $e); } catch (Throwable $e) { $this->reportException($e = new FatalThrowableError($e)); $response = $this->renderException($request, $e); } $this->app['events']->dispatch( new Events\RequestHandled($request, $response) ); return $response; }
/** * Send the given request through the middleware / router. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ protected function sendRequestThroughRouter($request) { $this->app->instance('request', $request); Facade::clearResolvedInstance('request'); //针对请求为应用程序'拔靴带' 执行bootstrap类的数组 //在请求处理阶段共有7个环节,每一个环节由一个类来实现的 //而且每个类都会有一个bootstrap()函数用于实现准备工作 $this->bootstrap(); return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); }
dispatchToRouter()
/** * Get the route dispatcher callback. *设置路由分发回调函数 * @return \Closure */ protected function dispatchToRouter() { return function ($request) { $this->app->instance('request', $request); return $this->router->dispatch($request); }; }
vendor\laravel\framework\src\Illuminate\Routing\Router.php
/** * Dispatch the request to the application. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse */ public function dispatch(Request $request) { $this->currentRequest = $request; return $this->dispatchToRoute($request); }
//分发请求到指定路由并返回一个响应
public function dispatchToRoute(Request $request) { return $this->runRoute($request, $this->findRoute($request)); }
protected function runRoute(Request $request, Route $route) { $request->setRouteResolver(function () use ($route) { return $route; }); $this->events->dispatch(new Events\RouteMatched($route, $request)); return $this->prepareResponse($request, $this->runRouteWithinStack($route, $request) ); }
//通过实例栈启动给定的路由
1 protected function runRouteWithinStack(Route $route, Request $request) 2 { 3 $shouldSkipMiddleware = $this->container->bound('middleware.disable') && 4 $this->container->make('middleware.disable') === true; 5 6 $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route); 7 8 return (new Pipeline($this->container)) 9 ->send($request)10 ->through($middleware)11 ->then(function ($request) use ($route) {12 return $this->prepareResponse(13 $request, $route->run()14 );15 });16 }
在路由信息存储实例中,通过“ $route = Sthis -> findRoute ( $request ) ; ”来查找请求对应的路由实例,
查找主要是根据请求的方法和请求 URI 来实现对应,当查找到请求对应的路由后,请求将会传递到对应的路由中去处理
,即’ " $route-> run ( $ request ) "
vendor\laravel\framework\src\Illuminate\Routing\Route.php
执行路由动作并返回响应
1 public function run() 2 { 3 $this->container = $this->container ?: new Container; 4 5 try { 6 if ($this->isControllerAction()) { 7 return $this->runController(); 8 } 9 10 return $this->runCallable();11 } catch (HttpResponseException $e) {12 return $e->getResponse();13 }14 }
//将请求发送到常规分发器去处理
1 protected function runController()2 {3 return $this->controllerDispatcher()->dispatch(4 $this, $this->getController(), $this->getControllerMethod()5 );6 }
在清求对应的路由中,会检测是否使用常规的控制分发器去处理,在初始的 Larave 框架中使用的是常规控制分发器,
通过服务容器自动生成这个控制分发器,这个服务是通过服务提供者 ControUerservicoProvider 注册的,下一步将会把请求及路由中关于处理函数的信息交给控制分发器去处理,
这里对路由中关于处理函数的信息是以控制器类名和函数名给出的,即$class和$method
控制器的生成:
控制器生成在控制分发器中,将会根据路由提供的响应函数信息来实例化控制器类,并调用对应的响应函数生成响应的内容部分厂.下面给出部分源码:
vendor\laravel\framework\src\Illuminate\Routing\ControllerDispatcher.php
向给定的控制器和方法发送请求。
1 public function dispatch(Route $route, $controller, $method) 2 { 3 $parameters = $this->resolveClassMethodDependencies( 4 $route->parametersWithoutNulls(), $controller, $method 5 ); 6 7 if (method_exists($controller, 'callAction')) { 8 return $controller->callAction($method, $parameters); 9 }10 11 return $controller->{ $method}(...array_values($parameters));12 }
响应的生成
\vendor\laravel\framework\src\Illuminate\Routing\Router.php
protected function runRoute(Request $request, Route $route) { $request->setRouteResolver(function () use ($route) { return $route; }); $this->events->dispatch(new Events\RouteMatched($route, $request)); return $this->prepareResponse($request, $this->runRouteWithinStack($route, $request) ); }
public function prepareResponse($request, $response) { return static::toResponse($request, $response); }
public static function toResponse($request, $response) { if ($response instanceof Responsable) { $response = $response->toResponse($request); } if ($response instanceof PsrResponseInterface) { $response = (new HttpFoundationFactory)->createResponse($response); } elseif (! $response instanceof SymfonyResponse && ($response instanceof Arrayable || $response instanceof Jsonable || $response instanceof ArrayObject || $response instanceof JsonSerializable || is_array($response))) { $response = new JsonResponse($response); } elseif (! $response instanceof SymfonyResponse) { $response = new Response($response); } if ($response->getStatusCode() === Response::HTTP_NOT_MODIFIED) { $response->setNotModified(); } return $response->prepare($request); }
vendor\symfony\http-foundation\Response.php
在发送到客户端之前准备响应
1 public function prepare(Request $request) 2 { 3 $headers = $this->headers; 4 5 if ($this->isInformational() || $this->isEmpty()) { 6 $this->setContent(null); 7 $headers->remove('Content-Type'); 8 $headers->remove('Content-Length'); 9 } else {10 // Content-type based on the Request11 if (!$headers->has('Content-Type')) {12 $format = $request->getRequestFormat();13 if (null !== $format && $mimeType = $request->getMimeType($format)) {14 $headers->set('Content-Type', $mimeType);15 }16 }17 18 // Fix Content-Type19 $charset = $this->charset ?: 'UTF-8';20 if (!$headers->has('Content-Type')) {21 $headers->set('Content-Type', 'text/html; charset='.$charset);22 } elseif (0 === stripos($headers->get('Content-Type'), 'text/') && false === stripos($headers->get('Content-Type'), 'charset')) {23 // add the charset24 $headers->set('Content-Type', $headers->get('Content-Type').'; charset='.$charset);25 }26 27 // Fix Content-Length28 if ($headers->has('Transfer-Encoding')) {29 $headers->remove('Content-Length');30 }31 32 if ($request->isMethod('HEAD')) {33 // cf. RFC2616 14.1334 $length = $headers->get('Content-Length');35 $this->setContent(null);36 if ($length) {37 $headers->set('Content-Length', $length);38 }39 }40 }41 42 // Fix protocol43 if ('HTTP/1.0' != $request->server->get('SERVER_PROTOCOL')) {44 $this->setProtocolVersion('1.1');45 }46 47 // Check if we need to send extra expire info headers48 if ('1.0' == $this->getProtocolVersion() && false !== strpos($this->headers->get('Cache-Control'), 'no-cache')) {49 $this->headers->set('pragma', 'no-cache');50 $this->headers->set('expires', -1);51 }52 53 $this->ensureIEOverSSLCompatibility($request);54 55 return $this;56 }