Sistema de Cache con PHP

baluart16 Marzo 2007 - 3:06am 24 comentarios
Enviar por Email Imprimir

Sea por el incremento de visitantes ó por el uso de scripts complejos, la ejecución de constantes consultas a la Base de Datos MySQL suele volver lento un servidor, con el consiguiente retraso a la hora de mostrar una página web.

Una de las mejores alternativas para solucionar este problema, es implementar un sistema de Cache con PHP. Con éste, en vez de realizar todas las consultas a la base datos para servir una página, simplemente se requerirá un archivo de texto. El resultado, es un enorme ahorro de recursos y una más rápida respuesta del servidor.

Este es el primero de una serie de artículos donde comentaremos como implementar una Cache a nuestros scripts PHP. En este mini tutorial veremos la creación de una sencillísima clase PHP, pero lo suficientemente flexible cómo para poder adaptarla a nuestros proyectos y personalizarla conforme a nuestras necesidades.

Las funciones básicas

Pero antes de ir a la parte práctica y ver el código fuente, vamos a dar una somera mirada a las tres funciones claves que vamos a utilizar en el script:

fileatime(): Con esta función obtendremos el momento exacto (hora del servidor) en que el fichero de la cache fue creado. Este dato, nos servirá para verificar si la cache ha expirado.

ob_start(): Esta función es la principal, e irremplazable, de todas. Con ella indicamos a PHP que almacene en un buffer interno toda la información de salida que encuentre. Es decir, no se mostrará nada en el navegador, todo el texto (html) quedará almacenado en el buffer.

ob_get_clean(): Esta función complementa a ob_start() y nos permite reducir algunas líneas de código. Por lo general, para crear una cache se suelen utilizar las funciones ob_get_contents() para recuperar y mostrar la información guardada en el buffer interno y, luego, ob_end_clean() para limpiar el buffer. Bueno, con ob_get_clean() logramos el mismo resultado que utlizando las dos anteriores.

Conociendo las funciones claves para crear nuestro script, ahora veremos su proceso de creación.

Los Atributos Necesarios

Nuestra clase va necesitar cinco atributos (variables claves). Cada uno de ellos contendrá información clave para que nuestra clase funcione correctamente.

$cache_dir, para indicar el path ó ruta donde se almacena la cache
$cache_time, para indicar el tiempo en que expira la cache (en segundos)
$caching, si es true indica que se guarde la cache. False por defecto.
$cleaning, si es true indica que se limpie la cache, para actualizarla luego. False por defecto.
$file, para indicar el path o ruta del script a cachear

Conociendo las funciones básicas y los atributos necesarios, procedemos a la creación de nuestra clase.

La Clase PHP: Cache

Nuestra clase tendrá solamente dos métodos, con el primero indicaremos que se inicie la ejecución de la cache y con el segundo, que finalice. Los métodos son:

function iniciar(): Este método require 2 parámetros obligatorios y 1 opcional. Los dos parámetros obligatorios son:

$path, que transmite la ruta al directorio de cache a $cache_dir; y
$time, que transmite el tiempo máximo de vigencia de la cache a $cache_time.

El parámetro opcional ($action), es muy útil para sitios que se actualizan constantemente. Por ejemplo, una página que muestra noticias, debe actualizarse cada vez que se envíe una noticia nueva, ó la página de un artículo debe actualizarse cada vez que se envíe un comentario nuevo. Es decir, cada vez que se ejecute determinada acción, debe actualizarse la cache. Para lograr esto, podemos utilizar el siguiente parámetro:

$action, que transmite si la cache debe actualizarse a $cleaning. Si se ha ejecutado determinada acción, $action es true. Por defecto es false.

Una vez recuperados los parámetros, el método iniciar() verificará si se cumple una condición. Si la respuesta es sí, solamente se lee la cache. Pero, si la respuesta es no, almacenamos la información en el buffer y volvemos al parámetro $caching como true (false por defecto).

Para reducir volumen a nuestra clase, y hacerla más sencilla, para la escritura de la cache utilizaremos la función readfile(), en reemplazo de las conocidas funciones fopen(), fread() y fclose(), aunque utilizar estas lo hace más flexible (en el códio aparecen entrecomillados).

El segundo método que utilizaremos es:

function cerrar(): Este metodo verifica si $caching es true. Si la respuesta es afirmativa, recupera la información guardada en el buffer, la muestra, y escribe (crea ó actualiza) el fichero de la cache.

Para no alargar el mini tutorial, y habiendo visto todos los detalles de la clase. Veamos el código (comentado).

<?php
/*
* Clase PHP - Cache
* www.baluart.net
*/

class cache
{
var $cache_dir; // path ó ruta donde se almacena la cache
var $cache_time; // tiempo en que expira la cache (en segundos)
var $caching = false; //true, para cachear
var $cleaning = false; //true, para limpiar y actualizar
var $file = ''; // path o ruta del script a cachear

function iniciar($path='',$time,$action=NULL){

global $_SERVER;

$this->cache_dir = $path;
$this->cache_time = $time;
$this->cleaning = $action;
$this->file = $this->cache_dir."cache_".md5(urlencode($_SERVER['REQUEST_URI'])); //md5, encriptado por seguridad

//condicional: Existencia del archivo, fecha expiración, acción
if (file_exists($this->file) && (fileatime($this->file)+$this->cache_time)>time() && $this->cleaning == false){

readfile($this->file);
/*
// abrimos el fichero
$handle = fopen( $this->file , "r");
do {
//leemos hasta 8192 bytes por defecto (podemos incrementarlo)
$data = fread($handle, 8192);
if (strlen($data) == 0) {
break;
}
//mostramos la cache
echo $data;
} while (true);
fclose($handle);
*/
exit();
} else {
$this->caching = true;
//grabamos buffer
ob_start();
}
}

function cerrar(){
if ($this->caching){
//Recuperamos información del buffer
$data = ob_get_clean();
// mostramos información
echo $data;
//borramos cache si existe
if(file_exists($this->file)){
unlink($this->file);
}
//escribimos información en cache
$fp = fopen( $this->file , 'w' );
fwrite ( $fp , $data );
fclose ( $fp );
}
}

} // Fin clase Cache
?>

Cache con PHP: Puesta en Marcha

Finalmente podemos utilizar nuestro script para cachear cualquier página PHP. A manera de ejemplo, ahora vamos a cachear sólo un trozo de código PHP y otro trozo no, para observar su funcionamiento.

<?php

//Creamos instancia
$cache1 = new cache();
//Sin cache
echo "Sin cache: ". date("D M j G:i:s T Y") . "<br>";
//Con cache por sesenta segundos.
$cache1->iniciar('tmp/',60,false);
echo "Con cache: ". date("D M j G:i:s T Y");
$cache1->cerrar();
//Cerramos cache

?>

Sin embargo, debemos tener cuidado de que la cache almacene todo el código html que se va mostrar en la página. En caso contrario, a la hora de utiliar la cache, el texto posterior a la cache no será mostrado. Esto se debe a que utilizamos la función exit() para terminar la ejecución del script.

Conclusiones y Ejemplo Final

Rememorando lo dicho en el tutorial, con el primer parámetro indicamos el directorio donde almacenaremos la cache, con el segundo indicamos el tiempo de vida de la cache (en este caso 60 segundos) y con el tercer parámetro (opcional) indicamos si la cache debe actualizarse o no, si se cumple determinada condición (si se ha publicado un nuevo artículo, si se ha actualizado el artículo, si se ha escrito un nuevo comentario, etc.).

Finalmente, podemos ver un ejemplo en funcionamiento desde el siguiente enlace.

Ejemplo | Cache con PHP

Pdta. Si desean probarlo en su PC, pueden copiar de manera literal el código de la clase y del bloque en un mismo fichero PHP. Pero, si van a utilizarlo dentro otros ficheros PHP, la clase debe ir en un fichero externo y la llaman mediante la función include(). Por ejemplo:

<?php
include('clases/cache.class.php');
?>

Donde cache.class.php es el nombre del fichero que contiene la clase.

Cómo comentamos lineas arriba, la necesidad de cachear todo el html, es decir, la imposibilidad de cachear únicamente un bloque o bloques de scripts de toda un página, es es la principal limitación de esta clase. Dado el caso que necesitemos hacer sólo esto, en el siguiente tutorial sobre Sistemas de Cache con PHP, daremos una alternativa de solución a esta limitación con una clase mucho más flexible, aunque también compleja.

Bueno, eso es todo. Espero que el mini tutorial les haya sido de ayuda.

Comentarios

Imagen de MaXaC
MaXaC

Muy interesante, espero ponerlo en practica proximamente, gracias!

Imagen de pablo
pablo

Muy interesante esto.Gracias ! 

Imagen de clinisbut
clinisbut

Creo que tienes un fallo en la clase.Porque usas fileatime (ultima fecha de ACCESO) y no filemtime (ultima fecha de modificacion)? 

Imagen de Edo
Edo

Excelente! Lo probe y mejor

Imagen de TaLu

Hola:Muchas gracias por compartir esta clase, me ha sido de mucha utilidad.Yo tambien le modifique el  fileatime por filemtime, le elimine la parte que borra el archivo (porque no entendi para que borrarlo si con 'w' en el fwrite se elimina el contenido anterior), le cambie el MD5 por SHA1 y le puse un .htaccess al directorio del cache para que no pueda ser accedido desde un navegador:Options -Indexes#deny all accessdeny from all Espero que sirva el pequeño aporte. Saludos y gracias nuevamente. 

Imagen de Pepe
Imagen de Ricardo
Ricardo

Hola, una consulta es posible cachear imagenes extraidas de una columna blob de mysql. gracias

Imagen de Ricardo
Ricardo

Hola, una consulta es posible cachear imagenes extraidas de una columna blob de mysql. gracias

Imagen de rock

Hola, implente esta clase para crear cache, gracias por la explicación.

Una consulta: como dicen mas arriba sobre usar fileatime o filemtime ¿cuál sería mejor?

Imagen de Anonymous
Anonymous

Hola, he implementado el sistemita de cache, esta bastante bueno pero los archivos de cache que te genera en la carpeta tmp, no se borran, como puedo solucionar esto sin afectar el proceso de cache? ya que estos archivos no se eliminan después de usarlos y generan mucho espacio en disco.

Imagen de Nod Kopfnickend

Muy bueno, lastima que necesito un sistema de cachado parcial de codigo y no uno completo, pero tu Class me sirve para crear el que yo necesito.

Gracias!!

Imagen de Víctor Barbero

Excepcional!: fácil de entender y de utilizar. Gracias por el código!

Imagen de J.v.C

Interesante sistema de cache, lo pondré en practica en mi página.

Saludos!

Imagen de Anonymous
Anonymous

Hola Fantastico tuto, ya se lo gregue a mi portal php y funciona muy bien.

Imagen de Daniel Martín

Esta bueno para implementarlo en páginas sencillas con un sistema de chacheo a medida, bueno para no usar el SMARTY.

Imagen de orlando_men
orlando_men

interesante este post me servira de mucho,
quiero saber si ya salio el otro post para cachear solo partes, como por ejemplo consultas en mysql
saludos

Imagen de Zenzuki
Zenzuki

Ademas, este ejemplo no funciona, igual debes ejecutar el php para guardarlo, estas haciendo doble trabajo...

Imagen de baluart
baluart

Sólo se guarda 1 vez, luego tu decides el tiempo de expiración de la cache.

Imagen de MrBram

El sistema está muy bien, el problema que veo es que en poco tiempo se llenará la carpeta /tmp, ¿hay alguna manera que se borre automáticamente?

Imagen de MrBram

Ya tengo el script para borrar el directorio temp cada 4 minutos (configurable según necesidades). Solo hay que añadirlo al php class cache y listo

<?php
// Borrar archivos
$dir = opendir('tmp/');
while($f = readdir($dir))
{
if((time()-filemtime('tmp/'.$f) > 240) and !(is_dir('tmp/'.$f)))
unlink('tmp/'.$f);
}
closedir($dir);
?>

Imagen de marcosllll
marcosllll

Hola el sistema muy bien lo que no entiendo es donde colocar el //Borrar archivos? deberia ir alguna especie de destructor...?

Imagen de mamonga

Hola, muchas gracias por la clase, me ha sido de gran ayuda.
El único pero es el tema del borrado de los archivos generados en el directorio /tmp, he probado a usar el codigo de MrBram pero no me ha funcionado (no se si no lo he usado bien o que) Simplemente he copiado el codigo abajo del todo de la clase, pero ha dejado de funcionar, asi que lo he quitado.

De todos modos muchas gracias !!

Imagen de Joefay
Joefay

Para lo que quieren borrar los archivos de cache generados, pueden mantenerlos en la cache hasta que la infromacion de su base de datos cambie, cuando realicen una modificacion a su bd entonces borran el cache de esa consulta desde php en su directorio de cache para que cuando el script se ejecute de nuevo genere un nuevo archivo de cache con la nueva informacion fresca.

Imagen de Tender
Tender

Bueno, el sistema es muy bueno, pero encontre un error, el error es que me guardo en varias paginas 0kb?

Entonces si guarda 0kb muestra pagina en blanco, lo que me gustaria es evitar eso, ayuda amigos por favor, Gracias espero su pronta respuesta.

Tutoriales

Cómo descargar videos de VK.com
En este artículo voy a explicar como descargar videos y películas...
Descargar Facebook Móvil Gratis
Por si aún no lo han hecho, es posible descargar Facebook Móvil...
Cómo generar tráfico web con las redes sociales - Paso a Paso
Muchas empresas están publicando contenidos como la forma de crear...

Artículo Recomendado

3 Tips cruciales para recuperar archivos eliminados
¿Te imaginas perder el trabajo de toda una semana en tan solo unos segundos? Todos hemos pasado por este problema. Quizás eliminamos por error un archivo importante o lo borramos sin pensar que era valioso para otro... más