El componente HttpFoundation

El componente HttpFoundation define una capa orientada a objetos para la especificación HTTP.

En PHP, la petición está representada por algunas variables globales ($_GET, $_POST, $_FILE, $_COOKIE, $_SESSION, ...) y la respuesta es generada por algunas funciones (echo, header, setcookie, ...).

El componente HttpFoundation de Symfony2 sustituye estas variables globales y funciones predefinidas en PHP por una capa orientada a objetos.

Instalando

Puedes instalar el componente de varias maneras diferentes:

Petición

La manera más común para crear una petición es basándose en las variables globales de PHP con createFromGlobals():

use Symfony\Component\HttpFoundation\Request;

$request = Request::createFromGlobals();

que es casi equivalente a la más prolija, pero también más flexible, llamada al método __construct():

$request = new Request(
    $_GET,
    $_POST,
    array(),
    $_COOKIE,
    $_FILES,
    $_SERVER
);

Accediendo a datos de la Petición

Un objeto Petición contiene información sobre la petición del cliente. Puedes acceder a esta información a través de muchas propiedades públicas:

  • request: equivalente de $_POST;
  • query: equivalente de $_GET ($request->query->get('name'));
  • cookies: equivalente de $_COOKIE;
  • attributes: sin equivalente — utilizado por tu aplicación para almacenar otros datos (ve más adelante)
  • files: equivalente de $_FILE;
  • server: equivalente de $_SERVER;
  • headers: en su mayoría equivalente a un subconjunto de $_SERVER ($request->headers->get('Content-Type')).

Cada propiedad es una instancia de Symfony\Component\HttpFoundation\ParameterBag (o una subclase), que es una clase que contiene datos:

  • request: Symfony\Component\HttpFoundation\ParameterBag;
  • query: Symfony\Component\HttpFoundation\ParameterBag;
  • cookies: Symfony\Component\HttpFoundation\ParameterBag;
  • attributes: Symfony\Component\HttpFoundation\ParameterBag;
  • files: Symfony\Component\HttpFoundation\FileBag;
  • server: Symfony\Component\HttpFoundation\ServerBag;
  • headers: Symfony\Component\HttpFoundation\HeaderBag.

Todas las instancias de la clase Symfony\Component\HttpFoundation\ParameterBag tienen métodos para recuperar y actualizar sus datos:

  • all(): Devuelve los parámetros;
  • keys(): Devuelve las claves de los parámetros;
  • replace(): Sustituye los parámetros actuales por un nuevo conjunto;
  • add(): Añade parámetros;
  • get(): Devuelve un parámetro por nombre;
  • set(): Establece un parámetro por nombre;
  • has(): Devuelve true si el parámetro está definido;
  • remove(): Elimina un parámetro.

La instancia de Symfony\Component\HttpFoundation\ParameterBag también tiene algunos métodos para filtrar los valores introducidos:

  • getAlpha(): Devuelve los caracteres alfanuméricos del valor del parámetro;
  • getAlnum(): Devuelve los caracteres alfanuméricos y dígitos del valor del parámetro;
  • getDigits(): Devuelve los dígitos del valor del parámetro;
  • getInt(): Devuelve el valor del parámetro convertido a entero;
  • filter(): Filtra el parámetro usando la función filter_var() de PHP.

Todos los captadores toman hasta tres argumentos: el primero es el nombre del parámetro y el segundo es el valor predeterminado a devolver si el parámetro no existe:

// La cadena de consulta es '?foo=bar'

$request->query->get('foo');
// devuelve bar

$request->query->get('bar');
// devuelve null

$request->query->get('bar', 'bar');
// devuelve 'bar'

Cuando PHP importa la consulta de la petición, manipula los parámetros de la petición como foo[bar]=bar de una manera especial, ya que crea un arreglo. Para que puedas obtener el parámetro foo y devolver un arreglo con un elemento bar. pero a veces, posiblemente quieras obtener el valor por medio del nombre «original» del parámetro: foo[bar]. Esto es posible con todos los captadores de ParameterBag como get() a través del tercer argumento:

// la cadena de consulta es '?foo[bar]=bar'

$request->query->get('foo');
// devuelve array('bar' => 'bar')

$request->query->get('foo[bar]');
// devuelve null

$request->query->get('foo[bar]', null, true);
// devuelve 'bar'

Por último, pero no menos importante, también puedes almacenar datos adicionales en la petición, gracias a la propiedad pública attributes, que también es una instancia de la clase Symfony\Component\HttpFoundation\ParameterBag. Casi siempre se usa para adjuntar información que pertenece a la Petición y que necesitas acceder desde diferentes puntos de tu aplicación. Para información sobre cómo se utiliza en la plataforma Symfony2, consulta la propiedad pública attributes.

Identificando una Petición

En tu aplicación, necesitas una manera de identificar una petición; la mayor parte del tiempo, esto se hace a través de la «información de ruta» de la petición, a la que puedes acceder a través del método getPathInfo():

// Para una petición a http://example.com/blog/index.php/post/hello-world
// la información de ruta es "/post/hello-world"
$request->getPathInfo();

Simulando una Petición

En lugar de crear una petición basada en las variables globales de PHP, también puedes simular una Petición:

$request = Request::create(
    '/hello-world',
    'GET',
    array('name' => 'Fabien')
);

El método create() crea una petición basándose en la información de una ruta, un método y algunos parámetros (los parámetros de consulta o los de la petición en función del método HTTP); and of course, you can also override all other variables as well (by default, Symfony creates sensible defaults for all the PHP global variables).

En base a dicha petición, puedes sustituir las variables globales de PHP a través de overrideGlobals():

$request->overrideGlobals();

Truco

También puedes duplicar una consulta existente a través del método duplicate() o cambiar un montón de parámetros con una sola llamada a initialize().

Accediendo a la Sesión

Si tienes una sesión adjunta a la Petición, puedes acceder a ella a través del método getSession(); el método hasPreviousSession() te dice si la petición contiene una sesión que se inició en una de las peticiones anteriores.

Accediendo a los datos de cabeceras Accept-*

Puedes acceder fácilmente a los dato básicos extraídos desde las cabeceras Accept-* utilizando los siguientes métodos:

  • getAcceptableContentTypes(): devuelve la lista de tipos de contenido aceptados ordenados descendentemente por cualidad;
  • getLanguages(): devuelve la lista de idiomas aceptados ordenados descendentemente por cualidad;
  • getCharsets(): devuelve la lista de juegos de caracteres («charsets») aceptados ordenados descendentemente por cualidad;

Nuevo en la versión 2.2: La clase Symfony\Component\HttpFoundation\AcceptHeader es nueva en Symfony 2.2.

Si necesitas conseguir acceso completo a los datos analizados de Accept, Accept-Language, Accept-Charset o Accept-Encoding, puedes usar la clase utilitaria Symfony\Component\HttpFoundation\AcceptHeader:

use Symfony\Component\HttpFoundation\AcceptHeader;

$accept = AcceptHeader::fromString($request->headers->get('Accept'));
if ($accept->has('text/html')) {
    $item = $accept->get('html');
    $charset = $item->getAttribute('charset', 'utf-8');
    $quality = $item->getQuality();
}

// acepta elementos que están ordenados descendentemente por cualidad
$accepts = AcceptHeader::fromString($request->headers->get('Accept'))->all();

Accediendo a otros datos

La clase Petición tiene muchos otros métodos que puedes utilizar para acceder a la información de la petición. Echa un vistazo a la API para más información sobre ellos.

Respuesta

Un objeto Symfony\Component\HttpFoundation\Response contiene toda la información que debes enviar de vuelta al cliente desde una determinada Petición. El constructor toma hasta tres argumentos: el contenido de la respuesta, el código de estado, y una serie de cabeceras HTTP:

use Symfony\Component\HttpFoundation\Response;

$response = new Response(
    'Content',
    200,
    array('content-type' => 'text/html')
);

También puedes manipular esta información después de haber creado la Respuesta:

$response->setContent('Hello World');

// el atributo público headers es un ResponseHeaderBag
$response->headers->set('Content-Type', 'text/plain');

$response->setStatusCode(404);

Al establecer el Content-Type de la respuesta, puedes configurar el juego de caracteres, pero es mejor configurarlo a través del método setCharset():

$response->setCharset('ISO-8859-1');

Ten en cuenta que de manera predeterminada, Symfony asume que sus respuestas están codificadas en UTF-8.

Enviando la Respuesta

Antes de enviar la respuesta, te puedes asegurar de que es compatible con la especificación del protocolo HTTP llamando al método prepare():

$response->prepare($request);

Enviar la respuesta entonces es tan sencillo cómo invocar al método send():

$response->send();

Enviando Cookies

Puedes manipular las cookies de la respuesta por medio del atributo público headers:

use Symfony\Component\HttpFoundation\Cookie;

$response->headers->setCookie(new Cookie('foo', 'bar'));

El método setCookie() toma como argumento una instancia de Symfony\Component\HttpFoundation\Cookie.

Puedes borrar una cookie a través del método clearCookie().

Gestionando la caché HTTP

La clase Symfony\Component\HttpFoundation\Response tiene un rico conjunto de métodos para manipular las cabeceras HTTP relacionadas con la memoria caché:

Puedes utilizar el método setCache() para establecer la información de caché utilizada comúnmente en una llamada al método:

$response->setCache(array(
    'etag'          => 'abcdef',
    'last_modified' => new \DateTime(),
    'max_age'       => 600,
    's_maxage'      => 600,
    'private'       => false,
    'public'        => true,
));

Para comprobar si los validadores de la Respuesta (ETag, Last-Modified) coinciden con un valor condicional especificado en la petición del cliente, utiliza el método isNotModified():

if ($response->isNotModified($request)) {
    $response->send();
}

Si la respuesta no se ha modificado, se establece el código de estado a 304 y elimina el contenido real de la respuesta.

Redirigiendo al usuario

Para redirigir al cliente a otra URL, puedes utilizar la clase Symfony\Component\HttpFoundation\RedirectResponse:

use Symfony\Component\HttpFoundation\RedirectResponse;

$response = new RedirectResponse('http://ejemplo.com/');

Transmitiendo una Respuesta

Nuevo en la versión 2.1: La compatibilidad a la transmisión de respuestas se añadió en Symfony 2.1.

La clase Symfony\Component\HttpFoundation\StreamedResponse te permite transmitir la respuesta de vuelta al cliente. El contenido de la respuesta está representado por una función PHP ejecutable en lugar de una cadena:

use Symfony\Component\HttpFoundation\StreamedResponse;

$response = new StreamedResponse();
$response->setCallback(function () {
    echo 'Hello World';
    flush();
    sleep(2);
    echo 'Hello World';
    flush();
});
$response->send();

Nota

The flush() function does not flush buffering. If ob_start() has been called before or the output_buffering php.ini option is enabled, you must call ob_flush() before flush().

Additionally, PHP isn’t the only layer that can buffer output. Your web server might also buffer based on its configuration. Even more, if you use fastcgi, buffering can’t be disabled at all.

Serving Files

Nuevo en la versión 2.1: El método makeDisposition fue añadido en Symfony 2.1.

When sending a file, you must add a Content-Disposition header to your response. Es fácil crear esta cabecera básica para descargar archivos, utilizando nombres de archivo no ASCII más envolventes. El método makeDisposition() abstrae el trabajo pesado detrás de una sencilla API:

use Symfony\Component\HttpFoundation\ResponseHeaderBag;

$d = $response->headers->makeDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'foo.pdf');

$response->headers->set('Content-Disposition', $d);

Nuevo en la versión 2.2: The Symfony\Component\HttpFoundation\BinaryFileResponse class was added in Symfony 2.2.

Alternatively, if you are serving a static file, you can use a Symfony\Component\HttpFoundation\BinaryFileResponse:

use Symfony\Component\HttpFoundation\BinaryFileResponse

$file = 'path/to/file.txt';
$response = new BinaryFileResponse($file);

The BinaryFileResponse will automatically handle Range and If-Range headers from the request. It also supports X-Sendfile (see for Nginx and Apache). To make use of it, you need to determine whether or not the X-Sendfile-Type header should be trusted and call trustXSendfileTypeHeader() if it should:

$response::trustXSendfileTypeHeader();

You can still set the Content-Type of the sent file, or change its Content-Disposition:

$response->headers->set('Content-Type', 'text/plain')
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'filename.txt');

Creando una respuesta JSON

Puedes crear cualquier tipo de respuesta vía la clase Symfony\Component\HttpFoundation\Response poniendo las cabeceras y contenido correctos. Una respuesta JSON podría tener la siguiente apariencia:

use Symfony\Component\HttpFoundation\Response;

$response = new Response();
$response->setContent(json_encode(array(
    'data' => 123,
)));
$response->headers->set('Content-Type', 'application/json');

Nuevo en la versión 2.1: The Symfony\Component\HttpFoundation\JsonResponse class was added in Symfony 2.1.

Además, hay una muy útil clase Symfony\Component\HttpFoundation\JsonResponse, la cual incluso, puede hacer esto mucho más fácil:

use Symfony\Component\HttpFoundation\JsonResponse;

$response = new JsonResponse();
$response->setData(array(
    'data' => 123
));

Esto codifica tu arreglo de datos a JSON y pone la cabecera Content-Type a application/json. Si estás utilizando JSONP, puedes especifiar a cual función retrollamada se tendría que pasar el dato:

$response->setCallback('handleResponse');

En este caso, la cabecera Content-Type será text/javascript y el contenido de respuesta tendrá esta apariencia:

handleResponse({'data': 123});

Session

La información de la sesión está en su propio documento: Gestión de la sesión.

Bifúrcame en GitHub