GET
Los datos viajan en la dirección:
buscar.php?q=teclado. Se ven, se pueden guardar en favoritos y
compartir.
Es lo correcto para una búsqueda o un filtro: la dirección describe lo que estás viendo.
Hasta ahora los datos los ponías tú dentro del archivo. A partir de esta lección los pone alguien de fuera: el visitante que llena un formulario de contacto, de pedido o de registro.
Eso cambia dos cosas. La primera, cómo llega el dato hasta tu código. La segunda, más importante: ese dato puede ser cualquier cosa, y tu programa tiene que estar preparado para que lo sea.
<form action="procesar.php" method="post">
<input type="text" name="nombre">
<input type="email" name="correo">
<textarea name="mensaje"></textarea>
<button type="submit">Enviar</button>
</form>Dos atributos mandan aquí. action dice a qué archivo
se le entregan los datos, y method dice cómo viajan.
Y hay un tercero que decide todo lo demás: name. Un campo
sin name no llega al servidor. Es la causa número uno de «no me llega el
dato», y no da ningún error.
Los datos viajan en la dirección:
buscar.php?q=teclado. Se ven, se pueden guardar en favoritos y
compartir.
Es lo correcto para una búsqueda o un filtro: la dirección describe lo que estás viendo.
Los datos viajan dentro de la petición, no en la dirección. No se ven en la barra ni quedan en el historial.
Es lo correcto para lo que cambia algo: registrar, comprar, enviar un mensaje, cambiar una contraseña.
Si volver a cargar la página repitiendo la acción sería un problema -un pedido duplicado, un mensaje enviado dos veces-, es POST.
Y ojo: POST no es seguridad. Solo es menos visible. Lo que protege de verdad es HTTPS, y aun así hay que validar.
<?php
// De un formulario TODO llega como texto, incluso los números.
$nombre = trim($_POST['nombre'] ?? '');
$correo = trim($_POST['correo'] ?? '');
$mensaje = trim($_POST['mensaje'] ?? '');
echo 'Recibido de: ' . $nombre;$_POST es un arreglo asociativo -de los de la lección 4- donde la
clave es el name de cada campo. Con method="get" sería
$_GET, igual de sencillo.
Las dos precauciones de esa línea son fijas y conviene copiarlas siempre:
?? '' evita el aviso si el campo no vino, y trim() quita los
espacios que el visitante dejó sin querer al copiar y pegar.
El formulario de arriba tiene type="email". El navegador, con eso,
se niega a enviar si el texto no parece un correo. Perfecto para el usuario.
Y no vale absolutamente nada como control. Porque nadie está obligado a usar tu formulario para enviarte datos:
curl -X POST https://ejemplo.com/procesar.php \
-d "nombre=cualquiera" \
-d "correo=esto-no-es-un-correo" \
-d "mensaje=hola"Esa orden cabe en un renglón, no necesita tu página y le llega a tu
procesar.php exactamente igual que si viniera del formulario.
El type="email", el required y todo el JavaScript de
validación no han existido.
Escríbelo así: lo del navegador es una sugerencia; lo que decide es el servidor. No es una recomendación de estilo, es la diferencia entre un sistema que aguanta y uno al que le venden un producto de 22.000 pesos por 100.
<?php
$errores = [];
$nombre = trim($_POST['nombre'] ?? '');
$correo = trim($_POST['correo'] ?? '');
$copias = (int) ($_POST['copias'] ?? 0);
if ($nombre === '') {
$errores[] = 'El nombre es obligatorio.';
}
if (mb_strlen($nombre) > 80) {
$errores[] = 'El nombre es demasiado largo.';
}
if (!filter_var($correo, FILTER_VALIDATE_EMAIL)) {
$errores[] = 'El correo no tiene un formato válido.';
}
// La regla de negocio: entre 1 y 50. Aquí, no solo en el <input min max>.
if ($copias < 1 || $copias > 50) {
$errores[] = 'La cantidad debe estar entre 1 y 50.';
}
if ($errores === []) {
echo 'Todo correcto, guardando...';
} else {
foreach ($errores as $error) {
echo '<p>' . htmlspecialchars($error) . '</p>';
}
}Se van acumulando en $errores y se muestran todos juntos.
Corregir un fallo por recarga es la forma más rápida de que alguien abandone un
formulario.
(int) convierte el texto a número entero. Sin eso estarías
comparando el texto '8' con el número 8, que con
=== no coinciden -lección 3-.
filter_var() con FILTER_VALIDATE_EMAIL ya
viene hecho y bien hecho. Comprueba que tenga forma de correo,
que no es lo mismo que comprobar que exista: eso solo lo demuestra enviarle un
mensaje de confirmación.
El tope de 50 no vive en el <input max="50">: vive en
esta comprobación. El atributo del formulario es la comodidad; esta línea es la
regla.
Falta el otro lado del mismo problema. Si el visitante escribe
<script> en el campo del nombre y tú lo imprimes en la página, ese
código se ejecuta en el navegador de quien lo lea. Se llama XSS y es de
los agujeros más comunes que hay.
La defensa es una función y usarla siempre: htmlspecialchars() convierte
los caracteres con significado en HTML -<, >,
&, las comillas- en su versión inofensiva.
<?php
$nombre = '<script>robar()</script>';
echo $nombre; // se ejecuta: mal
echo htmlspecialchars($nombre); // se ve el texto: bien<script>robar()</script>
Desde PHP 8.1 no hace falta pasarle nada más:
htmlspecialchars() ya escapa por defecto también la comilla simple. En
ejemplos de internet verás htmlspecialchars($x, ENT_QUOTES), que era
obligatorio antes y hoy es redundante -aunque no molesta-.
La regla en una frase: se valida al entrar y se escapa al salir. Las dos cosas, siempre, sin excepciones que «esto es interno».
Le pasa a todo el mundo: F5 después de un POST reenvía el formulario y duplica el pedido. La solución estándar es redirigir después de guardar, para que la página que queda cargada sea otra:
header('Location: gracias.php'); exit;
Ese exit no es opcional: sin él, el código de abajo se sigue
ejecutando.
Existe y mezcla GET, POST y cookies. Precisamente por eso no se usa: un dato que esperabas por POST podría llegar por la dirección, y pierdes el control de por dónde entra qué.
Sé explícito: $_POST o $_GET, el que corresponda.
Es muy común y se hace preguntando por el método de la petición:
if ($_SERVER['REQUEST_METHOD'] === 'POST') { ... }
Si es POST se procesa; si no, se pinta el formulario vacío.
Llega en $_FILES y necesita
enctype="multipart/form-data" en el formulario. Tiene sus propias
reglas -comprobar el tipo real y no fiarse de la extensión, y guardar el archivo
fuera de la carpeta pública- y da para una lección entera.
Ese último punto no es un detalle: un archivo subido dentro de la carpeta pública se descarga con solo saber la dirección, aunque tu aplicación pida contraseña.
Tu formulario tiene required y type="email".
¿Qué protege eso?
Un campo del formulario no llega nunca a $_POST. ¿Qué
revisas primero?
$_POST es el name. Sin él, el
campo simplemente no se envía, y no hay ningún error que lo avise.El nombre que escribió el visitante se va a mostrar en la página. ¿Qué haces con él?
<script> guardado se ejecuta en el navegador de quien lea la
página.Ya recibes datos y no te fías de ellos. Ahora el problema es otro: tu sitio son varias páginas y estás copiando la cabecera y el menú en todas. En la siguiente lección se escriben una sola vez.
Siguiente: Armar el sitio por partes
Continuar ← Arreglos y funciones