# Recogida de datos

## Matriz $_REQUEST

Cuando se envía un formulario, PHP almacena la información recibida en una matriz llamada $_REQUEST. El número de valores recibidos y los valores recibidos dependen tanto del formulario como de la acción del usuario.

Cualquier control se envía solamente si está establecido su atributo name. El atributo name del control puede contener cualquier carácter (números, acentos, guiones, etc), pero si contiene espacios, los espacios se sustituyen por guiones bajos (_). Cada control crea un elemento de la matriz $_REQUEST, que se identifica como $_REQUEST[valor_del_atributo_name] y que contiene el valor entregado por el formulario (en su caso).

El siguiente ejemplo muestra un ejemplo de formulario:

    <form action="ejemplo.php">
      <p>Nombre: <input type="text" name="nombre" /></p>
      <p><input type="submit" value="Enviar" /></p>
    </form>
| Nombre:

Mientras se está programando, para comprobar que el fichero php está recibiendo la información enviada por el control, lo más fácil es utilizar la función print_r($matriz) para mostrar el contenido de la matriz $_REQUEST. Una vez se ha comprobado que la información llega correctamente, la línea se debe comentar o eliminar.

El siguiente ejemplo muestra lo que escribiría el programa PHP si recibiera la información del formulario anterior.

Nombre: |
    <?php
    print "<pre>";
    print_r($_REQUEST);
    print "</pre>\n";
    print "<p>Su nombre es $_REQUEST[nombre]</p>";
    ?>
    Array
    (
        [nombre] => Pepito Conejo
    )
Su nombre es Pepito Conejo

Conviene colocar etiquetas <pre> alrededor del print_r($_REQUEST) para facilitar la visualización de los valores.

Volver al principio de la página

## Rerefencia a $_REQUEST dentro y fuera de cadenas

Al hacer referencia a los elementos de la matriz $_REQUEST, hay que tener en cuenta si la referencia se encuentra dentro de una cadena o fuera de ella.

- Si se hace referencia dentro de una cadena al control de nombre "nombre", se debe escribir $_REQUEST[nombre] (sin comillas). Si se escribe $_REQUEST['nombre'] (con comillas) se produce un error.

      <?php
      print "<p>Su nombre es $_REQUEST['nombre']</p>";
      ?>
  | Parse error: parse error, unexpected T_ENCAPSED_AND_WHITESPACE, expecting T_STRING or T_VARIABLE or T_NUM_STRING in ejemplo.php on line 2
      <?php
      print "<p>Su nombre es $_REQUEST[nombre]</p>";
      ?>
  | Su nombre es Pepito Conejo

- Si se hace referencia fuera de una cadena al control de nombre "nombre", se debe escribir $_REQUEST['nombre'] (con comillas). Si se escribe $_REQUEST[nombre] (sin comillas) se produce un aviso.

      <?php
      print "<p>Su nombre es ".$_REQUEST[nombre]."</p>";?>
  | Notice: Use of undefined constant nombre - assumed 'nombre' in ejemplo.php on line 2 Su nombre es Pepito Conejo
      <?php
      print "<p>Su nombre es ".$_REQUEST['nombre']."</p>";
      ?>
  | Su nombre es Pepito Conejo

Volver al principio de la página

## Comprobación de existencia

La mayoría de controles aparecen en la matriz $_REQUEST aunque el usuario no escriba nada en el formulario. El valor almacenado es una cadena vacía, pero está definida. Si el programa supone que se ha introducido algún valor, el resultado puede confundir al usuario ...

Nombre: |
    <?php
    print "<pre>"; print_r($_REQUEST); print "</pre>\n";

    print "<p>Su nombre es $_REQUEST[nombre]</p>";
    ?>
    Array (
        [nombre] =>
    )
Su nombre es

... por lo que conviene incluir una estructura if ... else que considere la posiblidad de que no se haya escrito nada en el formulario:

Nombre: |
    <?php
    print "<pre>"; print_r($_REQUEST); print "</pre>\n";

    if ($_REQUEST['nombre']=="") {
        print "<p>No has escrito ningún nombre</p>";
    } else {
        print "<p>Su nombre es $_REQUEST[nombre]</p>";
    }
    ?>
    Array (
        [nombre] =>
    )
No has escrito ningún nombre

Sin embargo, las casillas de verificación y los botones radio solamente están definidos en la matriz $_REQUEST si se han marcado en el formulario. Por ejemplo, si en el siguiente formulario:

    <form action="ejemplo.php">
      <p>Deseo recibir información: <input type="checkbox" name="acepto" /></p>
      <p><input type="submit" value="Enviar" /></p>
    </form>
| Deseo recibir información:

... el usuario no marca la casilla, la matriz $_REQUEST no contiene ningún dato:

Deseo recibir información: |
    <?php
    print "<pre>"; print_r($_REQUEST); print "</pre>\n";
    ?>
    Array (
    )

Y al pretender utilizar el valor, se produce un aviso por utilizar un índice no definido:

Deseo recibir información: |
    <?php
    print "<p>$_REQUEST[acepto]</p>\n";
    ?>
| Notice: Undefined index: acepto in ejemplo.php on line 2

Este problema se resuelve comprobando que el índice está definido antes de hacer referencia a él, utilizando la función isset($variable), que admite como argumento una variable y devuelve 1 (Verdadero) si existe y 0 (Falso) si no existe.

Deseo recibir información: |
    <?php
    if (isset($_REQUEST['acepto'])) {
        print "<p>Desea recibir información</p>";
    } else {
        print "<p>No desea recibir información</p>";
    }
    ?>
| No desea recibir información
Deseo recibir información: |
    <?php
    if (isset($_REQUEST['acepto'])) {
        print "<p>Desea recibir información</p>";
    } else {
        print "<p>No desea recibir información</p>";
    }
    ?>
| Desea recibir información

En realidad es conveniente efectuar siempre la verificación de existencia, para prevenir los casos en que un usuario intente acceder a la página php sin pasar por el formulario.

Volver al principio de la página

## Seguridad en las entradas

Un usuario puede insertar código html en la entrada de un formulario, lo que puede acarrear comportamientos inesperados y riesgos de seguridad. El siguiente ejemplo solamente perjudicaría al aspecto de la página:

Nombre: |
    <?php
    print "<pre>"; print_r($_REQUEST); print "</pre>\n";

    print "<p>Su nombre es $_REQUEST[nombre]</p>";
    ?>
    Array (
        [nombre] => Pepito Conejo
    )
Su nombre es Pepito Conejo

Para evitarlo, se puede utilizar la función strip_tags($cadena), que elimina todas las etiquetas html, como en el siguiente ejemplo.

Nombre: |
    <?php
    print "<pre>"; print_r($_REQUEST); print "</pre>\n";
    print "<p>Su nombre es".strip_tags($_REQUEST['nombre'])."</p>";
    ?>
    Array (
        [nombre] => Pepito Conejo
    )
Su nombre es Pepito Conejo

El problema es que esta función elimina también cualquier cosa que interprete como una etiqueta, es decir, que empiece por "<", como en el siguiente ejemplo, así que no debería utilizarse si queremos conservar ese carácter.

Nombre: |
    <?php
    print "<pre>"; print_r($_REQUEST); print "</pre>\n";
    print "<p>Su nombre es".strip_tags($_REQUEST['nombre'])."</p>";
    ?>
    <pre>Array
    (
        [nombre] => <pepe>
    )
    </pre>
    <p>Su nombre es</p>

Otra función que conviene aplicar a cualquier entrada de formulario es la función trim($cadena), que elimina los espacios en blanco iniciales y finales y devuelve la cadena sin esos espacios.

Por ejemplo, si en el ejemplo siguiente, el usuario introduce varios espacios en blanco en vez de su nombre, la comprobación no serviría para nada ya que la cadena con espacios en blanco no es una cadena vacía

Nombre: |
    <?php
    print "<pre>"; print_r($_REQUEST); print "</pre>\n";

    if ($_REQUEST['nombre'] == "") {
        print "<p>No ha escrito ningún nombre</p>";
    } else {
        print "<p>Su nombre es $_REQUEST[nombre]</p>";
    }
    ?>
    Array (
        [nombre] =>
    )
Su nombre es

Sin embargo, si se aplica la función trim(), la cadena introducida queda reducida a la cadena vacía y la comprobación la detecta:

Nombre: |
    <?php
    print "<pre>"; print_r($_REQUEST); print "</pre>\n";
    if (trim($_REQUEST['nombre']) == "") {
        print "<p>No ha escrito ningún nombre</p>";
    } else {
        print "<p>Su nombre es ".trim($_REQUEST['nombre'])."</p>";
    }
    ?>
    Array (
        [nombre] =>
    )
No ha escrito ningún nombre

### Comillas mágicas (magic quotes)

Aunque actualmente se desaconseja su uso, muchos servidores tienen activada la extensión magic_quotes_gpc. Cuando esta extensión está activada, PHP añade automáticamente caracteres de escape (\) delante de las comillas que se escriban en un campo de formulario. Para eliminar esos caracteres de escape en los programas, es necesario comprobar si están activadas las comillas mágicas y eliminar los caracteres de escape añadidos.

El ejemplo siguiente muestra lo que ocurriría si están activadas las comillas mágicas (magic_quotes_gpc = On)

Nombre: |
    <?php
    if ($_REQUEST['nombre'] == "") {
        print "<p>No ha escrito ningún nombre</p>";
    } else {
        print "<p>Ha escrito: ".$_REQUEST['nombre']."</p>";
    }
    ?>
| Ha escrito: Me llamo \"Pepe\"

Para eliminar los caracteres de escape, se puede utilizar la función get_magic_quotes_gpc() para detectar si la extensión está activada y la función stripslashes() para eliminarlas.

Nombre: |
    <?php
    if ($_REQUEST['nombre'] == "") {
        print "<p>No ha escrito ningún nombre</p>";
    } else {
        print "<p>Ha escrito: ";
        if (get_magic_quotes_gpc()) {
           print stripslashes($_REQUEST['nombre']);
        } else {
             print $_REQUEST['nombre'];
        }
        print "</p>";
    }
    ?>
| Ha escrito: Me llamo "Pepe"

Volver al principio de la página

## Utilización de variables

Si aplicamos las medidas mínimas de seguridad comentadas en el punto anterior, cualquier referencia a $_REQUEST[control] debería sustituirse por trim(strip_tags($_REQUEST[control])). Si además queremos comprobar si el control está definido, el código se complica.

Una solución es guardar los valores de la matriz $_REQUEST en variables y realizar todas las comprobaciones al definir esas variables. En el resto del código basta con utilizar la variable en vez del elemento de la matriz $_REQUEST.

Nombre: |
    <?php
    print "<pre>"; print_r($_REQUEST); print "</pre>\n";

    if (isset($_REQUEST['nombre'])) {
        $nombre = trim(strip_tags($_REQUEST['nombre']));
    } else {
        $nombre = "";
    }

    if ($nombre == "") {
        print "<p>No ha escrito ningún nombre</p>";
    } else {
        print "<p>Su nombre es $nombre</p>";
    }
    ?>
    Array (
        [nombre] =>
    )
No ha escrito ningún nombre
Nombre: |
    <?php
    print "<pre>"; print_r($_REQUEST); print "</pre>\n";

    if (isset($_REQUEST['nombre'])) {
        $nombre = trim(strip_tags($_REQUEST['nombre']));
    } else {
        $nombre = "";
    }

    if ($nombre == "") {
        print "<p>No ha escrito ningún nombre</p>";
    } else {
        print "<p>Su nombre es $nombre</p>";
    }
    ?>
    Array (
        [nombre] => Pepito Conejo
    )
Su nombre es Pepito Conejo

La asignación de la variable se puede realizar en una sola línea, utilizando la notación abreviada: (condición) ? verdadero : falso;:

Nombre: |
    <?php
    print "<pre>"; print_r($_REQUEST); print "</pre>\n";

    $nombre = (isset($_REQUEST['nombre'])) ? trim(strip_tags($_REQUEST['nombre'])) : "";

    if ($nombre == "") {
        print "<p>No ha escrito ningún nombre</p>";
    } else {
        print "<p>Su nombre es $nombre</p>";
    }
    ?>
    Array (
        [nombre] => Pepito Conejo
    )
Su nombre es Pepito Conejo

Volver al principio de la página

## Salida de datos

Si los datos recogidos se escriben luego en una página web, hay que tener cuidado en algunos casos especiales. Se comentan a continuación dos de ellos y después se comenta una solución general para este tipo de caracteres.

### El carácter & (ampersand)

Si el usuario escribe en una entrada el carácter & (ampersand, entidad de carácter &amp;), si esa cadena se escribe en una página, la página se verá correctamente en el navegador, pero la página no será válida (el xhtml será invalido).

Nombre: |
    <?php
    $nombre = (isset($_REQUEST['nombre'])) ? trim(strip_tags($_REQUEST['nombre'])) : "";

    if ($nombre == "") {
        print "<p>No ha escrito ningún nombre</p>";
    } else {
        print "<p>Su nombre es $nombre</p>";
    }
    ?>
| Su nombre es Pepito & Company

El motivo por el que la página es inválida es que el carácter & indica el comienzo de una entidad de carácter. Como en nuestro caso no hay una entidad de carácter, la página no es válida.

La solución es sustituir el carácter & por su entidad de carácter correspondiente (&amp;). Eso se puede hacer con la función str_replace()

Nombre: |
    <?php
    $nombre = (isset($_REQUEST['nombre'])) ? trim(strip_tags($_REQUEST['nombre'])) : "";
    $nombre = str_replace('&', '&amp;', $nombre);

    if ($nombre == "") {
        print "<p>No ha escrito ningún nombre</p>";
    } else {
        print "<p>Su nombre es $nombre</p>";
    }
    ?>
| Su nombre es Pepito & Company

### El carácter " (comillas)

Si el usuario escribe en una entrada el carácter " (comillas, entidad de carácter &quot;), si esa cadena se escribe dentro de otras comillas (por ejemplo, en el atributo value de una etiqueta input), la página no se verá correctamente y además no será válida.

Nombre: |
    <?php
    $nombre = (isset($_REQUEST['nombre'])) ? trim(strip_tags($_REQUEST['nombre'])) : "";

    if ($nombre == "") {
        print "<p>No ha escrito ningún nombre</p>";
    } else {
        print "<p>Corrija: <input type=\"text\" value=\"$nombre\" /></p>";
    }
    ?>
| Corrija:

El problema es que el código fuente contiene comillas dentro de comillas:

    <p>Corrige: <input type="text" value="Me llamo "Pepe"" /></p>

Como en el caso anterior, la solución es sustituir el carácter " por su entidad de carácter correspondiente (&quot;). Eso se puede hacer con la función str_replace()

Nombre: |
    <?php
    $nombre = (isset($_REQUEST['nombre'])) ? trim(strip_tags($_REQUEST['nombre'])) : "";
    $nombre = str_replace('"', '&quot;', $nombre);

    if ($nombre == "") {
        print "<p>No ha escrito ningún nombre</p>";
    } else {
        print "<p>Corrija: <input type=\"text\" value=\"$nombre\" /></p>";
    }
    ?>
| Corrija:

### Solución general

Además de los caracteres ampersand (&) y comillas ("), también podrían dar problemas los caracteres comillas simples (') o las desigualdades (< y >). Se podría sustituir cada uno de ellos como se ha hecho en los ejemplos anteriores, pero la función htmlspecialchars() realiza todas las sustituciones de una sola vez.

Nombre: |
    <?php
    $nombre = (isset($_REQUEST['nombre'])) ? htmlspecialchars(trim(strip_tags($_REQUEST['nombre']))) : "";

    if ($nombre == "") {
        print "<p>No ha escrito ningún nombre</p>";
    } else {
        print "<p>Corrija: <input type=\"text\" value=\"$nombre\" /></p>";
    }
    ?>
| Corrija:

El ejemplo anterior tiene el inconveniente de que la funcion strip_tags() puede eliminar etiquetas que queríamos conservar. Otra solución sería aplicar primero la función htmlspecialchars(), como en el ejemplo siguiente.

Nombre: |
    <?php
    $nombre = (isset($_REQUEST['nombre'])) ? strip_tags(trim(htmlspecialchars($_REQUEST['nombre']))) : "";

    if ($nombre == "") {
        print "<p>No ha escrito ningún nombre</p>";
    } else {
        print "<p>Corrija: <input type=\"text\" value=\"$nombre\" /></p>";
    }
    ?>
| Corrija:

En este caso la función strip_tags() probablemente no haga nada en la mayoría de los casos.

Volver al principio de la página

## Funciones de recogida de datos

### Recogida de un dato

Para tener en cuenta todos los aspectos comentados en los puntos anteriores, es conveniente definir funciones para la recogida de datos:

- La funcion recoge() del ejemplo siguiente tiene como argumento el nombre del control que se quiere recibir y devuelve el valor recibido o una cadena vacía si el control no se ha recibido.
- La función recoge elimina las etiquetas que pueda contener el texto. Si se desearan recoger etiquetas, la función tendría que ser distinta.
- Al comienzo del programa el dato recibido se almacena en una variable.
- En el resto del programa se trabaja con la variable.

    <?php
    // FUNCIÓN DE RECOGIDA DE UN DATO
    function recoge($var)
    {
        $tmp = (isset($_REQUEST[$var])) ? strip_tags(trim(htmlspecialchars($_REQUEST[$var]))) : '';
        if (get_magic_quotes_gpc()) {
            $tmp = stripslashes($tmp);
        }
        return $tmp;
    }

    // EJEMPLO DE USO DE LA FUNCIÓN ANTERIOR
    $nombre = recoge('nombre');
    if ($nombre == "") {
        print "<p>No ha escrito ningún nombre</p>";
    } else {
        print "<p>Su nombre es $nombre</p>";
    }
    ?>

### Recogida de un dato con valor predeterminado

La función recoge() del apartado anterior se puede modificar para definir un valor predeterminado (es decir, que si el dato no existe, la función devuelve el valor enviado como segundo argumento y si no se envía el valor predeterminado la función devolvería la cadena vacía).

En el ejemplo siguiente, si no se recibe el nombre, se le asigna el nombre "pobrecito hablador".

    <?php
    // FUNCIÓN DE RECOGIDA DE UN DATO CON VALOR PREDETERMINADO
    function recoge($var, $var2='')
    {
        $tmp = (isset($_REQUEST[$var])&&(trim(strip_tags($_REQUEST[$var])!=''))) ?
            strip_tags(trim(htmlspecialchars($_REQUEST[$var]))) : strip_tags(trim(htmlspecialchars($var2)));
        if (get_magic_quotes_gpc()) {
            $tmp = stripslashes($tmp);
        }
        return $tmp;
    }

    // EJEMPLO DE USO DE LA FUNCIÓN ANTERIOR
    $nombre = recoge('nombre', 'pobrecito hablador');
    print "<p>Su nombre es $nombre</p>";
    ?>

### Recogida de una matriz de una dimensión

Si un formulario envía los datos en forma de matriz, como en el ejemplo siguiente, la función recoge() del punto anterior no serviría.

    <form action="ejemplo.php">
      <p>Nombre: <input type="text" name="nombre[1]" /></p>
      <p>Apellido: <input type="text" name="nombre[2]" /></p>
      <p><input type="submit" value="Enviar" /></p>
    </form>
| Nombre: Apellido:

Hay que hacer otra función que recoja y trate los datos en forma de matriz.

- La funcion recogeMatriz() del ejemplo siguiente tiene como argumento el nombre del control que se quiere recibir (que debe ser una matriz de una dimensión, no sirve para matrices de dos o más dimensiones) y devuelve una matriz con los valores recibidos o una matriz vacía si el control no se ha recibido.
- Al comienzo del programa los datos recibidos se almacenan en una matriz.
- En el resto del programa se trabaja con la matriz.

    <?php
    // FUNCIÓN DE RECOGIDA DE UNA MATRIZ DE UNA DIMENSIÓN
    function recogeMatriz($var)
    {
        $tmpMatriz = array();
        if (isset($_REQUEST[$var]) && is_array($_REQUEST[$var])) {
            foreach ($_REQUEST[$var] as $indice => $valor) {
                $tmp = strip_tags(trim(htmlspecialchars($indice)));
                if (get_magic_quotes_gpc()) {
                    $tmp = stripslashes($tmp);
                }
                $indiceLimpio = $tmp;

                $tmp = strip_tags(trim(htmlspecialchars($valor)));
                if (get_magic_quotes_gpc()) {
                    $tmp = stripslashes($tmp);
                }
                $valorLimpio  = $tmp;

                $tmpMatriz[$indiceLimpio] = $valorLimpio;
            }
        }
        return $tmpMatriz;
    }

    // EJEMPLO DE USO DE LA FUNCIÓN ANTERIOR
    $nombre = recogeMatriz('nombre');

    if ($nombre[1]=="") {
        print "<p style=\"color:red\">No ha escrito su nombre.</p>\n";
    } else {
        print "<p>Su nombre es <strong>$nombre[1]</strong>.</p>\n";
    }

    if ($nombre[2]=="") {
        print "<p style=\"color:red\">No ha escrito su apellido.</p>\n";
    } else {
        print "<p>Su apellido es <strong>$nombre[2]</strong>.</p>\n";
    }
    ?>

### Recoger y recortar los datos

En caso de que por algún motivo se quiera recortar el tamaño de los datos recibidos a un tamaño determinado se puede crear una función recorta() que recorte la longitud de los campos y llamarla desde la función recoge():

    <?php
    // FUNCIONES DE RECOGIDA Y RECORTE DE UN DATO
    $tamNombre = 30;

    $recorta = array(
        'nombre'   => $tamNombre);

    function recorta($campo, $cadena)
    {
        global $recorta;

        $tmp = isset($recorta[$campo]) ? substr($cadena, 0, $recorta[$campo]) : $cadena;
        return $tmp;
    }

    function recoge($var)
    {
        $tmp = (isset($_REQUEST[$var])) ? strip_tags(trim(htmlspecialchars($_REQUEST[$var]))) : '';
        if (get_magic_quotes_gpc()) {
            $tmp = stripslashes($tmp);
        }
        $tmp = recorta($var, $tmp);
        return $tmp;
    }

    // EJEMPLO DE USO DE LAS FUNCIONES ANTERIORES
    $nombre = recoge('nombre');
    if ($nombre == "") {
        print "<p>No ha escrito ningún nombre</p>";
    } else {
        print "<p>Su nombre es $nombre</p>";
    }
    ?>

Volver al principio de la página

Esta obra está bajo una licencia de Creative Commons Reconocimiento-CompartirIgual 3.0 España.