Symfony HttpKernel 源码:框架的心脏
HttpKernel 是 Symfony 的核心组件,Laravel 也基于它构建。
今天深入分析 HttpKernel 的实现原理。
HttpKernel 概述
HttpKernel 负责将 Request 转换为 Response。
interfaceHttpKernelInterface
{
publicfunctionhandle(
Request $request,
int $type = self::MAIN_REQUEST,
bool $catch = true
): Response;
}
核心流程
// Symfony\Component\HttpKernel\HttpKernel
publicfunctionhandle(Request $request, int $type = self::MAIN_REQUEST, bool $catch = true): Response
{
$request->headers->set('X-Php-Ob-Level', (string) ob_get_level());
try {
return$this->handleRaw($request, $type);
} catch (\Exception $e) {
if (!$catch) {
throw $e;
}
return$this->handleThrowable($e, $request, $type);
}
}
privatefunctionhandleRaw(Request $request, int $type = self::MAIN_REQUEST): Response
{
// 1. 触发 kernel.request 事件
$event = new RequestEvent($this, $request, $type);
$this->dispatcher->dispatch($event, KernelEvents::REQUEST);
if ($event->hasResponse()) {
return$this->filterResponse($event->getResponse(), $request, $type);
}
// 2. 解析控制器
if (false === $controller = $this->resolver->getController($request)) {
thrownew NotFoundHttpException();
}
// 3. 触发 kernel.controller 事件
$event = new ControllerEvent($this, $controller, $request, $type);
$this->dispatcher->dispatch($event, KernelEvents::CONTROLLER);
$controller = $event->getController();
// 4. 解析控制器参数
$arguments = $this->argumentResolver->getArguments($request, $controller);
// 5. 触发 kernel.controller_arguments 事件
$event = new ControllerArgumentsEvent($this, $controller, $arguments, $request, $type);
$this->dispatcher->dispatch($event, KernelEvents::CONTROLLER_ARGUMENTS);
$arguments = $event->getArguments();
// 6. 调用控制器
$response = $controller(...$arguments);
// 7. 处理响应
if (!$response instanceof Response) {
$event = new ViewEvent($this, $request, $type, $response);
$this->dispatcher->dispatch($event, KernelEvents::VIEW);
if ($event->hasResponse()) {
$response = $event->getResponse();
} else {
thrownew \LogicException('Controller must return a Response');
}
}
return$this->filterResponse($response, $request, $type);
}
事件系统
HttpKernel 通过事件实现扩展。
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
事件监听示例
useSymfony\Component\HttpKernel\Event\RequestEvent;
useSymfony\Component\HttpKernel\KernelEvents;
classRequestListener
{
publicfunctiononKernelRequest(RequestEvent $event): void
{
$request = $event->getRequest();
// 检查 API Token
if (!$request->headers->has('X-API-Token')) {
$event->setResponse(new JsonResponse([
'error' => 'Missing API token'
], 401));
}
}
}
// 注册监听器
$dispatcher->addListener(
KernelEvents::REQUEST,
[new RequestListener(), 'onKernelRequest']
);
控制器解析器
// Symfony\Component\HttpKernel\Controller\ControllerResolver
publicfunctiongetController(Request $request): callable|false
{
// 从请求属性获取 _controller
if (!$controller = $request->attributes->get('_controller')) {
returnfalse;
}
// 字符串格式:App\Controller\UserController::index
if (is_string($controller)) {
if (str_contains($controller, '::')) {
$controller = explode('::', $controller, 2);
} else {
// 可调用类
return$this->instantiateController($controller);
}
}
// 数组格式:[UserController::class, 'index']
if (is_array($controller)) {
if (is_string($controller[0])) {
$controller[0] = $this->instantiateController($controller[0]);
}
return $controller;
}
return $controller;
}
参数解析器
// Symfony\Component\HttpKernel\Controller\ArgumentResolver
publicfunctiongetArguments(Request $request, callable $controller): array
{
$arguments = [];
// 获取控制器方法的参数
$reflection = new \ReflectionMethod($controller[0], $controller[1]);
foreach ($reflection->getParameters() as $param) {
// 遍历参数解析器
foreach ($this->argumentValueResolvers as $resolver) {
if ($resolver->supports($request, $param)) {
$arguments[] = $resolver->resolve($request, $param);
continue2;
}
}
// 使用默认值
if ($param->isDefaultValueAvailable()) {
$arguments[] = $param->getDefaultValue();
} else {
thrownew \RuntimeException("Cannot resolve argument {$param->getName()}");
}
}
return $arguments;
}
内置参数解析器
// Request 参数
classRequestValueResolverimplementsArgumentValueResolverInterface
{
publicfunctionsupports(Request $request, ArgumentMetadata $argument): bool
{
return Request::class === $argument->getType();
}
publicfunctionresolve(Request $request, ArgumentMetadata $argument): iterable
{
yield $request;
}
}
// 路由参数
classRequestAttributeValueResolverimplementsArgumentValueResolverInterface
{
publicfunctionsupports(Request $request, ArgumentMetadata $argument): bool
{
return $request->attributes->has($argument->getName());
}
publicfunctionresolve(Request $request, ArgumentMetadata $argument): iterable
{
yield $request->attributes->get($argument->getName());
}
}
响应过滤
privatefunctionfilterResponse(Response $response, Request $request, int $type): Response
{
// 触发 kernel.response 事件
$event = new ResponseEvent($this, $request, $type, $response);
$this->dispatcher->dispatch($event, KernelEvents::RESPONSE);
// 触发 kernel.finish_request 事件
$this->finishRequest($request, $type);
return $event->getResponse();
}
privatefunctionfinishRequest(Request $request, int $type): void
{
$this->dispatcher->dispatch(
new FinishRequestEvent($this, $request, $type),
KernelEvents::FINISH_REQUEST
);
}
异常处理
privatefunctionhandleThrowable(\Throwable $e, Request $request, int $type): Response
{
// 触发 kernel.exception 事件
$event = new ExceptionEvent($this, $request, $type, $e);
$this->dispatcher->dispatch($event, KernelEvents::EXCEPTION);
// 获取异常响应
$e = $event->getThrowable();
if (!$event->hasResponse()) {
// 没有监听器处理,重新抛出
throw $e;
}
$response = $event->getResponse();
// 设置异常状态码
if (!$event->isAllowingCustomResponseCode() && !$response->isClientError() && !$response->isServerError() && !$response->isRedirect()) {
if ($e instanceof HttpExceptionInterface) {
$response->setStatusCode($e->getStatusCode());
$response->headers->add($e->getHeaders());
} else {
$response->setStatusCode(500);
}
}
return$this->filterResponse($response, $request, $type);
}
子请求
HttpKernel 支持子请求(Sub-Request)。
// 主请求
$response = $kernel->handle($request, HttpKernelInterface::MAIN_REQUEST);
// 子请求(用于 ESI、内部转发等)
$subRequest = Request::create('/api/users');
$subResponse = $kernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
子请求的应用
// 控制器中转发
classUserController
{
publicfunction__construct(private HttpKernelInterface $kernel){}
publicfunctionshow(int $id): Response
{
// 内部转发到另一个控制器
$subRequest = Request::create("/api/users/{$id}");
return$this->kernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
}
}
终止阶段
// Symfony\Component\HttpKernel\HttpKernel
publicfunctionterminate(Request $request, Response $response): void
{
$this->dispatcher->dispatch(
new TerminateEvent($this, $request, $response),
KernelEvents::TERMINATE
);
}
终止监听器
classSlowLogListener
{
publicfunctiononKernelTerminate(TerminateEvent $event): void
{
$request = $event->getRequest();
$startTime = $request->server->get('REQUEST_TIME_FLOAT');
$duration = microtime(true) - $startTime;
if ($duration > 1.0) {
// 记录慢请求
$this->logger->warning('Slow request', [
'path' => $request->getPathInfo(),
'duration' => $duration,
]);
}
}
}
与 Laravel 的关系
Laravel 的 HTTP 层基于 Symfony HttpKernel 构建。
// Laravel 使用 Symfony 的 Request 和 Response
useSymfony\Component\HttpFoundation\Request;
useSymfony\Component\HttpFoundation\Response;
// Laravel 的 Kernel 实现了 Symfony 的接口
classKernelimplementsHttpKernelContract
{
publicfunctionhandle($request)
{
// 类似 Symfony 的处理流程
// 但使用 Laravel 的中间件管道
}
}
自定义 HttpKernel
useSymfony\Component\HttpKernel\HttpKernel;
useSymfony\Component\HttpKernel\Controller\ControllerResolver;
useSymfony\Component\HttpKernel\Controller\ArgumentResolver;
useSymfony\Component\EventDispatcher\EventDispatcher;
// 创建事件调度器
$dispatcher = new EventDispatcher();
// 添加监听器
$dispatcher->addListener(KernelEvents::REQUEST, function(RequestEvent $event){
// 请求处理
});
// 创建 HttpKernel
$kernel = new HttpKernel(
$dispatcher,
new ControllerResolver(),
new RequestStack(),
new ArgumentResolver()
);
// 处理请求
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
总结
|
|
|
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
核心事件:
-
kernel.request – 请求开始 -
kernel.controller – 控制器解析后 -
kernel.response – 响应发送前 -
kernel.exception – 异常发生时 -
kernel.terminate – 响应发送后
下一篇我们来聊 PHP 底层 Zval 结构。
夜雨聆风
