04 Formularios
04 Formularios
04 Formularios
CEFIRE
ndice de contenidos
1 - Presentacin de la Sesin.......................................................................................4 2 - Breve Repaso A Los Formularios............................................................................5 3 - Envo Y Recepcin De Datos en Formularios.........................................................6 4 - Acceso Desde PHP A Los Diferentes Tipos De Elementos De Entrada.................8 4.1 - Elemento INPUT TEXT.....................................................................................8 4.2 - Elemento INPUT RADIO...................................................................................8 4.3 - Elemento INPUT CHECKBOX..........................................................................9 4.4 - Elemento INPUT BUTTON................................................................................9 4.5 - Elemento INPUT HIDDEN...............................................................................10 4.6 - Elemento INPUT PASSWORD........................................................................10 4.7 - Elemento INPUT SUBMIT...............................................................................11 4.8 - Elemento SELECT SIMPLE............................................................................11 4.9 - Elemento SELECT MLTIPLE........................................................................12 4.10 - Elemento TEXTAREA...................................................................................12 4.11 - Elemento INPUT IMAGE...............................................................................13 4.12 - Paso de Matrices A PHP...............................................................................14 4.13 - Paso De Datos Por QUERY_STRING..........................................................15 4.14 - Elemento INPUT FILE...................................................................................16 4.14.1 - Gestin De Subida de Ficheros Desde HTML......................................16 4.14.2 - Tratamiento De Subida de Ficheros Desde PHP..................................17 4.14.3 - Problemas ms habituales en la configuracin.....................................17 4.14.4 - Ejemplo de subida de ficheros comentado...........................................18 5 - Procesamiento De Formularios En PHP................................................................21 5.1 - Procesamiento En Un Solo Script...................................................................21 5.2 - Validacin De Formularios Desde El Servidor................................................22 5.2.1 - Expresiones Regulares...........................................................................22 5.2.2 - Esquemas de validacin..........................................................................24 6 - Aendice I................................................................................................................30 7 - Bibliografa.............................................................................................................37
CEFIRE
1 - PRESENTACIN DE LA SESIN
En esta parte del bloque vamos a ver como enviar datos desde formularios en el cliente al script de PHP que los procesar en el servidor. Tambin se abordarn algn esquema de validacin de datos, para que estos lleguen de forma correcta al servidor. No se abordar, dejndose para ms adelante la gestin de cookies y sesiones.
-4-
Aunque los elementos los describiremos ms adelante, podemos destacar las etiquetas fieldset y legend para agrupar elementos de un formulario y aplicarles estilos comunes.
<html> <body> <form action="procesa_formulario.php" method="post"> <fieldset> <legend>datos personales</legend> <div> nombre: <input type="text" name="nombre" value="" size="20"> </div> <div> correo: <input type="text" name="correo" value="" size="20"> </div> <div> <input type="submit" name="enviar" value="enviar"> <input type="reset" name="borrar" value="borrar formulario"> </div> </fieldset> </form> </body> </html>
-5-
CEFIRE
-6-
Si enviamos los datos mediante GET, se procesarn por el siguiente script procesa_formulario.php: y podemos observar que los pares nombre-valor van en la url:
http://localhost:8084/pruebas/procesa_formulario.php? nombre=Juan&enviar=Enviar
<html> <body> <?php if (isset($_GET['enviar']) === true) // tambin seria vlido $_REQUEST['nombre'] echo 'hola '.$_GET['nombre']; else echo 'no hay datos para visualizar'; ?> </body> </html>
Si enviamos los datos mediante POST, tambin se procesarn por procesa_formulario_php pero en la URL no veremos los pares nombre valor, irn ocultos en el mensaje HTTP-POST.
<html> <body> <?php if (isset($_POST['enviar']) === true) // tambin seria vlido $_REQUEST['nombre'] echo 'hola '.$_POST['nombre']; else echo 'no hay datos para visualizar'; ?> </body> </html>
-7-
CEFIRE
-8-
-9-
CEFIRE
No se visualizar nada.
- 10 -
- 11 -
CEFIRE
- 12 -
- 13 -
CEFIRE
- 14 -
El siguiente script se enva a si mismo las variables screen.widht y screen.height de Javascript para saber que resolucin de pantalla se esra usando en el cliente.
<?php // Como hemos pasado los datos en el QUERY_STRING usamos $_GET if (isset($_GET['ancho'])===true && isset($_GET['altura'])===true) { echo "El ancho de la pantalla es: ". $_GET['ancho'] ."<br />\n"; echo "La altura de la pantalla es: ". $_GET['altura'] ."<br />\n"; } else { echo "<script language='javascript'>\n"; // Pasamos las variables al propio script. echo " location.href=\"${_SERVER['SCRIPT_NAME']}?"; // Concatenamos el QUERY_STRING que ya tuviera la pagina. echo "${_SERVER['QUERY_STRING']}"; // Concatenamos las variables de Javascript de alto y ancho de la // resolucin que esta usando el usurio. echo "ancho=\" + screen.width + \"&"; echo "altura=\" + screen.height;\n"; echo "</script>\n"; exit(); } ?>
- 15 -
CEFIRE
- 16 -
En el propio formulario A travs de un campo oculto en el propio formulario denominado MAX_FILE_SIZE cuyo valor indicar el tamao mximo en bytes y que no puede ser mayor al especificado en upload_max_filesize.
<h1>Subida de ficheros</h1> <form enctype="multipart/form-data" action="subida.php" method="post"> <input type="hidden" name="MAX_FILE_SIZE" value="1000000"> Seleccione el fichero .GIF a subir: <input name="imagen" type="file"> <input type="submit" value="Enviar"> </form>
- 17 -
CEFIRE
Si nos fijamos la mayor parte del cdigo esta dirigido a la comprobacin de errores, puesto que la carga de archivos implica riesgos potenciales en la seguridad que deberemos tener controlados.
- 18 -
A continuacin comentaremos las diferentes partes de cdigo de ejemplo. En primer lugar comprobamos el cdigo de error en $_FILES[imagen]['error'] cuyos defines para los valores que puede contener son: UPLOAD_ERR_OK Valor: 0; El fichero se ha subido con xito. UPLOAD_ERR_INI_SIZE Valor: 1; El tamao del fichero excede el establecido en la directiva upload_max_filesize. UPLOAD_ERR_FORM_SIZE Valor: 2; El tamao del fichero excede el establecido en MAX_FILE_SIZE que se especifico en el Form de HTML. UPLOAD_ERR_PARTIAL Valor: 3; El fichero se ha subido pracialmente. UPLOAD_ERR_NO_FILE Valor: 4; no se ha subido ningn fichero. UPLOAD_ERR_NO_TMP_DIR Valor: 6; No se encuentra el directorio temporal de descarga. UPLOAD_ERR_CANT_WRITE Valor: 7; fallo de escritura en disco. Introducido en UPLOAD_ERR_EXTENSION Valor: 8; Subida parada debido a la extensin.
Una segunda comprobacin de seguridad puede ser que el contenido del fichero sea del tipo que esperamos examinando su tipo MIME en $_FILES[imagen][' type'] A finales del 2000 exista un tipo de ataque el cual consista en engaar al script para que tomara como archivo subido algn archivo local, de tal manera que si su contenido se mostraba o se copiaba en otra ubicacin donde quedara accesible poda producir fallos de seguridad.(http://seclists.org/bugtraq/2000/Sep/0055.html) Para evitar este tipo de ataques se incluy la funcin: bool is_uploaded_file ( string nombre_archivo ) Devuelve TRUE si el archivo dado por nombre_archivo fue cargado a travs de HTTP POST, lo cual nos permite verificar que un usuario malicioso no ha intentado engaar al script hacindole trabajar sobre algn archivo local como por ejemplo, /etc/passwd en Linux. Para que trabaje adecuadamente, la funcin is_uploaded_file() necesita un argumento como $_FILES[imagen][' tmp_name'] Existe otra funcin que cumple un cometido similar. Si utilizramos copy para copiar el archivo temporal a otra ubicacin con otro nombre para que no se pierda o para acceder a el posteriormente a la finalizacin del script. Nadie nos asegura que no estamos copiando un archivo local de nuestro sistema a una ubicacin accesible. Para ello se introdujo la funcin:
- 19 -
CEFIRE
bool move_uploaded_file ( string nombre_archivo, string destino ) Esta funcin realiza un chequeo para asegurar que el archivo indicado por nombre_archivo sea un archivo cargado vlido.
Por este motivo fijmonos que antes de mover el archivo subido, comprobamos con bool is_file ( string nombre_archivo ) que no existe en destino un archivo con el nombre $_FILES[imagen]['name'] y si as sucede aadimos como prefijo al nombre una marca de tiempo para que no sea sobrescrito.
- 20 -
Si aplicamos este esquema al ejemplo sencillo que vimos al principio, nos quedar el siguiente cdigo:
<html> <body> <?php if (isset($_GET['enviar']) === true) echo 'hola '.$_GET['nombre']; else { ?> <form action="<?php echo $_SERVER['PHP_SELF'] ?>" method="get"> nombre: <input type="text" name="nombre" value="" size="20"> <input type="submit" name="enviar" value="enviar"> </form> <?php } ?> </body> </html>
Primero comprobamos si existe la variable del submit y si es as, suponemos que el resto de variables del formulario estn tambin establecidas. En caso contrario mostramos el formulario y en ACTION usamos la variable supreglobal $_SERVER['PHP_SELF'] para enviar los datos al mismo script que esta mostrando el formulario.
- 21 -
CEFIRE
Aunque no se va a entrar en la construccin de ER, si que vamos a ver algunas de las funciones de PHP para utilizar ER usando PCRE. (Posiblemente si ya las hemos utilizado en otros lenguajes nos suenen las ER de los ejemplos).
Para aprender a construir expresiones regulares o patrones en la librera PCRE podemos consultar la siguiente pgina http://www.php.net/manual/es/reference.pcre.pattern.syntax.php
- 22 -
int preg_match ( string $pattern , string $subject [, array &$matches [, int $flags [, int $offset ]]] ) Busca en el string subject las coincidencias con la expresin regular pattern. Si se proporciona array de entrada/salida matches, entonces ste se llena con los resultados de la bsqueda. $matches[0] contendr el texto que coincidi con el patrn completo, $matches[1] tendr el texto que coincidi con el primer sub-patrn entre parntesis capturado, y as sucesivamente. Normalmente, la bsqueda comienza por el principio de la cadena objetivo. Devuelve el nmero de veces que pattern coincide. Esto pordr ser 0 veces (sin coincidencias) o 1 vez ya que preg_match() detendr la bsqueda despus de la primera coincidencia. FALSE en caso de error en la expresin regular.
function EsNumeroReal($texto) { $patron = "/^[-+]?\d*[,.]?\d+([eE][-+]?\d+)?$/"; return (preg_match($patron, $texto) > 0)?true:false; } $stdin = fopen('php://stdin', 'r'); for(;;) { echo 'Numero real (escribe "s" para terminar): '; if (($nuemro = trim(fgets($stdin,1000))) == "s") exit; echo $nuemro; echo (EsNumeroreal($nuemro) === true)?' es ':' no es '; echo "un numero real \n"; } fclose($stdin);
Como hemos comentado en la descripcin del parmetros, a travs del array $matches podemos obtener el valor de sub-patrones. Algo muy til si queremos extraer datos de un texto,
$fecha = '2007-09-17'; $patron = /^(\d{4})-(\d{2})-(\d{2})$/; if (preg_match($patron, $matches) > 0) // Mostrar 1007-09-17 -> 17-09-2007 echo $matches[0].' -> '.$matches[3].'-'.$matches[2].'-'. $matches[1]; else echo 'Formato de fecha no vlido: '.$fecha;
Como en otras libreras, tambin podemos etiquetar los sub-patrones para aclarar el cdigo y evitar errores de la siguiente manera.
$fecha = '2007-09-17'; $patron = "/^(?<ao>\d{4})-(?<mes>\d{2})-(?<dia>\d{2})$/"; if (preg_match($patron, $matches) > 0) echo $matches['dia'].'-'.$matches['mes'].'-'.$matches['ao']; else echo 'Formato de fecha no vlido: '.$fecha;
- 23 -
CEFIRE
Aunque tambin podemos aplicar un esquema en el que se muestre el formulario slo una vez de la siguiente forma:
SI Se ha enviado el formulario Validar datos FINSI SI Se ha enviado el formulario y no hay errores Procesar formulario SINO Mostrar formulario con valores por defecto o ya enviados Mostrar algn aviso de error. FINSI
Vamos a poner un ejemplo de implementacin del segundo esquema o patrn de validacin, donde se muestra solo una vez el formulario....
- 24 -
valida.inc
- 25 -
CEFIRE
formulario.css
- 26 -
- 27 -
CEFIRE
{ ?>
<div class="Form"> <form action="<?php echo $_SERVER['PHP_SELF'] ?>" method="post" name="Form_ejemplo"> <?php // Mostramos la etiqueta Nombre con o sin error. echo '<div class = "EtiControl">'; echo '<span class="'; echo $errores['Nombre']?'EtiquetaError">*':'Etiqueta">'; echo 'Nombre</span>'; // Mostramos el campo de texto para Nombre acompaado del // aviso de error si procede. echo '<span class="Control">'; echo '<input name="Nombre" id="Nombre" size="30" type="text" value="'.$datos['Nombre'].'">'; AvisoError($errores, $txterrores, 'Nombre'); echo '</span>'; echo '</div>'; // Mostramos la etiqueta Nota con o sin error. echo '<div class = "EtiControl">'; echo '<span class="'; echo $errores['Nota']?'EtiquetaError">*':'Etiqueta">'; echo 'Nota</span>'; // Mostramos el campo de texto para Nota acompaado del // aviso de error si procede. echo '<span class="Control">'; echo '<input name="Nota" id="Nota" size="3" type="text" value="'.$datos['Nota'].'">'; AvisoError($errores, $txterrores, 'Nota'); echo '</span>'; echo '</div>';?> <!-- Botones de enviar y reestablecer --> <div class = "EtiControl"> <span class="Control"> <input type="submit" name="datosenviados" value="Enviar"> <input name="Reestablecer" type="reset" id="Reestablecer" value="Restablecer"> </span> </div> </form> <?php // Aviso de revisin de errores. if ($hayErroresEnFormulario === true) echo '<br /><div class="Error">* Vuelva a introducir los campos no vlidos.</div>'; ?> </div> <!-- del Form --> <?php } ?> </body> </html>
ejemplo.php
- 28 -
El siguiente cdigo nos proporcionara una salida como esta al introducir un campo con uno o ms valores no vlidos:
Si queremos ver una explicacin ms detellada de este ejemplo podemos consultar el Apndice I de este documento.
- 29 -
CEFIRE
6 - AENDICE I
En el ejemplo del punto 5.2.2, intentamos implementar el modelo en que se validan los errores mostrando una sola vez el formularo. El mostarlo una vez evita que el cdigo se repita y as evitar posibles errores adems de simplificar su actualizacin. Su seudocdigo un poco ampliado sera:
SI Se ha enviado el formulario Obtener los datos Validar datos SINO Inicializar los datos por defecto a mostrar la primera vez. FINSI SI Se ha enviado el formulario y no hay errores Procesar formulario SINO Mostrar formulario con los datos por defecto o ya enviados Mostrar algn aviso de error FINSI
Vamos a intentar explicar de forma detallada como hemos implementado este esquema en el ejemplo.
- 30 -
SI Se ha enviado el formulario Obtener los datos Validar datos SINO Inicializar los datos por defecto a mostrar la primera vez. FINSI
Los datos del formulario se introducen en la matriz asociativa $datos y as ya podemos tener 2 o 400 datos en el formulario, que ni la interfaz, ni la implementacin de la funcin valida_CamposCorrectos cambiar. Adems definimos otra matriz asociativa $tipos, que ser un mapeo de tipos de datos a comprobar de la matriz $datos. Por ejemplo si en el formulario vamos a tener un campo llamado Nombre tendremos: $datos['Nombre'] $tipos['Nombre'] Siendo $datos['Nombre'] donde tendremos el valor del nombre Siendo $tipos['Nombre'] donde tendremos el tipo del campo a validar ('texto').
$enviadoFormulario = isset($_POST['datosenviados']); if ($enviadoFormulario === true) { // Obtenemos los datos recibidos. $datos = array(); $datos['Nombre'] = trim($_POST['Nombre'], " \t"); $datos['Nota'] = $_POST['Nota']; // Definimos la comprobacin para los datos recibidos. $tipos = array(); $tipos['Nombre'] = 'texto'; $tipos['Nota'] = 'entero'; // Validamos el formulario, obteniendo los errores si los hay. if (valida_CamposCorrectos($datos, $tipos, $errores, $txterrores)) $hayErroresEnFormulario = false; else $hayErroresEnFormulario = true; } else { // Inicializamos los datos a vaco, para la primera visualizacin // del formulario. $datos = array(); $datos['Nombre'] = ""; $datos['Nota'] = ""; $hayErroresEnFormulario = false; $errores = false; $txterrores = false; }
- 31 -
CEFIRE Validacin:
Definimos las comprobaciones para cada tipo, lo ms sencillo es utilizar expresiones regulares y por eso en el material comentamos algunas funciones de la librera preg. Como el objetivo de este curso no es explicar ER por tanto no pediremos que creis ninguna. Las que podis necesitar las proporcionamos. Aquellos que las hayan utilizado en otros lenguajes como Java, C#, Perl, etc.. podreis utilizarlas sin problemas. Como indicamos en el punto 5.2.1, si tnis tiempo podis profundizar en su sitaxis en la siguente URL_ http://www.php.net/manual/es/reference.pcre.pattern.syntax.php
<?php function valida_EsTexto($texto) { $patron = "/^[A-Za-z][A-Za-z ]*$/"; return (preg_match($patron, $texto) > 0)?true:false; } function valida_EsEntero($texto) { $patron = "/^[0-9]+$/"; return (preg_match($patron, $texto) > 0)?true:false; } // Aqu podemos aadir otras funciones de validacin.
- 32 -
Recibe como parmetros de entrada: $datos: Matriz asociativa con Nombre campo (Clave) Dato campo (Valor) $tipos: Matriz asociativa mapeada con $datos con Nombre campo (Clave) Tipo (Valor) Devuelve como parmetros de salida: $errores: Matriz asociativa con Nombre campo (Clave) bool (Valor) el valor ser true en caso de que haya un error en el tipo del campo y false en caso sontrrio. $txterrores: Matriz asociativa mapeada con $errores con Nombre campo (Clave) texto (Valor) el texto del valor ser una descripcin del error en caso de que $errores['nombre_campo'] = true; Adems si ha habido algn error la funcin se evaluar a true y false en caso contrrio.
function valida_CamposCorrectos($datos, $tipos, &$errores, &$txterrores) { $numDatos = count($datos); assert($numDatos == count($tipos)); // Validacin mapeo. $correctos = true; reset($datos); while (list($clave, $valor) = each($datos)) { switch($tipos[$clave]) { case 'texto': $errores[$clave] =!valida_EsTexto($valor); $txterrores[$clave] = $clave." debes introducir un texto."; break; case 'entero': $errores[$clave] =!valida_EsEntero($valor); $txterrores[$clave] = $clave." debes introducir un nmero entero."; break; // Aqu podemos aadir otros casos de validacin. case 'nada': // Si no hay tipo, no hay error.. $errores[$clave] = false; break; default: // La opcin por defecto no se puede dar. assert(false); } if ($errores[$clave] === true) $correctos = false; } return $correctos; } ?>
- 33 -
SI Se ha enviado el formulario y no hay errores Procesar formulario SINO Mostrar formulario con los datos por defecto o ya enviados Mostrar algn aviso de error FINSI
if ($enviadoFormulario === true && $hayErroresEnFormulario === false) { // Si todo ha ido correctamente procesamos los datos. echo '<div class = "Resultado">'; while (list($clave, $valor) = each($datos)) { echo '<div class = "EtiControl">'; echo '<span class="Etiqueta">'.$clave.':</span>'; echo '<span class="Control">'.$valor.'</span><br />'; echo '</div>'; } echo '</div>'; } else { ?> // Este cdigo podra ir en una funcin para aclarar el esquema. <div class="Form"> <form action="<?php echo $_SERVER['PHP_SELF'] ?>" method="post" name="Form_ejemplo"> <?php
El modelo de visualizacin de la etiqueta y control con el error asociado ser igual para todos los campos y se explica a continuacin: 1 Mostramos la etiqueta con un estilo diferente dependiendo de si hay error o no: Sabremos que el campo tiene un error por que $errores['<nombre_campo>'] se evaluar a true.
// Mostramos la etiqueta Nombre con o sin error. echo '<div class = "EtiControl">'; echo '<span class="'; echo $errores['Nombre']?'EtiquetaError">*':'Etiqueta">'; echo 'Nombre</span>';
2 Mostramos el control con el valor del dato asociado : $datos['<nombre_campo>'] contendr el valor a visualizar en el control, ya sea el dato por defecto si ejecutamos la primera vez o el dato errneo si volvemos a pedir los datos.
// Mostramos el campo de texto para Nombre acompaado del // aviso de error si procede. echo '<span class="Control">'; echo '<input name="Nombre" id="Nombre" size="30" type="text" value="'.$datos['Nombre'].'">';
- 34 -
3 Tras mostrar la etiqueta y el dato mostramos el error asociado al campo SI LO HAY: Para ello utilizaremos la siguiente la funcin AvisoError
// Funcin encargada de mostrarme los errores en un determinado campo. function AvisoError($errores, $txterrores, $campo) { if ($errores != false && $errores[$campo]) echo '<br /><span class="Error">'. $txterrores[$campo].'</span>'; }
En este caso le indicamos que el campo (clave de las matrices asocitivas $errores y $txterrores) es 'Nombre' y dentro de la funcin AvisoError comprobar si hay error en $errores[$campo] y si lo hay, mostrar el texto del error obtenindolo de $txterrores.
AvisoError($errores, $txterrores, 'Nombre'); echo '</span>'; echo '</div>';
El resto de etiquetas seguirn un formato parecido, de echo quiz podramos generalizarlo para todos los imput text metiendolo en una funcin y as no repetir cdigo.
// Mostramos la etiqueta Nota con o sin error. echo '<div class = "EtiControl">'; echo '<span class="'; echo $errores['Nota']?'EtiquetaError">*':'Etiqueta">'; echo 'Nota</span>'; // Mostramos el campo de texto para Nota acompaado del // aviso de error si procede. echo '<span class="Control">'; echo '<input name="Nota" id="Nota" size="3" type="text" value="'.$datos['Nota'].'">'; AvisoError($errores, $txterrores, 'Nota'); echo '</span>'; echo '</div>';?> <!-- Botones de enviar y reestablecer --> <div class = "EtiControl"> <span class="Control"> <input type="submit" name="datosenviados" value="Enviar"> <input name="Reestablecer" type="reset" id="Reestablecer" value="Restablecer"> </span> </div> </form>
- 35 -
CEFIRE
- 36 -
7 - BIBLIOGRAFA
DIVERSOS AUTORES Manual PHP Oficial Versin web 17/08/2010 desde RedIris http://docs.php.net/manual/es/ Editor principal Philip Olson. Copyright 1997-2010 por el Grupo de documentacin de PHP Bajo Licencia http://creativecommons.org/licenses/by/3.0/
- 37 -