前提:使用的laravel版本为5.7,因为涉及到的流程比较复杂,请结合具体代码查看
文件名:public/index.php
1
2
3
|
<?php
$app = require_once __DIR__.'/../bootstrap/app.php';
|
文件名:bootstrap/app.php
1
2
3
4
5
|
<?php
$app = new Illuminate\Foundation\Application(
$_ENV['APP_BASE_PATH'] ?? dirname(__DIR__)
);
|
文件名:vendor/laravel/framework/src/Illuminate/Foundation/Application.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<?php
/**
* Create a new Illuminate application instance.
*创建一个新的应用程序实例
* @param string|null $basePath
* @return void
*/
public function __construct($basePath = null)
{
if ($basePath) {
//设置应用程序的基本路径和绑定容器中的所有应用程序路径
$this->setBasePath($basePath);
}
//将基本绑定注册到容器中
$this->registerBaseBindings();
//注册所有基本服务提供者
$this->registerBaseServiceProviders();
//在容器中注册核心类别名
$this->registerCoreContainerAliases();
}
|
文件名:bootstrap/app.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<?php
//绑定实例
$app->singleton(
Illuminate\Contracts\Http\Kernel::class,
App\Http\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
|
文件名:public/index.php
1
2
3
4
5
6
7
8
9
|
<?php
//创建一个新的HTTP内核实例
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
//处理传入的HTTP请求
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()//创建一个http请求,这里用到了Symfony实例
);
|
说明:我们关注的重点在handle方法,传入参数为使用到Symfony组件中的http包,下面我们具体看一下handle方法
文件名:vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
<?php
/**
* Handle an incoming HTTP request.
*处理传入的HTTP请求
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function handle($request)
{
try {
//启用对_method请求参数的支持,以确定预期的HTTP方法
$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;
}
|
很明显,sendRequestThroughRouter才是主角,我们来看一下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<?php
/**
* 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');
//为HTTP请求引导应用程序
$this->bootstrap();
return (new Pipeline($this->app))//这个扩展管道捕获每个片期间发生的任何异常
->send($request)//设置通过管道发送的对象
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)//中间件
->then($this->dispatchToRouter());//使用最终目标回调运行管道,传入参数为路由调度程序回调
}
|
这里都加了注释,需要注意的是then方法,我们来看一下:
文件名:D:\WWW\Test\laravel_test\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
<?php
/**
* Run the pipeline with a final destination callback.
*使用最终目标回调运行管道
* @param \Closure $destination
* @return mixed
*/
public function then(Closure $destination)
{
//$this->pipes为中间件, carry方法为处理
$pipeline = array_reduce(
array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination)
);
return $pipeline($this->passable);
}
|
这里用到了array_reduce函数,具体的使用方法看文档:https://www.php.net/manual/zh/function.array-reduce.php,我们需要关注的是第三个参数,这个参数为前面传入的路由调度程序回调,这里面还是用到了carry方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
<?php
/**
* Get a Closure that represents a slice of the application onion.
*获取一个表示应用程序洋葱片的闭包
* @return \Closure
*/
protected function carry()
{
return function ($stack, $pipe) {
return function ($passable) use ($stack, $pipe) {
if (is_callable($pipe)) {
// If the pipe is an instance of a Closure, we will just call it directly but
// otherwise we'll resolve the pipes out of the container and call it with
// the appropriate method and arguments, returning the results back out.
return $pipe($passable, $stack);
} elseif (! is_object($pipe)) {
[$name, $parameters] = $this->parsePipeString($pipe);
// If the pipe is a string we will parse the string and resolve the class out
// of the dependency injection container. We can then build a callable and
// execute the pipe function giving in the parameters that are required.
$pipe = $this->getContainer()->make($name);
$parameters = array_merge([$passable, $stack], $parameters);
} else {
// If the pipe is already an object we'll just make a callable and pass it to
// the pipe as-is. There is no need to do any extra parsing and formatting
// since the object we're given was already a fully instantiated object.
$parameters = [$passable, $stack];
}
//这里走到了中间件的handle方法,里面有个next方法
$response = method_exists($pipe, $this->method)
? $pipe->{$this->method}(...$parameters)
: $pipe(...$parameters);
return $response instanceof Responsable
? $response->toResponse($this->container->make(Request::class))
: $response;
};
};
}
|
这里将app/Http/Kernel.php中的$middleware拿来进行处理,每一个里面都有handle方法来进行 处理传入请求 ,我们来看一个中间件:
文件名:vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php
1
2
3
4
5
6
7
8
9
10
11
12
|
<?php
public function handle($request, Closure $next)
{
$max = $this->getPostMaxSize();
if ($max > 0 && $request->server('CONTENT_LENGTH') > $max) {
throw new PostTooLargeException;
}
return $next($request);
}
|
我们需要注意的是最后的 return $next($request);正是因为有了这个,才可以使用array_reduce函数进行迭代
好,我们再回到这个文件 :vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php 中的dispatchToRouter这个方法,下面我们就按照给出代码一步一步找到路由分配控制器的地方
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
<?php
/**
* Get the route dispatcher callback.
*获取路由调度程序回调
* @return \Closure
*/
protected function dispatchToRouter()
{
return function ($request) {
$this->app->instance('request', $request);
return $this->router->dispatch($request);
};
}
/**
* Run the given route within a Stack "onion" instance.
*在堆栈“onion”实例中运行给定的路由
* @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);
return (new Pipeline($this->container))
->send($request)
->through($middleware)
->then(function ($request) use ($route) {
return $this->prepareResponse(
$request, $route->run()
);
});
}
|
文件名:vendor/laravel/framework/src/Illuminate/Routing/Route.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
<?php
/**
* Run the route action and return the response.
*运行route操作并返回响应
* @return mixed
*/
public function run()
{
$this->container = $this->container ?: new Container;
try {
if ($this->isControllerAction()) {//是string 则运行controller
return $this->runController();
}
return $this->runCallable();
} catch (HttpResponseException $e) {
return $e->getResponse();
}
}
/**
* Run the route action and return the response.
*运行route操作并返回响应
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
protected function runController()
{
return $this->controllerDispatcher()->dispatch(
$this, $this->getController(), $this->getControllerMethod()
);
}
/**
* Get the controller instance for the route.
*获取路由的控制器实例
* @return mixed
*/
public function getController()
{
if (! $this->controller) {
$class = $this->parseControllerCallback()[0];
//这里处理依赖注入, 容器
$this->controller = $this->container->make(ltrim($class, '\\'));
}
return $this->controller;
}
|
我们需要关注的是:$this->controllerDispatcher()->dispatch()这段代码,其中的 dispatch 方法实现了分配到路由,我们来看一下具体代码:
文件名:vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<?php
/**
* 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)
{
//解析对象方法的类型暗示依赖项 传入参数:1,为没有空值的参数的键/值列表,2:控制器实例,3:方法
$parameters = $this->resolveClassMethodDependencies(
$route->parametersWithoutNulls(), $controller, $method
);
//有callAction,则执行, 继承框架中的controller,就会有callAction
if (method_exists($controller, 'callAction')) {
return $controller->callAction($method, $parameters);
}
//执行
return $controller->{$method}(...array_values($parameters));
}
|
如果我们继承了Illuminate\Routing\Controller,拿着里面就会有一个callAction方法:我们来看一下:
文件名:vendor/laravel/framework/src/Illuminate/Routing/Controller.php
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?php
/**
* Execute an action on the controller.
*在控制器上执行一个操作,大部分操作会走到这里
* @param string $method
* @param array $parameters
* @return \Symfony\Component\HttpFoundation\Response
*/
public function callAction($method, $parameters)
{
return call_user_func_array([$this, $method], $parameters);
}
|
最后,我们来到$response->send();这一句,来看一下:
文件名:vendor/symfony/http-foundation/Response.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<?php
/**
* Sends HTTP headers and content.
*
* @return $this
*/
public function send()
{
//发送HTTP header
$this->sendHeaders();
//发送conetent
$this->sendContent(); //echo $this->content;
if (\function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
} elseif (!\in_array(\PHP_SAPI, array('cli', 'phpdbg'), true)) {
static::closeOutputBuffers(0, true);
}
return $this;
}
|
至此,laravel的生命周期完成