Petición HTTP
Nette encapsula la petición HTTP en objetos con una API clara y ofrece a la vez un filtro de saneamiento.
La petición HTTP está representada por el objeto Nette\Http\Request. Si trabaja con Nette, el framework crea este
objeto automáticamente y puede hacer que se lo pasen mediante inyección
de dependencias. En los presenters basta con llamar al método $this->getHttpRequest(). Si trabaja fuera de
Nette Framework, puede crear el objeto con RequestFactory.
Una gran ventaja de Nette es que, al crear el objeto, sanea automáticamente todos los parámetros de entrada (GET, POST, COOKIE) y también la URL, eliminando los caracteres de control y las secuencias UTF-8 no válidas. Después puede trabajar con esos datos con seguridad. Los datos saneados se usan a continuación en los presenters y los formularios.
Nette\Http\Request
Este objeto es inmutable. No tiene setters; tiene solo un llamado wither, withUrl(), que no cambia el objeto sino
que devuelve una nueva instancia con el valor modificado.
withUrl(Nette\Http\UrlScript $url): Nette\Http\Request
Devuelve un clon con otra URL.
getUrl(): Nette\Http\UrlScript
Devuelve la URL de la petición como objeto UrlScript.
$url = $httpRequest->getUrl();
echo $url; // https://nette.org/en/documentation?action=edit
echo $url->getHost(); // nette.org
Atención: los navegadores no envían el fragmento al servidor, así que $url->getFragment() devolverá una
cadena vacía.
getQuery(?string $key=null): string|array|null
Devuelve los parámetros GET de la petición.
$all = $httpRequest->getQuery(); // array de todos los parámetros de la URL
$id = $httpRequest->getQuery('id'); // devuelve el parámetro GET 'id' (o null)
getPost(?string $key=null): string|array|null
Devuelve los parámetros POST de la petición.
$all = $httpRequest->getPost(); // array de todos los parámetros POST
$id = $httpRequest->getPost('id'); // devuelve el parámetro POST 'id' (o null)
getFile(string|string[] $key): ?Nette\Http\FileUpload
Devuelve un archivo subido como objeto Nette\Http\FileUpload:
$file = $httpRequest->getFile('avatar');
if ($file?->hasFile()) { // ¿se subió algún archivo?
$file->getUntrustedName(); // nombre de archivo enviado por el usuario
$file->getSanitizedName(); // nombre sin caracteres peligrosos
}
Para acceder a una estructura anidada, indique un array de claves.
// <input type="file" name="my-form[details][avatar]">
$file = $request->getFile(['my-form', 'details', 'avatar']);
Como no puede fiarse de los datos externos y por tanto tampoco de la estructura de los archivos, este enfoque es más seguro
que, por ejemplo, $request->getFiles()['my-form']['details']['avatar'], que podría fallar.
getFiles(): array
Devuelve un árbol de todos los archivos subidos en una estructura normalizada cuyas hojas son objetos Nette\Http\FileUpload:
$files = $httpRequest->getFiles();
getCookie(string $key): ?string
Devuelve una cookie, o null si no existe.
$sessId = $httpRequest->getCookie('sess_id');
getCookies(): array
Devuelve todas las cookies.
$cookies = $httpRequest->getCookies();
getMethod(): string
Devuelve el método HTTP usado en la petición.
$httpRequest->getMethod(); // GET, POST, HEAD, PUT
isMethod(string $method): bool
Comprueba el método HTTP usado en la petición. El parámetro no distingue mayúsculas de minúsculas.
if ($httpRequest->isMethod('GET')) // ...
getHeader(string $header): ?string
Devuelve una cabecera HTTP, o null si no existe. El parámetro no distingue mayúsculas de minúsculas.
$userAgent = $httpRequest->getHeader('User-Agent');
getHeaders(): array<string, string>
Devuelve todas las cabeceras HTTP como array asociativo. Las claves están normalizadas a minúsculas.
$headers = $httpRequest->getHeaders();
echo $headers['content-type'];
isSecured(): bool
¿Está cifrada la conexión (HTTPS)? Para que funcione correctamente puede hacer falta configurar un proxy.
isSameSite(): bool
¿Llegó la petición desde el mismo sitio? Desde la versión 3.4 lo sustituye el más capaz isFrom().
isFrom(FetchSite|array $site, FetchDest|array|null $dest=null, ?bool $user=null): bool
Le dice de dónde llegó la petición y cómo la hizo el navegador, a partir de las cabeceras Sec-Fetch-* (los
llamados Fetch Metadata), que el
navegador pone él mismo y que una página que se ejecute en el navegador de la víctima no puede falsificar ni eliminar. Nette
las usa internamente para proteger automáticamente los formularios y las señales contra el Cross-Site Request Forgery (CSRF). Es útil
cuando quiera proteger sus propias acciones sensibles, como endpoints de API o enlaces destructivos.
El método devuelve true solo cuando la petición cumple todas las condiciones que indique. El primer
parámetro $site describe la relación entre la página que inició la petición y su sitio (la cabecera
Sec-Fetch-Site). Acepta un único valor o una lista de estos casos de FetchSite:
FetchSite::SameOrigin: del mismo origen exacto (esquema, host y puerto)FetchSite::SameSite: del mismo sitio, posiblemente de otro subdominioFetchSite::CrossSite: de un sitio ajenoFetchSite::None: la inició directamente el usuario, p. ej. escribiendo la URL o abriendo un marcador
// ¿procede la petición de nuestras propias páginas?
if (!$httpRequest->isFrom([FetchSite::SameOrigin, FetchSite::SameSite])) {
// bloquea la acción
}
El parámetro opcional $dest (la cabecera Sec-Fetch-Dest) dice qué tipo de recurso está obteniendo
el navegador, p. ej. FetchDest::Document para una navegación de nivel superior o FetchDest::Empty para
una petición hecha desde JavaScript. El parámetro opcional $user (la cabecera Sec-Fetch-User) indica
si la navegación la desencadenó una acción real del usuario, como pulsar un enlace o enviar un formulario; pase
true para exigirlo.
Una comprobación de que una acción solo es accesible desde sus propias páginas y solo mediante una acción real del usuario tiene entonces este aspecto:
if (!$httpRequest->isFrom(FetchSite::SameOrigin, FetchDest::Document, user: true)) {
$this->error();
}
Los navegadores antiguos (Safari anterior a 16.4) no envían las cabeceras Sec-Fetch-*. Para ellos,
Nette recurre a una cookie SameSite=Strict que solo demuestra que la petición no es cross-site. Una comprobación
que exija además $dest o $user no se puede verificar así y devuelve false en esos
navegadores; si eso es demasiado estricto, compruebe solo $site.
isAjax(): bool
¿Es una petición AJAX?
getRemoteAddress(): ?string
Devuelve la dirección IP del usuario. Para que funcione correctamente puede hacer falta configurar un proxy.
getRemoteHost(): ?string
Obsoleto, devuelve siempre null. Las consultas DNS inversas eran lentas y poco fiables; si necesita el nombre del
host, resuélvalo usted mismo a partir de getRemoteAddress().
getBasicCredentials(): ?array
Devuelve las credenciales de autenticación de la autenticación HTTP Basic.
[$user, $password] = $httpRequest->getBasicCredentials();
getRawBody(): ?string
Devuelve el cuerpo de la petición HTTP.
$body = $httpRequest->getRawBody();
getOrigin(): ?UrlImmutable
Devuelve el origen desde el que llegó la petición. Un origen se compone del esquema (protocolo), el nombre de host y el
puerto, por ejemplo https://example.com:8080. Devuelve null si la cabecera de origen no está presente
o vale 'null'.
$origin = $httpRequest->getOrigin();
echo $origin; // https://example.com:8080
echo $origin?->getHost(); // example.com
El navegador envía la cabecera Origin en los siguientes casos:
- peticiones cross-origin (llamadas AJAX a otro dominio)
- peticiones POST, PUT, DELETE y otras que modifican
- peticiones hechas con la Fetch API
El navegador NO envía la cabecera Origin en:
- las peticiones GET corrientes al mismo dominio (navegación same-origin)
- la navegación directa escribiendo una URL en la barra de direcciones
- las peticiones de clientes que no son navegadores
A diferencia de la cabecera Referer, Origin contiene solo el esquema, el host y el
puerto, no la ruta completa de la URL. Eso la hace más adecuada para las comprobaciones de seguridad y preserva la privacidad del
usuario. La cabecera Origin se usa sobre todo para la validación CORS (Cross-Origin Resource Sharing).
detectLanguage(array $langs): ?string
Detecta el idioma. Pase como parámetro $langs un array de los idiomas que soporta la aplicación y devolverá el
preferido por el navegador del visitante. No es magia; simplemente usa la cabecera Accept-Language. Si no encuentra
ninguna coincidencia, devuelve null.
// El navegador envía p. ej.: Accept-Language: cs,en-us;q=0.8,en;q=0.5,sl;q=0.3
$langs = ['hu', 'pl', 'en']; // idiomas soportados por la aplicación
echo $httpRequest->detectLanguage($langs); // en
RequestFactory
La clase Nette\Http\RequestFactory sirve para crear
una instancia de Nette\Http\Request, que representa la petición HTTP actual. (Si trabaja con Nette, el framework
crea automáticamente el objeto de la petición HTTP.)
$factory = new Nette\Http\RequestFactory;
$httpRequest = $factory->fromGlobals();
El método fromGlobals() crea el objeto de la petición a partir de las variables globales actuales de PHP
($_GET, $_POST, $_COOKIE, $_FILES y $_SERVER). Al crear el objeto
limpia automáticamente todos los parámetros de entrada (GET, POST, COOKIE) y también la URL de caracteres de control y
secuencias UTF-8 no válidas, lo que garantiza la seguridad al trabajar después con esos datos.
RequestFactory se puede configurar antes de llamar a fromGlobals():
- el método
$factory->setBinary()desactiva la limpieza automática de los parámetros de entrada de caracteres de control y secuencias UTF-8 no válidas. - el método
$factory->setProxy(...)indica la dirección IP del servidor proxy, necesaria para detectar correctamente la dirección IP del usuario. - el método
$factory->setForceHttps().{data-version:3.3.4} fuerza el esquema HTTPS de la petición independientemente del entorno del servidor.
RequestFactory permite definir filtros que transforman automáticamente partes de la URL de la petición. Estos filtros eliminan de las URL caracteres no deseados que pueden haber insertado, por ejemplo, implementaciones incorrectas de los sistemas de comentarios de distintos sitios web:
// elimina los espacios de la ruta
$requestFactory->urlFilters['path']['%20'] = '';
// elimina el punto, la coma o el paréntesis derecho del final del URI
$requestFactory->urlFilters['url']['[.,)]$'] = '';
// limpia la ruta de barras dobles (filtro predeterminado)
$requestFactory->urlFilters['path']['/{2,}'] = '/';
La primera clave, 'path' o 'url', determina a qué parte de la URL se aplicará el filtro. La segunda
clave es la expresión regular que se busca y el valor es el reemplazo que se usará en lugar del texto encontrado.
Archivos subidos
El método Nette\Http\Request::getFiles() devuelve un array de todos los archivos subidos en una estructura
normalizada cuyas hojas son objetos Nette\Http\FileUpload.
Estos encapsulan los datos enviados por el elemento de formulario <input type=file>.
La estructura refleja los nombres de los elementos en HTML. En el caso más simple puede tratarse de un único elemento de formulario con nombre, enviado como:
<input type="file" name="avatar">
En ese caso, $request->getFiles() devuelve el array:
[
'avatar' => /* instancia de FileUpload */
]
El objeto FileUpload se crea aunque el usuario no haya subido ningún archivo o la subida haya fallado. El
método hasFile() devuelve true si se envió un archivo:
$request->getFile('avatar')?->hasFile();
En el caso de un nombre de elemento con notación de array:
<input type="file" name="my-form[details][avatar]">
el árbol devuelto tiene este aspecto:
[
'my-form' => [
'details' => [
'avatar' => /* instancia de FileUpload */
],
],
]
También puede crear arrays de archivos:
<input type="file" name="my-form[details][avatars][]" multiple>
En ese caso, la estructura tiene este aspecto:
[
'my-form' => [
'details' => [
'avatars' => [
0 => /* instancia de FileUpload */,
1 => /* instancia de FileUpload */,
2 => /* instancia de FileUpload */,
],
],
],
]
La mejor forma de acceder al índice 1 del array anidado es esta:
$file = $request->getFile(['my-form', 'details', 'avatars', 1]);
if ($file instanceof Nette\Http\FileUpload) {
// ...
}
Como no puede fiarse de los datos externos y por tanto tampoco de la estructura de los archivos, este enfoque es más seguro
que, por ejemplo, $request->getFiles()['my-form']['details']['avatars'][1], que podría fallar.
Resumen de los métodos de FileUpload
hasFile(): bool
Devuelve true si el usuario subió un archivo.
isOk(): bool
Devuelve true si el archivo se subió correctamente.
getError(): int
Devuelve el código de error asociado al archivo subido. Es una de las constantes UPLOAD_ERR_XXX. Si el archivo se subió correctamente,
devuelve UPLOAD_ERR_OK.
move(string $dest)
Mueve un archivo subido a una nueva ubicación. Si el archivo de destino ya existe, se sobrescribirá.
$file->move('/path/to/files/name.ext');
getContents(): ?string
Devuelve el contenido del archivo subido. Si la subida no fue correcta, devuelve null.
getContentType(): ?string
Detecta el tipo de contenido MIME del archivo subido a partir de su firma. Si la subida no fue correcta o la detección
falló, devuelve null.
Requiere la extensión de PHP fileinfo.
getUntrustedName(): string
Devuelve el nombre original del archivo tal y como lo envió el navegador.
No se fíe del valor que devuelve este método. Un cliente podría enviar un nombre de archivo malicioso con la intención de dañar o comprometer su aplicación.
getSanitizedName(): string
Devuelve el nombre de archivo saneado. Contiene solo caracteres ASCII [a-zA-Z0-9.-]. Si el nombre no contiene esos
caracteres, devuelve 'unknown'. Si el archivo es una imagen JPEG, PNG, GIF, WebP o AVIF, devuelve además la
extensión de archivo correcta.
Requiere la extensión de PHP fileinfo.
getSuggestedExtension(): ?string
Devuelve la extensión de archivo adecuada (sin el punto) que corresponde al tipo MIME detectado.
Requiere la extensión de PHP fileinfo.
getUntrustedFullPath(): string
Devuelve la ruta original del archivo tal y como la envió el navegador al subir un directorio. La ruta completa solo está disponible en PHP 8.1 y superior. En versiones anteriores, este método devuelve el nombre original del archivo.
No se fíe del valor que devuelve este método. Un cliente podría enviar un nombre de archivo malicioso con la intención de dañar o comprometer su aplicación.
getSize(): int
Devuelve el tamaño del archivo subido. Si la subida no fue correcta, devuelve 0.
getTemporaryFile(): string
Devuelve la ruta a la ubicación temporal del archivo subido. Si la subida no fue correcta, devuelve ''.
__toString(): string
Devuelve la ruta a la ubicación temporal del archivo subido. Eso permite usar el objeto FileUpload directamente
como cadena.
isImage(): bool
Devuelve true si el archivo subido es una imagen JPEG, PNG, GIF, WebP o AVIF. La detección se basa en su firma y
no verifica la integridad del archivo entero. Si una imagen está dañada se puede averiguar, por ejemplo, intentando cargarla.
Requiere la extensión de PHP fileinfo.
getImageSize(): ?array
Devuelve el par [width, height] con las dimensiones de la imagen subida. Si la subida no fue correcta o no es una
imagen válida, devuelve null.
toImage(): Nette\Utils\Image
Carga la imagen como objeto Image. Si la subida no fue correcta o no es una
imagen válida, lanza una Nette\Utils\ImageException.