Archivo

Archive for the ‘PHP’ Category

Curso PHP. Capítulo 13: Control de excepciones.

9 julio, 2010 7 comentarios

A partir de la versión 5, PHP incluye un mecanismo para controlar las excepciones con una sintaxis muy similar al mecanismo que incluye JAVA o C#. La principal diferencia con estos lenguajes es que las funciones nativas de la API de PHP no lanzan excepciones, al menos de momento.

PHP cuenta con dos tipos de excepciones predefinidas, Exception y ErrorException, y un conjunto de excepciones definidas en la SPL (standard PHP library).

Podemos lanzar excepciones manualmente utilizando la palabra clave throw:

throw new Exception('Mensaje');

El constructor de la clase Exception adopta una serie de parámetros opcionales, un mensaje y un código de error.

Para manejar las excepciones que lanzamos utilizamos la estructura de control try/catch. Todo el código que pueda lanzar una excepción debe incluirse dentro del bloque try ya que este bloque es el encargado de parar la ejecución del script y pasar el control al bloque catch cuando se produzca una excepción.

try
{

    // Codigo que puede lanzar excepciones

}
catch ( Exception $excepcion )
{

    // Codigo para controlar la excepcion

}

Puede haber más de un bloque catch asociado al mismo bloque try siempre que cada bloque catch espere capturar un tipo de excepción distinta. El objeto que se pasa al bloque catch es el objeto que se ha lanzado con la instrucción throw.

Vamos a ver un ejemplo para entender mejor el mecanismo de control de las excepciones:

<?php
try{
   throw new Exception('Se ha producido un error muy grave.');
}
catch(Exception $excepcion)
{
    echo $excepcion->getMessage();
}

echo 'El bloque catch no finaliza la ejecución total del script.';
?>

En este ejemplo lo que estamos haciendo es lanzar directamente una excepción de la clase Exception informando que se ha producido un error muy grave. Una vez lanzada la excepción, el bloque catch la captura y pasa a procesarla mostrando al usuario el mensaje que hemos utilizado en el constructor del objeto excepción. Para ello utilizamos el método getMessage(). La clase Exception cuenta con un conjunto de métodos que muestran información sobre la excepción que ha ocurrido:

getCode(): Devuelve el código tal cual se haya pasado al constructor. Si no pasamos ninguno por defecto devuelve el código de error cero.

getMessage(): Devuelve el mensaje tal cual se haya pasado al constructor. En nuestro caso devolvería la cadena ‘Se ha producido un error muy grave.’.

getFile(): Devuelve la ruta completa al archivo de código que haya lanzado la excepción.

getLine(): Devuelve el número de la línea del archivo de código en el que se ha producido la excepción.

getTrace(): Devuelve un array con la traza que indica dónde se ha producido la excepción.

getTraceAsString(): Devuelve la misma información que el anterior método pero con formato de cadena.

– También podemos utilizar el método mágico __toString() para customizar la información que mostramos sin imprimimos directamente el objeto excepción.

Cabe destacar que realmente no deberíamos mostrar está información al usuario en un entorno de producción debido que muestra información sensible de como está desarrollado nuestra aplicación. Una solución efectiva es mostrar siempre una plantilla de error básica que informe un poco del error ocurrido y darle la opción al usuario de volver al inicio o a la página anterior.

Podemos crear nuestras propias excepciones personalizadas si creamos una clase que herede de la clase base Exception, pero no podemos modificar el comportamiento de la mayoría de métodos ya que están declarados como finales. Para personalizar el mensaje de error podemos modificar el comportamiento del método mágico __toString(). Vamos a ver un ejemplo de excepción personalizada:

<?php

class MiExcepcionPersonalizada extends Exception
{

  function __toString()
  {
     return $this->getMessage();
  }

}

try{
   throw new MiExcepcionPersonalizada('Se ha producido un error muy grave.');
}
catch(MiExcepcionPersonalizada $excepcion)
{
   echo $excepcion;
}

?>

Como hemos visto podemos lanzar excepciones y manejarlas utilizando el mecanismo de control de excepciones de PHP. Importante destacar que de momento ninguna función nativa de PHP lanza por sí solas excepciones. Los errores comunes de PHP que se muestran en el navegador si tenemos las directivas de mostrar errores en on, no son mapeadas a expeciones automáticamente. No sé si en versiones posteriores de PHP los errores típicos que suprimimos con el operador @ podrán ser controlados de esta forma. Estaría genial que PHP se convirtiera con el tiempo en un lenguaje totalmente orientado a objetos.

Categorías: PHP Etiquetas: ,

Curso PHP. Capítulo 12: Interactuando con MySQL.

2 julio, 2010 2 comentarios

Retomamos el curso de PHP con un nuevo capítulo donde veremos como interactuar con el sistema gestor de base de datos MySQL desde PHP. Para poder seguir este capítulo es recomendable haber leído los artículos dedicados a las sentencias SQL en MySQL.

Capítulo 1: Introducción

Capítulo 2: Sentencias SQL de definición de datos

Capítulo 3: Sentencias SQL de manipulación de datos

Si has llegado hasta aquí sin haber leído los capítulos del curso de MySQL tienes que saber que en ellos creamos una base de datos llamada blog con tres tablas para guardar los articulos, las categorías y los comentarios. Además insertamos una serie de datos de prueba que utilizaremos para los ejemplos de este capítulo.

Os adjunto el conjunto de sentencias SQL que crean la base de datos, las tablas y los datos de prueba. Lo guardais en un archivo con extensión .sql y tipeais el comando SOURCE en el monitor MySQL para ejecutar las sentencias SQL directamente desde el archivo.

CREATE DATABASE IF NOT EXISTS blog;

USE blog;

CREATE TABLE articulo
( id_articulo BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
id_categoria INT UNSIGNED NOT NULL,
titulo VARCHAR(250) NOT NULL,
contenido TEXT NOT NULL
);

CREATE TABLE comentario
( id_comentario INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
id_articulo BIGINT UNSIGNED NOT NULL ,
autor VARCHAR(250) NOT NULL,
contenido TEXT NOT NULL
);

CREATE TABLE categoria
( id_categoria INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
titulo VARCHAR(250) NOT NULL,
descripcion TEXT NOT NULL
);

INSERT INTO categoria (titulo, descripcion)
VALUES ('PHP','Categoría dedicada a PHP'),
('MySQL','Categoría dedicada a MySQL'),
('Javascript','Categoría dedicada a Javascript');

INSERT INTO articulo (id_categoria, titulo, contenido)
VALUES (1,'Curso PHP. Capítulo 1','Este es el contenido del capítulo 1.'),
(1,'Curso PHP. Capítulo 2','Este es el contenido del capítulo 2.'),
(1,'Curso PHP. Capítulo 3','Este es el contenido del capítulo 3.'),
(2,'Curso MySQL. Capítulo 1','Este es el contenido del capítulo 1.'),
(2,'Curso MySQL. Capítulo 2','Este es el contenido del capítulo 2.'),
(2,'Curso MySQL. Capítulo 3','Este es el contenido del capítulo 3.'),
(3,'Curso Javascript. Capítulo 1','Este es el contenido del capítulo 1.'),
(3,'Curso Javascript. Capítulo 2','Este es el contenido del capítulo 2.'),
(3,'Curso Javascript. Capítulo 3','Este es el contenido del capítulo 3.');

INSERT INTO comentario (id_articulo, autor, contenido)
VALUES (1,'Francisco Belmonte','Genial el primer capítulo del curso de PHP.'),
(1,'Estefania Martinez','Sí, me ha servido de mucho, gracias.'),
(3,'Francisco Belmonte','Gran capítulo tres, a la espera de más.'),
(3,'Estefania Martinez','Eso, que me he quedado con ganas de seguir aprendiendo.'),
(4,'Estefania Martinez','Genial, has comenzado uno de MySQL.'),
(6,'Estefania Martinez','Muchas gracias por la tercera entrega de MySQL.'),
(7,'Francisco Belmonte','Suerte con este nuevo curso de JavaScript.');

Ya tenemos la base de datos de prueba lista para interactuar con ella desde PHP. Vamos a ver en concreto la extensión mysqli orientada a objetos de PHP para interactuar con MySQL. En capitulos posteriores veremos PDO, la capa de abstracción de acceso a datos utilizada por PHP para interactuar con diferentes sistemas gestores de bases de datos.

Configurando una conexión

El primer paso para configurar una conexión a MySQL es instanciar un objeto de la clase mysqli. El constructor de la clase mysqli admite hasta cuatro parámetros: el host donde está alojado el servidor, el nombre del usuario que tiene permisos para acceder a la base de datos, la contraseña del usuario y el nombre de la base de datos que se va a utilizar.

En mi caso utilizaría la siguiente instrucción en PHP:

$mysqli = new mysqli('localhost', 'user', 'user123', 'blog');

Con todo esto le indicamos:

  1. Que quermos conectarnos al sevidor de forma local, también podría haberle pasado la dirección IP local ‘127.0.0.1’ como primer parámetro o la dirección IP del servidor si está alojado en otro ordenador, aunque lo normal es tenerlo instalado en el mismo ordenador que hace de servidor web.
  2. Que vamos a utilizar al usuario ‘user’ para accceder al servidor. Este usuario lo he añadido yo y tiene los privilegios mínimos necesarios para hacer un subconjuntos de consultas suficientes para lo que queremos hacer.
  3. Que la contraseña del usuario que vamos a utilizar para acceder a la base de datos es ‘user123’.
  4. Que la base de datos que vamos a utilizar es ‘blog’, la que construimos en los artículos dedicados a MySQL.

Una vez instanciado este objeto ya podemos empezar a interactuar con la base de datos, pero si necesitamos utilizar otro servidor o otra base de datos podemos utilizar los métodos connect() y select_db() del objeto mysqli.

También podemos utilizar estos métodos para instanciar por primera vez el objeto mysqli de la siguiente forma:

$mysqli = new mysqli();
$mysqli->connect('localhost', 'user', 'user123', 'blog');

Instanciamos la clase mysqli con el constructor vacío y utilizamos el método connect() que acepta los mismos parámetros que el constructor de la clase.

Incluso podemos hacerlo de la siguiente forma:

$mysqli = new mysqli('localhost', 'user', 'user123');
$mysqli->select_db('blog');

Instanciamos la clase mysqli pasándole los tres primeros parámetros y después seleccionamos la base de datos con el método select_db().

Como vemos tenemos varias formas de configurar la conexión al servidor MySQL. A partir de aquí ya podemos realizar consultas al servidor.

Cuando finaliza el script las conexiones al servidor se cierran automáticamente pero también podemos utilizar el método close() para cerrar la conexión de la siguiente forma:

$mysqli->close();
Errores al conectar:

Antes de enviar cualquier consulta al servidor a través del objeto mysqli es conveniente comprobar que la conexión se ha establecido satisfactoriamente. Para ello la clase mysqli cuenta con dos propiedades, connect_errno y connect_error que devuelven el código de error de la última llamada de conexión y una cadena descriptiva del último error de conexión respectivamente. Si no se ha producido ningún error la propiedad connect_errno devuelve el valor cero.

if($mysqli->connect_errno)
{
   die('Error al conectar: ' . $mysqli->connect_error);
}

Obviamente mostrar la cadena descriptiva del error producido al usuario no es lo más conveniente, lo mejor sería redireccionar a una página de error especialmente preparada para este tipo de ocasiones.

Definiendo la información del servidor en un archivo separado

Otro factor importante a tener en cuenta es que pueden haber cambios en la configuración del servidor, se puede cambiar el usuario o la contraseña si el servidor ha sido comprometido o cambiar el nombre de la base de datos. Si definimos una serie de constantes en un archivo separado no tendremos que buscar por todos los scripts las diferentes configuraciones al servidor para modificarlas una a una.

Podemos por ejemplo definir una serie de constantes en un archivo separado:

define('MYSQL_HOST','localhost');
define('MYSQL_USER','user');
define('MYSQL_PASS','user123');
define('MYSQL_DB','blog');

Cada vez que queramos configurar una conexión utilizaremos esas constantes siempre que hayamos incluido antes el archivo con la información:

$mysqli = new mysqli(MYSQL_HOST, MYSQL_USER, MYSQL_PASS, MYSQL_DB);

Enviando consultas a la base de datos

Podemos enviar consultas a la base de datos a través del método query() que toma como parámetro un string con la sentencias SQL que se desea ejecutar en la base de datos. Importante destacar que si la consulta utiliza datos de entrada del usuario hay que filtrarlos para evitar ataques SQL-Injection. En este capítulo no vamos a entrar mucho en detalle de como filtrar los datos satisfactoriamente ya que estoy escribiendo un artículo dedicado exclusivamente a las inyecciones SQL.

El método query() devuelve:

  • FALSE si falla la consulta a la base de datos.
  • Un objeto de la clase mysqli_result si devuelve resultados, por ejemplo, en una sentencia SELECT.
  • TRUE si la sentencia no falla y no se devuelve resultados, por ejemplo, en una sentencia INSERT.
$consulta = 'SELECT * FROM categoria ORDER BY titulo ASC';
$resultado = $mysqli->query($consulta);

Accediendo a los resultados de la consulta

Una vez ejecutada la consulta podemos acceder a los datos devueltos por el servidor a través del objeto de la clase mysqli_result que cuenta con una serie de métodos para extraer las filas del resultado.

Lo primero que podemos hacer es consultar el número de filas que ha devuelto el resultado de nuestra consulta SELECT a través de la propiedad num_rows del objeto resultado. Si hubieramos ejecutado una sentencia INSERT, UPDATE o DELETE podríamos acceder al número de columnas afectadas a través de la propiedad affected_rows.

$num_resultados = $resultado->num_rows;

De esta forma podemos obtener las filas del resultado en un bucle:

for( $i=0; $i<$num_resultados; $i++ )
{
     //obtenemos las filas del resultado
}

Para obtener cada fila o registro del resultado de la consulta contamos con una serie de métodos:

  1. El método fetch_object() que devuelve la fila actual del resultado en forma de objeto.
  2. El método fetch_row() que devuelve la fila actual del resultado como una matriz de índice numérico.
  3. El método fecth_assoc() que devuelve la fila actual del resultado como una matriz asociativa.

Vamos a ver un ejemplo de como utilizar cada método, para ello mostraremos al usuario todas las filas del resultado que hemos obtenido al ejecutar la sentencia SELECT anterior.

Utilizando el método fetch_object():
for( $i=0; $i<$num_resultados; $i++ )
{
   $fila        = $resultado->fetch_object();
   $id          = $fila->id_categoria;
   $titulo      = $fila->titulo;
   $descripcion = $fila->descripcion;
   echo 'ID_CATEGORIA: '.$id.'<br />';
   echo 'TITULO: '.$titulo.'<br />';
   echo 'DESCRIPCION: '.$descripcion.'<br />';
   echo '<br />';
}

El método fetch_object() nos devuelve un objeto que tiene una serie de propiedades que se llaman igual que el nombre de las columnas de la tabla y cuyo valor es el valor de la columna de la fila actual del resultado.

Utilizando el método fetch_row():
for( $i=0; $i<$num_resultados; $i++ )
{
   $fila        = $resultado->fetch_row();
   $id          = $fila[0];
   $titulo      = $fila[1];
   $descripcion = $fila[2];
   echo 'ID_CATEGORIA: '.$id.'<br />';
   echo 'TITULO: '.$titulo.'<br />';
   echo 'DESCRIPCION: '.$descripcion.'<br />';
   echo '<br />';
}

El método fetch_row() nos devuelve una matriz de índice numérico con el valor de las columnas de la fila actual del resultado.

Utilizando el método fetch_assoc():
for( $i=0; $i<$num_resultados; $i++ )
{
   $fila        = $resultado->fetch_assoc();
   $id          = $fila['id_categoria'];
   $titulo      = $fila['titulo'];
   $descripcion = $fila['descripcion'];
   echo 'ID_CATEGORIA: '.$id.'<br />';
   echo 'TITULO: '.$titulo.'<br />';
   echo 'DESCRIPCION: '.$descripcion.'<br />';
   echo '<br />';
}

El método fetch_assoc() nos devuelve una matriz asociativa que utiliza como claves los nombres de las columnas de la tabla y cuyo valor es el valor de la columna de la fila actual del resultado. Tambien tenemos el método fetch_all() que acepta un parámetro que puede ser:

  • MYSQLI_ASSOC: Si solo queremos que devuelva una matriz asociativa.
  • MYSQLI_NUM: Si solo queremos que devuelva una matriz con índices numéricos.
  • MYSQLI_BOTH: Si queremos que devuelva una matriz asociativa a la que también se pueda acceder por índices numéricos.

Liberando la memoria del resultado

Es conveniente liberar la memoria que está utilizando el objeto resultado una vez que no vayamos a trabajar más con él. Para ello podemos utilizar el método free() del objeto resultado.

$resultado->free();

A partir de aquí ya no podemos acceder a las filas del resultado dado que hemos liberado la memoria que utilizaba.

RESUMEN

Ya hemos visto los principales métodos para conectarse al servidor MySQL, envíar consultas y acceder a los resultados. Queda pendiente el artículo sobre inyecciones SQL y como protegerse de ellas. No vamos a entrar en detalle sobre el uso de consultas preparadas, una nueva funcionalidad en la nueva extensión mysqli, pero podeis informaros en el manual de referencia oficial de PHP.

La curva de desarrollo que yo seguí una vez tenía conocimientos básicos de PHP fue la siguiente:

  1. Empezé programando pequeños scripts para aprender lo básico.
  2. Después practicaba mientras creaba una aplicación similar al fotolog, pero utilizando muchos includes y la programación estructurada, entremezclando código de la lógica de negocios con la presentación.
  3. Después fui mejorandola cambiando algunas cosas por objetos y utilizando la arquitectura modelo-vista-controlador para no estar todo el rato entremezclando código..
  4. Cansado de hacer las cosas poco eficientes empezé a investigar sobre frameworks de desarrollo en PHP y encontré Symfony, me enamoré de él nada más estudiarlo un poco. Descubrí el uso de los ORM y fue una maravilla en comparación a utilizar consultas relacionales.

Y es que el mundo del desarrollo de aplicaciones engloba muchas tecnologías y tienes que aprender a dominarlas todas, no solo te sirve PHP, tienes que aprender a rezalizar buenos marcados de contenido con XHTML, buenas presentaciones en css, añadir funcionalides interactivas en el lado del cliente con JavaScript, técnicas de accesibilidad, etc.

Un mundo enorme por explorar.

Categorías: MySQL, PHP Etiquetas: ,

Recomendando libros: Symfony

21 junio, 2010 Deja un comentario

Seguimos recomendando libros, en este caso, vamos a ver una serie de libros que tratan sobre este framework PHP de desarrollo de aplicaciones webs dinámicas orientadas a objetos siguiendo una arquitectura modelo-vista-controlador.

Cuando uno se inicia en PHP suele seguir un enfoque procedimental, es decir, los scripts hacen uso de un conjunto de funciones organizadas en ficheros a las que va llamando mediante includes, ya sean para mostrar formularios o para conectarse a una base de datos. Además en la mayoría de ocasiones te encuentras con un código que entremezcla PHP con HTML muy difícil de mantener y de realizar cambios.

Symfony establece un marco de trabajo para desarrollar aplicaciones webs dinámicas siguiendo un enfoque orientado a objetos, además separa la lógica de las páginas de la lógica de negocios y la vista. De esta forma estructuramos nuestras aplicaciones de una manera organizada y más facil de mantener y de realizar modificaciones.

Descubrí este framework gracias a la colección de libros que tanto me ayudo en mis inicios al mundo del desarrollo web.  Una colección de libros traducidos por Javier Eguíluz que podeis encontrar en www.librosweb.es.

– Symfony: la guía definitiva 1.2

Actualmente la última versión estable de symfony es la 1.4.5 pero este libro traducido por Javier Eguíluz sigue sirviendo para iniciarse en el mundo de symfony. El libro es una traducción del libro original escrito por el creador del framework, Fabien Potencier, y la verdad es que te quedas maravillado al ver la potencia que tiene este framework.

– Jobeet, symfony en la práctica.

Este libro está enfocado en la creación de un ejemplo de desarrollo utilizando el framework symfony. Podeis seguir los capítulos para crear paso a paso una aplicación de gestión de ofertas trabajos del estilo infojobs. Recomiendo su lectura despues de estudiarse el anterior libro. De esta forma puedes aclarar más los conocimientos que has ido adquiriendo desarrollando una aplicación totalmente funcional.

– Los formularios de symfony.

Este libro está dedicado exclusivamente al tratamiento de los formularios con symfony.

– Más con symfony.

Libro que extiende los conocimientos de symfony para desarrolladores más expertos que ya hayan tocado este framework. No puedo decir mucho de este libro porque todavía no he tenido tiempo de echarle un vistazo.  Podeis encontrarlo traducido al castellano en el mismo sitio que todos los demás.

Si algo bueno tiene este framework es toda la documentación disponible a la que podeis acceder desde la página oficial del proyecto (totalmente en inglés) o desde las páginas de Javier Eguíluz: www.symfony.es y www.librosweb.es.

Categorías: Libros, Symfony Etiquetas: , ,

Curso PHP. Capítulo 11: Sesiones y cookies

16 junio, 2010 9 comentarios

HTTP es un protocolo sin estado. Esto quiere decir que el protocolo no dispone de un método para conservar el estado entre dos peticiones de un mismo usuario. PHP dispone de una forma para implementar las sesiones generando un identificador de sesión aleatorio que se almacena en el lado del cliente en forma de cookie, o si el cliente tiene deshabilitada las cookies se pasa al servidor por la URL. Este identificador actua como una clave y nos permite almacenar variables de sesión que se guardan en el servidor.

Cookies

Una cookie es es un fragmento de información que queda almacenada en el ordenador del usuario. Las cookies contienen pares de clave/valor y solo son accesibles por el dominio que las crea. En PHP hay dos formas de crear cookies en el ordenador del usuario, mediante la función setcookie() o enviando una cabecera en la respuesta HTTP. Para poder utilizar los dos métodos no se deben haber enviado datos al flujo de salida. Vamos a ver como crear cookies utilizando los dos métodos y como recuperar la información pasada por cookies desde el servidor con PHP.

-Cabecera Set-Cookie:

El protocolo HTTP cuenta con una cabecera para establecer cookies y podemos enviarla con la función header() como hemos visto en capítulos anteriores. La cabecera en cuestión tiene la siguiente sintaxis:

– Set-Cookie: NOMBRE=VALOR;  expires=FECHA; path=RUTA; domain=NOMBRE_DOMINIO; secure

De esta forma se crea una cookie con el nombre NOMBRE y con el valor VALOR. Los demás parámetros son opcionales, el campo expires establece el tiempo que se mantendrá la cookie en el lado del cliente, path y domain los podemos utilizar para restringir el acceso a la cookie a ciertos dominios o rutas, y secure establece si la cookie se envia a través de una conexión segura HTTPS.

<?php
header('Set-Cookie: nombre=Francisco;');
?>

En este ejemplo hemos creado una cookie en el ordenador del usuario con la clave nombre y el valor francisco.

– setcookie():

Esta función recibe como parámetros los mismos que la cabecera Set-Cookie, un nombre, un valor, una fecha, una ruta, un dominio y un booleano para establecer cookies seguras.

<?php
setcookie('nombre','Francisco');
?>

El mismo ejemplo utilizando la función.

-Acceder a las cookies desde PHP:

Podemos acceder a las cookies definidas mediante la variable super-global $_COOKIE, un array asociativo que almacena los valores mediantes las claves definidas en las cookies. De esta forma en nuestro ejemplo podemos acceder a la cookie creada de la siguiente forma.

<?php
$nombre=$_COOKIE['nombre'];
//Imprimirá Francisco
echo $nombre;
?>

Podemos eliminar las cookies si establecemos la misma cookie con una fecha de caducidad pasada. El problema de utilizar cookies para guardar información referente al usuario es que los datos se almacenan en el ordenador del usuario, y pueden no estar disponibles si el usuario ha deshabilitado el uso de cookies en su navegador. Esta es la principal razón por las que se utilizan las sesiones para guardar datos entre distintas peticiones del mismo usuario.

Sesiones

PHP utiliza por defecto cookies con sesiones. Esto quiere decir que si el navegador del usuario acepta cookies se definirá una para almacenar el identificador de sesión. Si el navegador del usuario no acepta cookies, el identificador se pasará como un parámetro más por la URL. Si queremos utilizar este último método siempre, debemos activar la directiva session.use_trans_sid en el archivo de configuración de PHP, aunque no está muy recomendado.

Los pasos que hay que seguir para utilizar sesiones son los siguientes:

1. Iniciar una sesión.

2. Registrar variables de sesión.

3. Utilizar estas variables.

4. Anular las variables registradas y eliminar la sesión.

Iniciando una sesión:

Para iniciar una sesión basta con llamar a la función session_start() antes de enviar cualquier caracter al flujo de salida siempre que queramos utilizar sesiones. Esta función comprueba si hay un identificador para el usuario, y si lo hay carga en la variable super-global $_SESSION todas las variables de sesión registradas.

<?php
session_start();
?>
Registrando variables de sesión:

Para registrar variables de sesión basta con definir un elemento en el array asociativo $_SESSION de la siguiente forma: $_SESSION[‘clave’]=valor;

<?php
session_start();

$_SESSION['nombre']='Francisco';
?>
Utilizando variables de sesión:

Antes de poder utilizar las variables de sesión definidas tenemos que iniciar la sesión. Una vez iniciadas ya podemos acceder a las variables desde el array asociativo $_SESSION[‘clave’]. Si utilizamos objetos como variables de sesión debe incluir antes la definición de clase.

<?php
session_start();
//Imprimirá Francisco
echo $_SESSION['nombre']';
?>
Anular las variables de sesión y eliminar la sesión:

Para eliminar una variable de sesión cuando ya no necesitemos utilizarla, podemos llamar a la función unset() pasandole como argumento la variable que queramos eliminar. No intenteis borrar el array $_SESSION completo porque puede deshabilitar las sesiones. Si queremos anular todas las variables podemos crear un nuevo array vacío.

Para destruir la sesión llamamos a la función session_destroy().

<?php
session_start();

//Eliminamos un par clave/valor
unset($_SESSION['nombre']);

//Eliminamos todas las variables de sesión
$_SESSION = array();

//Destruimos la sesion
session_destroy();
?>
Configurar el control de sesiones:

En el archivo de configuración de PHP existe un conjunto de directivas para configurar las sesiones. Ya hemos visto la directiva session.use_trans_sid que permitia establecer la forma de pasar el id a través de las URLs.

Algunas directivas que podemos configurar son:

– session.auto_start : Inicia las sesiones automáticamente, con el inconveniente que no se pueden almacenar objetos como variables de sesión.

– session.cache_expire: Establece la duración en minutos de la sesión.

– session.cookie_lifetime: Indica el tiempo que permanecerá la cookie de sesión en el navegador del usuario. Por defecto está 0, que indica que se borrará la cookie cuando el navegador se cierre.

– session.use_cookies: Establece las sesiones para que utilizen cookies. Por defecto viene activado.

RESUMEN

Utilizando las sesiones en PHP podemos implementar la autenticación de usuarios, por ejemplo, para mostrar ciertos contenidos dependiendo del usuario que esté utilizando la aplicación. Bastaría con definir una variable de sesión con el nombre o el id del usuario registrado en el sistema.

Ya hemos visto gran parte de los conceptos que utiliza PHP para intercambiar información entre distintas peticiones. En el próximo capítulo vamos a ver como podemos interactuar con el sistema gestor de base de datos MySQL desde PHP, pero antes voy a intentar redactar un artículo sobre el lenguaje de consulta estructurado SQL.

Hasta la próxima.

Categorías: PHP Etiquetas:

Curso PHP. Capítulo 10: Subida y descarga de ficheros.

15 junio, 2010 22 comentarios

Ahora que ya sabemos manipular archivos vamos a ver un ejemplo sencillo de aplicación que permita subir y descargar ficheros.

Subida de ficheros

Los formularios pueden contener un campo para subir ficheros al servidor. Antes de ver la lógica del sistema de subida de ficheros que vamos a implementar, vamos a explicar la variable super-global $_FILES de PHP y los atributos que hay que modificar en el formulario para permitir la subida de ficheros.

<form method="post" action="subir.php" enctype="multipart/form-data">
   <input type="file" name="archivo" id="archivo" />
   <input type="submit" value="Subir fichero" />
</form>

Este formulario de ejemplo muy sencillo lo hemos diseñado con dos campos, un campo de tipo file para seleccionar el archivo que se subirá al servidor, y un botón de tipo submit que envía el formulario por el método post al script subir.php.  Cabe destacar el uso del atributo enctype=»multipart/form-data» que añadirá la cabecera en la petición HTTP para indicarle al servidor que el cuerpo de la petición incluye un archivo.

Cuando envíamos una petición HTTP con archivos al servidor, PHP detecta que estamos subiendo un archivo y lo almacena en un directorio temporal establecido en la directiva upload_tmp_dir del archivo de configuración de PHP. El tamaño del archivo no debe sobrepasar lo establecido en la directiva upload_max_filesize.

Podemos acceder al fichero subido a través del array super-global $_FILES[‘name del campo file’] que nos ofrece un conjunto de propiedades a las que podemos acceder:

$_FILES[‘archivo’][‘tmp_name’]: El valor almacenado en esta clave devuelve el directorio temporal en el que se ha almacenado el archivo en el servidor.

$_FILES[‘archivo’][‘name’]: El nombre del archivo en el sistema del usuario.

$_FILES[‘archivo’][‘size’]: El tamaño en bytes del archivo subido.

$_FILES[‘archivo’][‘type’]: El tipo MIME del archivo, por ejemplo text/plain o image/gif.

$_FILES[‘archivo’][‘error’]: Código de error si se ha producido algún error.

Comprobando que se ha subido el fichero:

Podemos utilizar la función is_uploaded_file() que toma como parámetro el nombre temporal del fichero subido al servidor, no el nombre del fichero del usuario, para ver si se ha subido satisfactoriamente al servidor. Esta función devuelve TRUE si el archivo está en el directorio temporal, y FALSE en caso contrario.

<?php
if (is_uploaded_file($_FILES['archivo']['nombre_tmp']))
{
   echo 'El archivo se ha subido con éxito';
}
?>
Comprobando que el fichero no sobrepasa el tamaño permitido:

Una simple comparación entre el tamaño que queramos permitir subir y $_FILES[‘archivo’][‘size’] bastará para asegurarnos que no se sobrepasa el tamaño permitido.

<?php

if ( is_uploaded_file($_FILES['archivo']['tmp_name']) )
{
   echo 'El archivo se ha subido con éxito';

   if( $_FILES['archivo']['size']<(512*1024) )
   {
       echo 'El archivo no sobrepasa el tamaño máximo: 512KB';
   }

}

?>
Comprobando que el fichero tiene una extensión permitida:

Para comprobar si el fichero tiene una extensión permitida basta con comprobar el tipo MIME del fichero con las extensiones que permitimos.

<?php
if ( is_uploaded_file($_FILES['archivo']['tmp_name']) )
{
   echo 'El archivo se ha subido con éxito';

   if( $_FILES['archivo']['size']<(512*1024) )
   {
       echo 'El archivo no sobrepasa el tamaño máximo: 512KB';

        if( $_FILES['archivo']['type']=='image/jpeg')
        {
             echo 'La extensión JPEG está permitida';
        }
   }
}
?>
Mover el fichero del directorio temporal a nuestro directorio de subidas:

Si no movemos el archivo del directorio temporal a un directorio donde tengamos almacenados los ficheros subidos por los usuarios, el fichero se perderá en el limbo de PHP y no podremos recuperarlo. Para ello contamos con la función move_uploaded_file() que toma dos parámetros, la ruta del fichero en el directorio temporal y la nueva ruta donde queramos almacenarlo.

<?php

if ( is_uploaded_file($_FILES['archivo']['tmp_name']) )
{
   echo 'El archivo se ha subido con éxito';

   if( $_FILES['archivo']['size']<(512*1024) )
   {
       echo 'El archivo no sobrepasa el tamaño máximo: 512KB';

        if( $_FILES['archivo']['type']=='image/jpeg')
        {
             echo 'La extensión JPEG está permitida';

             $rand = rand(1000,999999);
             $origen = $_FILES['archivo']['tmp_name'];
             $destino = 'uploads/'.$rand.$_FILES['archivo']['name'];
             move_uploaded_file($origen, $destino);

        }
   }
}

?>

El número aleatorio rand entre 1000 y 999999 lo utilizamos para prevenir la sobreescritura de ficheros con el mismo nombre.  De esta forma ya hemos implementado un sistema de subida de archivos JPEG de tamaño menor a 512KB. Ahora bien, este sistema no es del todo seguro para evitar la subida de archivos maliciosos diseñados para atacar nuestra aplicación, como por ejemplo, una shell en php. Queda pendiente realizar una entrada para evitar esta vulnerabilidad de las aplicaciones web. Hasta ahora contais con dos artículos dedicados a las principales vulnerabilidades: evitar XSS y evitar RFI y FLI.

Descarga de ficheros

Para descargar ficheros normalmente ponemos la ruta al fichero en el enlace y el navegador ya se encarga de realizar la petición HTTP oportuna al servidor. Pero, ¿que ocurre cuando el archivo que se desea bajar se ha generado dinámicamente y aún no está guardando en ningún directorio dentro del servidor o solo queremos que se puedan descargar archivos a través de un script de PHP?  Pues que tendremos que diseñar algún método que permita forzar la descarga de archivos.

La solución es enviar la petición HTTP de respuesta mediante código PHP enviandouna serie de cabeceras HTTP mediante la función header() de PHP que solo funciona si no se ha enviado nada al flujo de salida.

Las cabeceras de la respuesta HTTP que vamos a enviar son:

– Content-Type: application/force-download

– Content-Disposition: attachment;  filename=nombre_del_fichero

– Content-Transfer-Enconding: binary

– Content-Length: tamaño_del_fichero

Accederemos al script de descarga de la siguiente manera:

<a href="descarga.php?archivo=imagen5.jpeg" >Descargar imagen</a>
<?php
//Si la variable archivo que pasamos por URL no esta 
//establecida acabamos la ejecucion del script.
if (!isset($_GET['archivo']) || empty($_GET['archvo'])) {
   exit();
}

//Utilizamos basename por seguridad, devuelve el 
//nombre del archivo eliminando cualquier ruta. 
$archivo = basename($_GET['archivo']);

$ruta = 'imagenes/'.$archivo;

if (is_file($ruta))
{
   header('Content-Type: application/force-download');
   header('Content-Disposition: attachment; filename='.$archivo);
   header('Content-Transfer-Encoding: binary');
   header('Content-Length: '.filesize($ruta));

   readfile($ruta);
}
else
   exit();
?>

Verificamos que el archivo existe, enviamos las cuatro cabeceras, y utilizamos la funcion readfile() que devuelve el contenido del archivo por el flujo de salida. Podemos mejorar mucho ese script añadiendo el content-type adecuado, y haciendo más comprobaciones de seguridad.

RESUMEN

Ya hemos visto como implementar dos sitemas sencillos de subida y descargas de fichero y como utilizar la variable super-global $_FILES[] para acceder a los datos de los ficheros subidos al servidor. Queda pendiente realizar un artículo sobre las vulnerabilidades en las subidas de archivos. Me documentaré sobre esta cuestión y más adelante intentaré redactar un artículo dedicado a este tema.

En el próximo capítulo veremos como persistir datos entre diferentes peticiones HTTP del mismo usuario.

Hasta la próxima.

Categorías: PHP Etiquetas: