La clase HttpKernel
es la clase central de Symfony2 y es responsable de procesar las peticiones del cliente. Su objetivo principal es convertir un objeto de tipo Request
en un objeto de tipo Response
.
El kernel de Symfony2 debe implementar la interfaz HttpKernelInterface
:
function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true)
18.2.1. Controladores
Para convertir un objeto Request
en uno de tipo Response
, el kernel utiliza un controlador, que puede ser cualquier código PHP ejecutable válido.
El kernel delega la selección del controlador que se debe ejecutar a una clase que implemente la interfaz ControllerResolverInterface
:
public function getController(Request $request);
public function getArguments(Request $request, $controller);
El método ControllerResolverInterface::getController()
devuelve el controlador (en realidad, devuelve un código PHP ejecutable) asociado a la petición del usuario. Por defecto, la clase ControllerResolver
) busca en la petición un atributo llamado _controller
que representa el nombre del controlador (en forma de cadena de texto con el formato class::method
, cómo por ejemplo Bundle\BlogBundle\PostController:indexAction
).
Truco Por defecto se utiliza la clase RouterListener
para definir el atributo _controller
de la petición.
El método ControllerResolverInterface::getArguments
devuelve un array de argumentos para pasarlos al controlador. Por defecto los argumentos del controlador se resuelven a partir de los atributos de la petición.
18.2.2. Procesando peticiones
El método HttpKernel::handle
admite como argumento un objeto Request
y siempre devuelve un objeto Response
. Para convertir la petición, el método handle()
utiliza la clase Resolver
y una serie de notificaciones de eventos (la siguiente sección proporciona más información acerca de cada evento):
- Antes que nada, se notifica el evento
kernel.request
. Si alguno de los listeners devuelve un objetoResponse
, se pasa directamente al paso número 8. - Se ejecuta el
Resolver
para determinar qué controlador se debe ejecutar. - Los listeners del evento
kernel.controller
ahora pueden manipular el controlador como quieran, incluso cambiándolo. - El kernel verifica que el controlador es un código PHP ejecutable válido.
- Se ejecuta de nuevo el
Resolver
para obtener los argumentos que se pasan al controlador. - El kernel ejecuta el controlador.
- Si el controlador no devuelve un objeto
Response
, los listeners del eventokernel.view
pueden convertir el valor devuelto por el controlador en un objeto de tipoResponse
. - Los listeners del evento
kernel.response
pueden manipular el objetoResponse
(tanto el contenido como las cabeceras). - Se devuelve la respuesta al usuario.
Si se produce una excepción durante el procesamiento de la petición, se notifica el evento kernel.exception
y se da la oportunidad a los listeners de convertir la excepción en un objeto Response
. Si esto funciona, se notifica también el evento kernel.response
; si no, se vuelve a lanzar la excepción.
Si no deseas que se capturen las excepciones, desactiva el evento kernel.exception
pasando false
como tercer argumento del método handle()
.
18.2.3. Peticiones internas
Durante el procesado de una petición, también se pueden manejar lo que se conoce como sub-peticiones. Por este motivo el segundo argumento del método handle()
es el tipo de petición, que puede ser uno de estos dos valores:
HttpKernelInterface::MASTER_REQUEST
HttpKernelInterface::SUB_REQUEST
El tipo de petición se pasa a todos los eventos para que los listeners puedan adecuar su funcionamiento al tipo de petición.
18.2.4. Eventos
Cada evento notificado por el kernel es una subclase de KernelEvent
. Esto significa que todos los eventos tienen acceso a la misma información básica:
KernelEvent::getRequestType()
, devuelve el tipo de la petición (HttpKernelInterface::MASTER_REQUEST
oHttpKernelInterface::SUB_REQUEST
).KernelEvent::getKernel()
, devuelve el kernel que está procesando la petición.KernelEvent::getRequest()
, devuelve el objetoRequest
que se está procesando en ese momento.
18.2.4.1. getRequestType()
El método getRequestType()
permite a los listeners conocer el tipo de la petición. Por ejemplo, si un listener sólo debe estar atento a las peticiones maestras, utiliza el siguiente código al principio de tu método listener:
use Symfony\Component\HttpKernel\HttpKernelInterface;
if (HttpKernelInterface::MASTER_REQUEST !== $event->getRequestType()) {
return;
}
Truco Si todavía no estás familiarizado con el event dispatcher de Symfony2, más adelante en este mismo capítulo se explica con detalle.
18.2.4.2. Evento kernel.request
La clase relacionada con este evento es Symfony\Component\HttpKernel\Event\GetResponseEvent
El objetivo de este evento es devolver inmediatamente un objeto Response
o crear las variables necesarias para que se pueda ejecutar un controlador justo después de este evento. Cualquier listener puede devolver un objeto Response
a través del método setResponse()
en el evento. En este caso, el resto de listeners registrados para este evento no se ejecutan.
Este evento lo utiliza el bundle FrameworkBundle
para establecer el atributo _controller
de la petición, mediante la clase RouterListener
. Por su parte, la clase RequestListener
usa un objeto de tipo RouterInterface
para determinar el nombre del controlador a partir de la petición (que se guarda en el atributo _controller
del objeto Request
).
18.2.4.3. Evento kernel.controller
La clase relacionada con este evento es Symfony\Component\HttpKernel\Event\FilterControllerEvent
Este evento no lo utiliza el bundle FrameworkBundle
, pero es una forma sencilla de modificar el controlador que se ejecuta:
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
// ...
// el controlador se puede cambiar por cualquier código PHP ejecutable
$event->setController($controller);
}
18.2.4.4. Evento kernel.view
La clase relacionada con este evento es Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent
Este evento no lo utiliza el bundle FrameworkBundle
, pero lo puedes usar para todas las tareas relacionadas con la vista. Este evento se llama sólo si el controlador no devuelve un objeto Response
. El propósito del evento es permitir que el valor devuelto por el controlador se convierta en un objeto de tipo Response
.
El valor devuelto por el controlador está disponible a través del método getControllerResult
:
use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
use Symfony\Component\HttpFoundation\Response;
public function onKernelView(GetResponseForControllerResultEvent $event)
{
$val = $event->getControllerResult();
$response = new Response();
// modificar el objeto Response ...
$event->setResponse($response);
}
18.2.4.5. Evento kernel.response
La clase relacionada con este evento es Symfony\Component\HttpKernel\Event\FilterResponseEvent
El propósito de este evento es permitir que otros sistemas modifiquen o sustituyan el objeto Response
después de su creación:
public function onKernelResponse(FilterResponseEvent $event)
{
$response = $event->getResponse();
// modifica el objeto Response ...
}
El bundle FrameworkBundle
registra varios listeners:
ProfilerListener
: recolecta información sobre la petición actual.WebDebugToolbarListener
: inyecta la barra de depuración web.ResponseListener
: establece el valor delContent-Type
de la respuesta basándose en el formato de la petición.EsiListener
: añade la cabeceraSurrogate-Control
de HTTP cuando es necesario procesar la respuesta porque contiene etiquetas ESI.
18.2.4.6. Evento kernel.terminate
Este evento está disponible a partir de Symfony 2.1. El objetivo del evento es permitir a la aplicación realizar tareas pesadas después de haber enviado la respuesta al usuario.
18.2.4.7. Evento kernel.exception
La clase relacionada con este evento es Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent
El bundle FrameworkBundle
registra el listener ExceptionListener
, que redirige la petición a un controlador determinado (configurable con la opción exception_listener.controller
).
Los listeners de este evento pueden crear y configurar un objeto Response
, crear y establecer un nuevo objeto Excepción
, o simplemente no hacer nada:
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;
public function onKernelException(GetResponseForExceptionEvent $event)
{
$exception = $event->getException();
$response = new Response();
// configura el objeto respuesta basándose en la excepción producida
$event->setResponse($response);
// si lo prefieres, puedes establecer una nueva excepción
// $exception = new \Exception('Some special exception');
// $event->setException($exception);
}
Nota Como Symfony siempre se asegura de que el código de estado del objeto Response
sea el más apropiado en función de la excepción producida, no es posible establecer manualmente el estado de la respuesta. En cualquier caso, para sobreescribir el código de estado de la respuesta (algo que deberías hacer solamente por una buena razón) utiliza la cabecera X-Status-Code
:
return new Response('Error', 404 /* este valor se ignora */,
array('X-Status-Code' => 200));