HttpKernel类是Symfony2的核心类,负责处理客户端的请求。它的主要目标是将Request对象“转换”成Response对象。
每个Symfony2的Kernel都实现HttpKernelInterface接口:
- function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true)
控制器
要将Request转换成Response,Kernel依赖“控制器”。控制器可以是任一有效的PHP调用。
Kernel代表控制器选择应该执行哪个ControllerResolverInterface实现:
- public function getController(Request $request);
- public function getArguments(Request $request, $controller);
getController()方法返回与给定Request相关的控制器(一个PHP调用)。缺省实现 (ControllerResolver) 查找请求参数_controller,该参数代表控制器名称(一个"class::method"字符串,如Bundle\BlogBundle\PostController:indexAction)。
该缺省实现使用RequestListener去定义_controller请求属性(参见 kernel-core_request)。
getArguments()方法返回一个发送给控制器调用的参数数组。缺省实现基于Request属性自动解析方法参数。
从Request属性匹配控制器方法参数
对于每个访问参数,Symfony2尝试从Request的同名属性中取值。如果没有定义则需要参数的缺省值:
- // Symfony2 will look for an 'id' attribute (mandatory)
- // and an 'admin' one (optional)
- public function showAction($id, $admin = true)
- {
- // ...
- }
处理请求
handle()方法接受一个Request并总是返回一个Response。为转换Request,handle()依赖Resolver和一个Event通知的步骤(每个Event的详细信息参见下一章节):
- 在做其它任何事之前,发送core.request事件通知。如果监听器返回Response,则直接跳到步骤8;
- 调用Resolver以确定执行Controller;
- core.response事件监听器现在可以按它们希望的方式(改变它、封装它...)调用Controller;
- Kernel检查Controller实际是一个有效的PHP调用;
- 调用Resolver以确定发送给Controller的参数;
如果在处理过程中抛出一个异常,那么发送core.exception事件通知,监听器将得到一个改变去将异常转换成Response。如果正常工作,那么发送core.response事件通知;否则将再次抛出异常。
如果你不想异常被捕获(如内嵌的请求),那么发送false给handle()方法的第3个参数,禁用core.exception事件。
内部请求
有时,在处理请求(“主”请求)时,可以处理子请求。你可以将请求类型发送到handle()方法(它是第2个参数):
- HttpKernelInterface::MASTER_REQUEST;
- HttpKernelInterface::SUB_REQUEST.
事件和监听器可以根据发送的类型处理(一些进程只能在主请求中发生)。
事件
每个通过Kernel抛出的事件都是KernelEvent的子类。这就是说每个事件都可以访问到相同的基本信息:
- getRequestType() - 返回请求类型 (HttpKernelInterface::MASTER_REQUEST或 HttpKernelInterface::SUB_REQUEST);
- getKernel() - 返回处理请求的Kernel;
- getRequest() - 返回当前正在处理的Request。
getRequestType()¶
getRequestType()方法可以让监听器知道请求类型。例如,如果监听器只能被主请求激活,那么在你监听器方法的开始添加以下代码:
- use Symfony\Component\HttpKernel\HttpKernelInterface;
- if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
- // return immediately
- return;
- }
如果你对Symfony2事件调度不熟的话,请先查看调度章节。
core.request事件
事件类:GetResponseEvent
该事件的目标是立即返回一个Response对象或设置变量,以便可以在事件后调用Controller。任何监听器都可以通过在事件的setResponse()方法返回一个Response对象。在这种情况下,其它所有监听器都不会被调用。
该事件被FrameworkBundle通过RequestListener来填充_controller请求属性。RequestListener使用RouterInterface对象来匹配Request,并确定Controller名(保存在_controller请求属性中)
core.controller事件
该事件没有被FrameworkBundle使用,但可以作为修改应该执行控制器的切入点:
- use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
- public function onCoreController(FilterControllerEvent $event)
- {
- $controller = $event->getController();
- // ...
- // the controller can be changed to any PHP callable
- $event->setController($controller);
- }
core.view事件
事件类:GetResponseForControllerResultEvent
该事件没有被FrameworkBundle使用,但它可以用来实现一个视图子系统。该事件仅当Controller没有返回Response对象时被调用。该事件的目的是让其它的返回值转换成Response。
通过Controller返回的值可以由getControllerResult方法访问:
- use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
- use Symfony\Component\HttpFoundation\Response;
- public function onCoreView(GetResponseForControllerResultEvent $event)
- {
- $val = $event->getReturnValue();
- $response = new Response();
- // some how customize the Response from the return value
- $event->setResponse($response);
- }
core.response事件
该事件目标是在其被创建后让其它系统去修改或替代Response对象:
- public function onCoreResponse(FilterResponseEvent $event)
- {
- $response = $event->getResponse();
- // .. modify the response object
- }
FrameworkBundle注册几个监听器:
- ProfilerListener: 为当前请求收集数据;
- WebDebugToolbarListener: 注入Web调试工具条;
- ResponseListener: 修复基于请求格式的Response的Content-Type;
- EsiListener: 当需要为ESI标签分析Response时添加一个Surrogate-Control HTTP头。
core.exception事件
事件类:GetResponseForExceptionEvent
FrameworkBundle注册一个将Request转发给指定Controller(exception_listener.controller参数的值必须在class::method注释中)的ExceptionListener。
该事件的监听器可以创建和设置一个Response对象,或者什么也不做:
- use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
- use Symfony\Component\HttpFoundation\Response;
- public function onCoreException(GetResponseForExceptionEvent $event)
- {
- $exception = $event->getException();
- $response = new Response();
- // setup the Response object based on the caught exception
- $event->setResponse($response);
- // you can alternatively set a new Exception
- // $exception = new \Exception('Some special exception');
- // $event->setException($exception);
- }