Programación Hipermedia I

Práctica 10: PHP 5 (sistema de ficheros)

1. Objetivos

2. Recursos

¿Cómo se accede al sistema de ficheros desde PHP? ¿Cómo se crea un fichero? ¿Cómo se lee un fichero? ¿Cómo se crea un directorio?

¿Cómo se maneja el envío de ficheros con PHP?

3. ¿Qué tengo que hacer?

En esta práctica tienes que incluir en la página principal un apartado de “fotos seleccionadas” donde se muestran, de entre todas las fotografías existentes en los álbumes, algunas fotografías seleccionadas por críticos de fotografía. Las fotografías seleccionadas se seleccionan a mano y se gestionan mediante un fichero de texto en el que se almacena una referencia a las fotografías seleccionadas, junto con el nombre de la persona que ha seleccionado cada fotografía y un comentario que explica las razones de dicha selección. Cada vez que se cargue la página principal se tiene que elegir una fotografía distinta seleccionada de forma aleatoria: se tiene que mostrar la fotografía, la información disponible sobre ella, el nombre de la persona que la ha seleccionado y su comentario.

Además, también tienes que manejar la subida de una fotografía de un usuario cuando se registra en la “página con el formulario de registro como nuevo usuario”. También tienes que permitir que la fotografía subida se pueda modificar o eliminar desde la opción “Modificar mis datos” del menú privado del usuario. Importante: todas las fotografías del perfil de los usuarios se tienen que almacenar en un mismo directorio, así que tienes que emplear un sistema de nombrado de ficheros que evite problemas cuando dos usuarios suban dos ficheros con el mismo nombre al servidor.

Por último, también tienes que manejar la subida de una fotografía desde la página “añadir foto a álbum” del menú privado del usuario. Se tiene que emplear alguna estrategia para evitar colisiones de nombres cuando dos usuarios suban dos fotografías (ficheros) con el mismo nombre al servidor o cuando un mismo usuario suba dos fotografías con el mismo nombre a diferentes álbumes.

4. ¿Cómo lo hago?

4.1. Sistema de ficheros

PHP proporciona un gran conjunto de funciones para acceder al sistema de ficheros y manejar los ficheros. La mayoría de las funciones permiten trabajar con ficheros que no están en local, sino que son accesibles a través de Internet mediante una URL.

Las principales funciones para trabajar con ficheros a nivel del sistema de ficheros son:

Las principales funciones para trabajar con directorios son:

El siguiente código muestra en una página web el contenido de un directorio, tal como lo hacen muchos servidores web. Para cada fichero se muestra su nombre, la fecha de la última modificación y el tamaño que ocupa. En la Figura 1 se muestra un ejemplo de la página que genera.

<!DOCTYPE html> 
<html lang="es"> 
<head> 
<meta charset="utf-8" /> 
<title>Prueba de lectura de directorio</title> 
</head> 
<body> 
<?php 
 $nomdir = ’./’; 
 echo "<h1>Contenido de $nomdir</h1>\n"; 
 $dir = opendir($nomdir); 
 
 echo "<pre>\n"; 
 echo "<b>"; 
 echo str_pad("Nombre", 30); 
 echo str_pad("Fecha ult. mod.", 20); 
 echo str_pad("Tamaño", 10); 
 echo "</b></pre>\n"; 
 echo "<hr /><pre>\n"; 
 
 while(($fichero = readdir($dir)) != FALSE) 
 { 
   echo str_pad($fichero, 30); 
   echo str_pad(date("d/m/Y H:i" , filemtime($nomdir . $fichero)), 20); 
   if(is_dir($nomdir . $fichero)) 
   { 
    echo "-"; 
   } 
   else 
   { 
    echo str_pad(filesize($nomdir . $fichero), 10); 
   } 
   echo "<br />\n"; 
 } 
 closedir($dir); 
 
 echo "</pre><hr />\n"; 
?> 
</body> 
</html>

En el ejemplo anterior se emplea la función str_pad(cadena, longitud) para generar cadenas con una longitud definida que se rellenan con espacios en blanco. Además, la función date(formato, tiempo) se emplea para aplicar un formato al valor devuelto por la función filemtime(fichero).


PIC


Figura 1: Visualización del contenido de un directorio


Las principales funciones para manejar los ficheros son:

El siguiente código muestra como leer todo el contenido de un fichero y visualizarlo línea a línea, indicando en cada línea el número de línea que es. En la Figura 2 se muestra como se visualiza el propio fichero.

<!DOCTYPE html> 
<html lang="es"> 
<head> 
<meta charset="utf-8" /> 
<title>Prueba de file</title> 
</head> 
<body> 
<?php 
 if(($fichero = @file("file.php")) == false) 
 { 
   echo "No se ha podido abrir el fichero"; 
 } 
 else 
 { 
   echo "<pre>\n"; 
   foreach($fichero as $numLinea => $linea) 
   { 
    echo "Línea #<b>" . sprintf("%03d", $numLinea) . "</b> : "; 
    echo htmlspecialchars($linea); 
   } 
   echo "</pre>\n"; 
 } 
?> 
</body> 
</html>

En el ejemplo anterior, se emplea el operador @ para evitar que se muestren en la página web los mensajes de error que puede producir la función file(nombre), como por ejemplo al no existir el fichero indicado7. Además, se emplea la función sprintf(formato, variable1, ...) para generar una cadena con un formato establecido; en este ejemplo se emplea para generar una cadena de tres dígitos rellenada con ceros. Por último, se emplea la función htmlspecialchars(cadena) para convertir los caracteres especiales, como <, > y & a entidades de HTML: &lt;, &gt; y &amp;.


PIC


Figura 2: Lectura de un fichero con la función file


4.2. Manejo de envío de ficheros

Para manejar el envío de ficheros a través de una página web desde el cliente al servidor web se emplea la variable predefinida superglobal $_FILES. Esta variable es un array multidimensional donde cada posición representa un fichero que se ha enviado y que contiene la siguiente información:

El manejo del envío de ficheros se puede configurar en el fichero php.ini con las directivas file_uploads, upload_max_filesize, upload_tmp_dir, post_max_size y max_input_time.

El siguiente ejemplo muestra cómo manejar el envío de ficheros. El primer fragmento de código es la página web que muestra un formulario que permite al usuario enviar un fichero. Para que se pueda realizar el envío se tiene que cumplir que:

<!DOCTYPE html> 
<html lang="es"> 
<head> 
<meta charset="utf-8" /> 
<title>Prueba de subir fichero</title> 
</head> 
<body> 
<form action="subirfichero.php" method="post" enctype="multipart/form-data"> 
Fichero: <input type="file" name="fichero" /> 
<br /> 
<input type="submit" value="Enviar" /> 
</form> 
</body> 
</html>

El segundo fragmento de código es la página PHP que recibe el fichero enviado en el servidor. En este código se emplea la función file_exists(nombre) para comprobar si ya existe el fichero que se intenta subir y la función move_uploaded_file(origen, destino) para mover el fichero temporal de la subida a su destino definitivo.

<!DOCTYPE html> 
<html lang="es"> 
<head> 
<meta charset="utf-8" /> 
<title>Prueba de subir fichero</title> 
</head> 
<body> 
<p> 
<?php 
 $msgError = array(0 => "No hay error, el fichero se subió con éxito", 
               1 => "El tamaño del fichero supera la directiva 
                   upload_max_filesize el php.ini", 
               2 => "El tamaño del fichero supera la directiva 
                   MAX_FILE_SIZE especificada en el formulario HTML", 
               3 => "El fichero fue parcialmente subido", 
               4 => "No se ha subido un fichero", 
               6 => "No existe un directorio temporal", 
               7 => "Fallo al escribir el fichero al disco", 
               8 => "La subida del fichero fue detenida por la extensión"); 
 
 if($_FILES["fichero"]["error"] > 0) 
 { 
   echo "Error: " . $msgError[$_FILES["fichero"]["error"]] . "<br />"; 
 } 
 else 
 { 
   echo "Nombre original: " . $_FILES["fichero"]["name"] . "<br />"; 
   echo "Tipo: " . $_FILES["fichero"]["type"] . "<br />"; 
   echo "Tamaño: " . ceil($_FILES["fichero"]["size"] / 1024) . " Kb<br />"; 
   echo "Nombre temporal: " . $_FILES["fichero"]["tmp_name"] . "<br />"; 
 
   if(file_exists("upload/" . $_FILES["fichero"]["name"])) 
   { 
    echo $_FILES["fichero"]["name"] . " ya existe"; 
   } 
   else 
   { 
    move_uploaded_file($_FILES["fichero"]["tmp_name"], 
                  "upload/" . $_FILES["fichero"]["name"]); 
    echo "Almacenado en: " . "upload/" . $_FILES["fichero"]["name"]; 
   } 
 } 
?> 
</p> 
</body> 
</html>

4.3. Generación de números aleatorios

La mayoría de los mecanismos de generación de números aleatorios que se utilizan en los sistemas informáticos son en realidad procesos pseudo-aleatorios, ya que los números se generan a partir de una fórmula y, por tanto, una secuencia de números pseudo-aleatorios se puede reproducir si se conoce el primer número y la fórmula.

En PHP, las principales funciones para generar números pseudo-aleatorios son:

Por ejemplo, el siguiente código genera 10 números pseudo-aleatorios:

<!DOCTYPE html> 
<html lang="es"> 
<head> 
<meta charset="utf-8" /> 
<title>Prueba de números aleatorios</title> 
</head> 
<body> 
<p> 
<?php 
 for($i = 1; $i <= 10; $i++) 
 { 
   echo rand() . "<br />"; 
 } 
?> 
</p> 
</body> 
</html>

5. Recomendaciones

Recuerda que las páginas que contengan código PHP tienen que tener la extensión .php. Si modificas alguna página web que ya tengas hecha de prácticas anteriores para añadirle código PHP, tendrás que cambiarle la extensión y corregir todos los enlaces que apunten a esa página.

Recuerda que para que el código PHP se ejecute, la página web tiene que pasar por el servidor web para que éste se la pase al intérprete de PHP. Para ello, tienes que cargar la página a través del servidor web: la URL de conexión tiene que tener la forma http://localhost.

El manual de PHP te lo puedes descargar en diferentes formatos de su sitio web8 para tenerlo siempre a mano y poder hacer las búsquedas de información rápidamente. También puedes acceder a través de Internet a la ayuda de cualquier función de PHP escribiendo el nombre de la función a continuación de la URL http://php.net/. Por ejemplo, http://php.net/header muestra la ayuda de la función header().

Cuando trabajes con ficheros debes realizar todas las comprobaciones que puedas, ya que los ficheros suelen ser origen de muchos problemas durante el tiempo de ejecución de un programa: un fichero puede ser borrado, puede cambiar su nombre o puede ser movido de sitio. Además, otro problema son los permisos del sistema de archivos: el fichero puede existir, pero puede ser que no tengas permisos para acceder a él.

Permitir que los usuarios puedan subir ficheros al servidor web puede suponer un agujero de seguridad del sitio web, por lo que hay que tomar todas las medidas de protección que se pueda, como por ejemplo limitar el tamaño máximo de fichero que se puede subir o comprobar las extensiones de los ficheros que se suben.

Las funciones de números aleatorios no suelen controlar que no se repita el mismo número en peticiones sucesivas de números aleatorios. Este comportamiento es el esperado en un suceso con probabilidad no condicionada, como puede ser el lanzamiento de una moneda para elegir cara o cruz. Sin embargo, a veces interesa obtener una secuencia de números aleatorios, pero sin que se repita un número en toda la secuencia o que no se repita al menos en dos posiciones consecutivas. En estos casos, es el programador el que debe controlar la generación de los números aleatorios para lograr el resultado esperado.

1http://es.php.net/manual/es/book.filesystem.php

2http://www.w3schools.com/php/php_file.asp

3http://www.w3schools.com/php/php_ref_filesystem.asp

4http://tools.ietf.org/html/rfc1867

5http://es.php.net/manual/es/features.file-upload.php

6http://www.w3schools.com/php/php_file_upload.asp

7No es una buena práctica utilizar @ para “esconder” los errores, lo correcto es utilizar un manejador de errores.

8http://www.php.net/download-docs.php