上篇讲了make方法-->Laravel源码剖析之make详解(三)_Attitude_do_it的博客-CSDN博客,
根据make方法的分析可以得出:
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
此处的$kernel是App\Http\Kernel的实例,如果还不懂的小伙伴,可以私信我。
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
$response->send();
这段代码,是Laravel最核心的请求处理部分,既然$kernel是App\Http\Kernel的实例,此处调用了它的handle方法,那么我们来看看这个类和这个函数都做了什么:
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
* 应用全局的HTTP中间件
* These middleware are run during every request to your application.
*
* @var array
*/
protected $middleware = [
\App\Http\Middleware\TrustProxies::class,
\App\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
/**
* The application's route middleware groups.
* 应用的路由中间件组
* @var array
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
'throttle:60,1',
'bindings',
],
];
/**
* The application's route middleware.
* 应用的路由中间件
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
];
/**
* The priority-sorted list of middleware.
* 按照优先级排序的中间件
* This forces non-global middleware to always be in the given order.
*
* @var array
*/
protected $middlewarePriority = [
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\Authenticate::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class,
\Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Illuminate\Auth\Middleware\Authorize::class,
];
}
可以看到此类继承了Illuminate\Foundation\Http\Kernel,并且没有重写它的handle方法,那么我们就要定位到Illuminate\Foundation\Http\Kernel的handle方法:
/**
* Handle an incoming HTTP request.
* 处理进来的HTTP请求
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function handle($request)
{
try {
//启用对_method请求参数的支持,enableHttpMethodParameterOverride下方有说明
$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 RequestHandled($request, $response)
);
return $response;
}
/**
* Enables support for the _method request parameter to determine the intended HTTP method.
*
* Be warned that enabling this feature might lead to CSRF issues in your code.
* Check that you are using CSRF tokens when required.
* If the HTTP method parameter override is enabled, an html-form with method "POST" can be altered
* and used to send a "PUT" or "DELETE" request via the _method request parameter.
* If these methods are not protected against CSRF, this presents a possible vulnerability.
*
* The HTTP method can only be overridden when the real HTTP method is POST.
* 启用对_method请求参数的支持,以确定预期的HTTP方法。请注意,启用此功能可能会导致代码中出现
CSRF问题。检查您是否在需要时使用CSRF令牌。如果启用了HTTP方法参数覆盖,则可以更改带有方法
“POST”的html表单,并使用该表单通过_methodrequest参数发送“PUT”或“DELETE”请求。如果这些
方法没有针对CSRF进行保护,则可能存在漏洞。只有当真正的HTTP方法是POST时,才能重写HTTP方法
*/
public static function enableHttpMethodParameterOverride()
{
self::$httpMethodParameterOverride = true;
}
/**
* Send the given request through the middleware / router.
* 通过中间件/路由器发送给定的请求
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
protected function sendRequestThroughRouter($request)
{
// 绑定request实例到容器
$this->app->instance('request', $request);
//清除之前已经解析过的request实例
Facade::clearResolvedInstance('request');
$this->bootstrap();
//处理请求的关键部分
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());
}
看看Pipeline类都干了什么:
/**
* Create a new class instance.
* 创建新的类实例
* @param \Illuminate\Contracts\Container\Container|null $container
* @return void
*/
public function __construct(Container $container = null)
{
$this->container = $container;
}
/**
* Set the object being sent through the pipeline.
* 设置通过管道发送的对象
* @param mixed $passable
* @return $this
*/
public function send($passable)
{
$this->passable = $passable;
return $this;
}
/**
* Set the array of pipes.
*
* @param array|mixed $pipes
* @return $this
*/
public function through($pipes)
{
//这里其实是设置需要的中间件
$this->pipes = is_array($pipes) ? $pipes : func_get_args();
return $this;
}
/**
* Run the pipeline with a final destination callback.
* 为最终目标回调运行pipeline
* @param \Closure $destination
* @return mixed
*/
public function then(Closure $destination)
{
$pipeline = array_reduce(
array_reverse($this->pipes()), $this->carry(), $this->prepareDestination($destination)
);
return $pipeline($this->passable);
}
then方法中用到了array_reverse和array_reduce,这里不对这两个方法进行讲解,希望小伙伴可以自己动手去查阅,这样印象更深刻一点。这里主要讲解一些carry方法:
/**
* Get a Closure that represents a slice of the application onion.
* 获取表示应用程序片段的闭包
* @return \Closure
*/
protected function carry()
{
//$stack是刚刚then方法中$this->prepareDestination($destination)的返回值
//$pipe是array_reverse($this->pipes())的元素,也就是刚刚through方法中设置的中间件,
元素值都是中间件的类名字符串,知道了这些下边就容易理解了
return function ($stack, $pipe) {
return function ($passable) use ($stack, $pipe) {
try {
if (is_callable($pipe)) {
// 既然知道$pipe是字符串,那这里指定是false
// If the pipe is a callable, then we will call it directly, but otherwise we
// will resolve the pipes out of the dependency container and call it with
// the appropriate method and arguments, returning the results back out.
return $pipe($passable, $stack);
} elseif (! is_object($pipe)) {
//会进入到这个方法,parsePipeString方法下边有介绍
[$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即中间件的实例
$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方法
$carry = method_exists($pipe, $this->method)
? $pipe->{$this->method}(...$parameters)
: $pipe(...$parameters);
//返回中间件的处理结果
return $this->handleCarry($carry);
} catch (Exception $e) {
return $this->handleException($passable, $e);
} catch (Throwable $e) {
return $this->handleException($passable, new FatalThrowableError($e));
}
};
};
}
/**
* Parse full pipe string to get name and parameters.
* 解析完整pipe字符串以获取名称和参数
* @param string $pipe
* @return array
*/
protected function parsePipeString($pipe)
{
[$name, $parameters] = array_pad(explode(':', $pipe, 2), 2, []);
if (is_string($parameters)) {
$parameters = explode(',', $parameters);
}
return [$name, $parameters];
}
从carry方法可以看出,在真正掉处理请求前,先执行了中间件的方法。
到这里框架对于分配路由前的处理就算是正式完成了,下篇来分析一下框架分配路由的具体操作,也就是针对下边这段代码中then方法的参数$this->dispatchToRouter()的解读,看看它里边到底做了哪些事情。有兴趣的小伙伴可以点下关注,咱们一起探讨,一起进步,欢迎私信沟通
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());