Capítulos
Ejercicios

ico tips&tricks tips&tricks
Trucos bajo la manga

Luis Felipe Ramírez Varela
https://luis.ramirez.cl
luis@ramirez.cl
luisfel

en construcción

Guardando un Array en la tabla.

Suele suceder que necesitamos insertar en un campo de la tabla un array de valores, por ejemplo las opciones de un formulario con múltiples opciones.

Ejemplo:
<form id="form1" name="form1" method="post">
  <p>
    <label>
      <input type="checkbox" name="lectura[]" value="1" id="lectura_0">
      Revistas</label>
    <br>
    <label>
      <input type="checkbox" name="lectura[]" value="2" id="lectura_1">
      Libros</label>
    <br>
    <label>
      <input type="checkbox" name="lectura[]" value="3" id="lectura_2">
      Períodicos</label>
    <br>
    <label>
      <input type="checkbox" name="lectura[]" value="4" id="lectura_3">
      Comics</label>
    <br>
    <label>
      <input type="checkbox" name="lectura[]" value="5" id="lectura_4">
      Envase de shampoo</label>
  </p>
</form>

En el ejemplo anterior, todos los valores del campo lectura[] se convierte en un array, en el servidor, al ser enviado por el formulario. Este array $_POST['lectura'] no se puede guardar en la tabla.

Cómo los array de PHP están en memoria RAM del servidor, no se pueden guardar como tal, pero si lo podemos convertir en una cadena estructurada de datos. Para eso PHP tiene las funciones serialize() que permiten convertir una Array en una cadena estructurada de datos y unserialize() que permite volver a un Array la cadena estructurada.

serialize()

Supongamos que tenemos la siguiente matriz:

Ejemplo:
$var = array("uno", $dos=2, 'tres', "3+1", $dos+3, number_format(6,2));
          
/*Array
(
    [0] => uno
    [1] => 2
    [2] => tres
    [3] => 3+1
    [4] => 5
    [5] => 6.00
)*/

              

Si lo pasamos por la función serialize() El resultado sería como:

Ejemplo:
echo serialize($var);
          
//a:6:{i:0;s:3:"uno";i:1;i:2;i:2;s:4:"tres";i:3;s:3:"3+1";i:4;i:5;i:5;s:4:"6.00";}
          

Si vemos la estructrura, es muy parecido al concepto de JSON (javascrit object notation) para transmitir objetos y arrays como cadena de texto de manera estructurada.

a:6:{i:0;s:3:"uno";i:1;i:2;i:2;s:4:"tres";i:3;s:3:"3+1";i:4;i:5;i:5;s:4:"6.00";}

Revisemo el resultado de esta función sobre el array. Dice que es un array de 6 elementos (a:6). En el primer campo (i:0) tiene una cadena de 3 caracteres (s:3) y su valor es uno. El segundo elemento (i:1) tiene un entero que vale 2 (i:2) el tercer elemento en la posicion 2 (i:2) tiene una cadena de 4 caracteres (s:4) que vale tres, etc.

Ahora que es cadena se puede guardar en una tabla para tener su persistencia.

unserialize()

La función unserialize() por el contrario, se encarga de tomar esta cadena que hemos guardado y volverlo a un formato de array para PHP

Ejemplo:
$array= unserialize(a:6:{i:0;s:3:"uno";i:1;i:2;i:2;s:4:"tres";i:3;s:3:"3+1";i:4;i:5;i:5;s:4:"6.00";})
          
/*Array
(
    [0] => uno
    [1] => 2
    [2] => tres
    [3] => 3+1
    [4] => 5
    [5] => 6.00
)*/

Subir archivos desde el browser al servidor no es tan complejo utilizando la fución de PHP move_uploaded_file(), pero si requiere revisar 3 elementos para que esto se pueda realizar y comprender el comportamiento de Apache con respecto a los archivos.

Vamos por pasos, lo primero es que el formulario que va a enviar el archivo tiene que si o si ser con el método POST, ya que no se puede adjuntar un archivo como parte de la URL, que es el caso del método GET.

El segundo elemento fundamental para que el formulario pueda enviar el archivo, es que el formulario tiene que estar con enctype="multipart/form-data. Normalmente los formularios capturan y transmite los datos como texto simple (text/plain) o como dato porcesado sin espacio y convirtiendo los caracteres especiales en valores ASCapplication/x-www-form-urlencodedapplication/x-www-form-urlencoded HEX (application/x-www-form-urlencoded). Al transmitirlo ecriptado como multipart/form-data lo que le dice que no encripte el contenido de los campos del tipo file para que no sufra alteraciones.

Ejemplo:
<form action="upload.php" id="form1" name="form1" method="post" enctype="multipart/form-data">
  <label for="archivo">File:</label>
  <input type="file" name="archivo" id="archivo">
  <input type="submit" name="enviar" value="enviar">
</form>

El segundo elemento que tenemos que tener claro es el dónde sube el archivo Apache y cómo lo recupero para utilizarlo.

Tenemos que entender, que en la configuración del PHP, en el archivo php.ini, se define el directorio de destino cuando se suben los archivos. Estos suelen ser directorios temporales, o sea que el sistema está borrando su contenido periódicamente para que no sobresature la capacidad de la máquina.

Si le pedimos al PHP que nos muestre el valor que está recibiendo en la variable global $_FILES[], nos damos cuenta que tiene varios valores para el archivo que estamos subiendo:

Ejemplo:
<?php
echo "<pre>",print_r($_FILES,1),"</pre>";
?>
Resultado:
[archivo] => Array
        (
            [name] => prueba.pdf
            [type] => application/pdf
            [tmp_name] => /tmp/phpknBSa0
            [error] => 0
            [size] => 10137198
        )

Si revisamos lo que recibió el archivo php de destino upload.php, veremos un array para el campo de formulario archivo con el nombre del archivo, el tipo de archivo, el nombre temporal del archivo, si hubo o no errores, y cuánto pesa el archivo recibido.

El tercero, el nombre temporal tmp_name tiene la ruta y nombre que le asignó Apache al subir el archivo. Ahora, lo que debemos hacer es tomar de ese lugar el archivo y moverlo a su destino final controlable en el directorio de nuestra aplicación con la función move_uploaded_file().

Ejemplo:
<?php
if ( isset( $_POST[ 'enviar' ] ) && $_POST[ 'enviar' ] == "enviar" ) {
  $origen = $_FILES[ 'archivo' ][ 'tmp_name' ];
  $destino = "upload/" . $_FILES[ 'archivo' ][ 'name' ];

  if ( move_uploaded_file( $origen, $destino ) ) {
    echo "archivo arriba";
  } else {
    echo "ups, hubo un error";
  }

}
?>

En este caso, si recibimos de respuesta "archivo arriba", entonces podemos revisar el directorio upload, que definimos como destino, y debería estar el archivo que subimos con el mismo nombre.

Nota:
Uno de los errores clásicos, por el que no funciona el subir archivo, es que el directorio de destino no tiene permisos para escribir. (chmod 777). Hay que recordar que las acciones de Apache son generadas como anónimo, por lo que el permiso de escritura y ejecución tiene que ser al meno 6 para el anónimo.

El resto del tratamiento de los archivos subido va a depender de los requerimientos del diseño. Si necesitamos registrar en un BD los datos del archivo, o tener mayor control sobre el nombre que se le asigna o validar que sea de un formato espécifico o menor a un peso máximo, son intervenciones sobre este mismo código.

Ejemplo:
<?php
if ( isset( $_POST[ 'enviar' ] ) && $_POST[ 'enviar' ] == "enviar" ) {
  $file_type = $_FILES[ 'archivo' ][ 'type' ]; //devuelve el mimetype o tipo de archivo
  $permitidos = array( "image/jpeg", "image/gif", "image/png" );//lista de archivos permitidos
  if ( !in_array( $file_type, $allowed ) ) {//si el tipo de archivoestá en la lista permitida.
    $error_message = 'Sólo se permiten archivos jpg, gif, y png.';
    echo $error_message;//devuelve mensaje de error.
    exit();//termina el proceso
  } else if ( ( $_FILES[ "archivo" ][ "size" ] > 2000000 ) ) {// si el tamaño del archivo es mayor a 2megas
    $error_message = 'El tamaño del archivo no puede ser de más de 2MB.';
    echo $error_message;//devuelve mensaje de error.
    exit();//termina el proceso
  } else {
    $extension = pathinfo( $_FILES[ "file" ][ "name" ], PATHINFO_EXTENSION );//extráe la extención del archivo de su nombre original
    $nombre_nuevo = date( 'YmdHis' ) . $extension;//construye nuevo nombre para el archivo
    $origen = $_FILES[ 'archivo' ][ 'tmp_name' ];
    $destino = "upload/" . $nombre_nuevo;//define nombre y destino del archivo
    if ( move_uploaded_file( $origen, $destino ) ) {
      echo "archivo arriba";
    } else {
      echo "ups, hubo un error al mover el archivo";
    }
  }
}
?>

En el ejemplo anterior, primero valida que sea un formato de imágen, que no pese más de 2MB y le cambia el nombre a un archivo a un dato único e irrepetible compuesto por el "timestamp" de la fecha/hora en que se procesa.

Ejercicio:
  • Generar un administrador de archivos a subir. Página masetra que se alimente de una BD con todos los archivos subidos y vínculo para ver o descargar el archivo listado. Debe tener un formulario para subir nuevos archivos.