Ver índice de contenidos del libro

18.2. El kernel

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):

  1. Antes que nada, se notifica el evento kernel.request. Si alguno de los listeners devuelve un objeto Response, se pasa directamente al paso número 8.
  2. Se ejecuta el Resolver para determinar qué controlador se debe ejecutar.
  3. Los listeners del evento kernel.controller ahora pueden manipular el controlador como quieran, incluso cambiándolo.
  4. El kernel verifica que el controlador es un código PHP ejecutable válido.
  5. Se ejecuta de nuevo el Resolver para obtener los argumentos que se pasan al controlador.
  6. El kernel ejecuta el controlador.
  7. Si el controlador no devuelve un objeto Response, los listeners del evento kernel.view pueden convertir el valor devuelto por el controlador en un objeto de tipo Response.
  8. Los listeners del evento kernel.response pueden manipular el objeto Response (tanto el contenido como las cabeceras).
  9. 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 o HttpKernelInterface::SUB_REQUEST).
  • KernelEvent::getKernel(), devuelve el kernel que está procesando la petición.
  • KernelEvent::getRequest(), devuelve el objeto Request 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 del Content-Type de la respuesta basándose en el formato de la petición.
  • EsiListener: añade la cabecera Surrogate-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));