<?php
/*--------------------------------------------------------------------------------
Application: phpSuperCache
==========================
Archivo: cache.class.php
Autor: Francisco Echarte [patxi@eslomas.com]
Fecha: 2001-08-23
Version: 0.2
Clases:
cacheManager => guarda en cache y recupera, páginas enteras o bloques
LICENCIA
========
copyright (c) 2001 Francisco Echarte [patxi@eslomas.com]
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
version 2.1 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details at
http://www.gnu.org/copyleft/lgpl.html
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Modificaciones:
.- Se añade el manejo de CACHE_QUERYS para decidir mediante expresiones
regulares que URIS se cachean
Observaciones:
.- Desarrollado tomando como base toncarta, de heyes-computing.net
.- Utiliza únicamente el filesystem, por se lo más rápido. No se usa una BD,
con el fin de evitar el cuello de botella que implica la solicitud continua
de conexiones.
.- Las páginas son almacenadas en texto plano y en comprimido (gzip), con el fin
de ofrecer el contenido de la cache comprimido, si el navegador del usuario
lo soporta.
--------------------------------------------------------------------------------*/
include_once(dirname(__FILE__).'/cache.config.php');
include_once(dirname(__FILE__).'/cache.timings.php');
class cacheManager{
var $request_uri; //Para guardar solicitdud del usuario
var $query_string; //Para guardar la query solicitada
var $php_self; //Para guardar página solicitada
var $gzip_supported; //Para indicar si en la petición, el navegador del
//usuario soporta información en formato gzip
var $genereting_output; //Para indicar cuando se está generando output para
//guardar en cache
/*---------------------------------------------------------------------------
Entrada:
Salida :
Efecto : Construtcr de la clase, inicializa las variables. Ejecuta la autolimpieza
con cierta probabilidad, si la opción está activada
---------------------------------------------------------------------------*/
function cacheManager(){
global $CACHE_CONFIG;
$this->gzip_supported = 0;
$this->genereting_output = 0;
$this->request_uri = $GLOBALS[REQUEST_URI];
$this->query_string = $GLOBALS[QUERY_STRING];
$this->php_self = basename($GLOBALS[PHP_SELF]);
$CACHE_CONFIG['run_cleanup'] == 1? $this->cacheCleanup() : NULL;
// Miro si el navegador del usuario permite envío de información comprimida
if(strpos(" ".$GLOBALS[HTTP_ACCEPT_ENCODING],'gzip') !== 0)
$this->gzip_supported = 1;
else
$this->gzip_supported = 0;
}
/*---------------------------------------------------------------------------
Entrada:
Salida : Si la página existe en cache, devuelve el contenido y finaliza el script.
Efecto : Si la página solicitada por el usuario está entre las que hay
que cachear, comprueba si hay una copia disponible y la envía,
finalizándose la ejecución. Si no hay una copia disponible, se
inicializa el output buffer, para poder luego capturar la salida
generada por el script.
---------------------------------------------------------------------------*/
function startCachingPage(){
global $CACHE_TIMINGS, $CACHE_CONFIG, $CACHE_QUERYS;
if($CACHE_TIMINGS[$this->php_self] > 0
&& ($this->query_string == ''
|| ereg($CACHE_QUERYS[$this->php_self],$this->query_string) ) ){
//
// Está la página cacheada ?
//
list($cached_page,$gzip) = $this->checkCache($this->request_uri,$CACHE_TIMINGS[$this->php_self]);
if($cached_page != ''){
// Compressed?
if($CACHE_CONFIG['return_compressed'] == 1 AND $gzip== 1){
header ("Content-Encoding: gzip");
}
echo $cached_page;
$this->logVisit($this->request_uri, 'HIT', $gzip);
exit;
}
else{
ob_start();
$this->genereting_output = 1;
$this->logVisit($this->request_uri, 'MISS', $gzip);
}
}
else{
$this->logVisit($this->request_uri, 'EXCL', $gzip);
}
}
/*---------------------------------------------------------------------------
Entrada:
Salida :
Efecto : Si la página solicitada por el usuario está entre las que hay
que cachear, comprueba si hay una copia disponible y la envía,
finalizándose la ejecución. Si no hay una copia disponible, se
inicializa el output buffer, para poder luego capturar la salida
generada por el script.
---------------------------------------------------------------------------*/
function endCachingPage(){
global $CACHE_CONFIG;
if($this->genereting_output == 1){
$output = ob_get_contents();
ob_end_clean();
$this->insertIntoCache($output, $this->request_uri);
}
}
/*----------------------------------------------------------------------
Entrada: $request => indica la página (uri) o bloque que se quiere comprobar
$refresh => indica el tiempo de permanencia en cache de la solicitud
$compressed => indica si se prefiere salida comprimida. Si la
solicitud es para un bloque debe ser false!!!
Salida : Si se consigue obtener (HIT) la solicitud de cache, se devuelve
un array con el contenido y true o false indicando si
está comprimido o no. Si no se puede realizar la solicitud (MISS)
se devuelve un array con FALSE y false como comprimido
Efecto : a partir de la solicitud y compressed, con md5 se crea el nombre de
que tendría en cache. Si el archivo existe se compara su fecha con el
tiempo de permanencia. En el caso de que se solicite comprimido
y este no exista, se intenta obtener la versión sin comprimir.
----------------------------------------------------------------------*/
function checkCache($request,$refresh,$compressed = '-1'){
global $CACHE_CONFIG;
$filename = $CACHE_CONFIG['data_dir'].md5($request);
if($compressed == '-1') $compressed = $this->gzip_supported;
if($CACHE_CONFIG['return_compressed'] == 1 && $compressed == 1)
$filename .= ".gz";
if(file_exists($filename)){
if(filemtime($filename) > time()-$refresh){
$data = fread($fp = fopen($filename, 'r'), filesize($filename));
fclose($fp);
return array($data,$compressed);
}
else
return array(0,1);
}
else if($compressed == 1)
$this->checkCache($request,$refresh,0);
else
return array(0,0);
}
/*----------------------------------------------------------------------
Entrada: $content => el contenido
$request => la solicitud
Salida : se imprime el contenido que se ha metido en cache
Efecto : se crea un archivo en el filesystem en base al nombre de la solicitud
codificándolo con md5. Además si está habilitada la opción de
guardar comprimido, se guarda el mismo archivo con extensión gz. Esto
se puede realizar exista o no la función gzcompress, utilizando una
llamada al shell para gzip.
----------------------------------------------------------------------*/
function insertIntoCache($content, $request){
global $CACHE_CONFIG;
$content .= "\n<!-- CACHED AT: " . date('Y/m/d : H:m:s') . " BY phpSuperCache -->\n";
$nombre = $CACHE_CONFIG['data_dir'].md5($request);
if($fp = fopen($nombre, 'w')){
flock($fp, LOCK_EX);
fwrite($fp,$content);
flock($fp, LOCK_UN);
fclose($fp);
if($CACHE_CONFIG['compress_output'] == 1){
if(function_exists('gzcompress')){
$gzcontent = "\x1f\x8b\x08\x00\x00\x00\x00\x00";
$Size = strlen($content);
$Crc = crc32($content);
$aux = gzcompress($content,$level);
$gzcontent .= substr($aux, 0, strlen($aux) - 4);
$gzcontent .= pack('V',$Crc);
$gzcontent .= pack('V',$Size);
$fp = fopen("$nombre.gz",'w');
flock($fp, LOCK_EX);
fwrite($fp,$gzcontent);
flock($fp, LOCK_UN);
fclose($fp);
}
else{
exec("cp $nombre $nombre.bak;gzip $nombre.bak;mv $nombre.bak.gz $nombre.gz");
}
}
}
echo $content;
}
/*----------------------------------------------------------------------
Entrada: $request => indica la solicitud realizada
$type => el resultado de la solicitud (HIT, MISS, EXCL)
$compressed => si se ha devuelto el resultado comprimido o no
Salida : nada
Efecto : añade una línea al log, si la opción esta activa.
----------------------------------------------------------------------*/
function logVisit($request, $type, $compressed){
global $CACHE_CONFIG;
if(!$CACHE_CONFIG['save_stats']) return;
if($compressed == 1) $compressed = 'true';
else $compressed = 'false';
$logfile = $CACHE_CONFIG['data_dir'].'stats.log';
$fp = fopen($logfile, 'a');
flock($fp, LOCK_EX);
fseek($fp, filesize($logfile));
$salida = sprintf("%-10s %-74s %-4s %-5s\r\n",time(),$request,$type,$compressed);
//fwrite($fp, time().' '.urlencode($request).' '.$type.' '.$compressed."\r\n");
fwrite($fp, $salida);
flock($fp, LOCK_UN);
fclose($fp);
}
/*----------------------------------------------------------------------
Entrada: nada
Salida : nada
Efecto : elimina los archivos del filesystem correspondientes a cachings viejos
----------------------------------------------------------------------*/
function cacheCleanup(){
global $CACHE_CONFIG;
srand((double)microtime()*1000000);
$num = rand(1,100);
if($num <= $CACHE_CONFIG['cleanup_freq']){
$dh = opendir($CACHE_CONFIG['data_dir']);
while($filename = readdir($dh)){
if($filename === '.' OR $filename === '..') continue;
if(filemtime($CACHE_CONFIG['data_dir'].$filename) < time() - $CACHE_CONFIG['max_age'])
unlink($CACHE_CONFIG['data_dir'].$filename);
}
}
}
/*----------------------------------------------------------------------
Entrada: un string correspondiente a un nombre de bloque
Salida : false si el bloque no está en cache y true si lo está. Además en caso
de que esté en cache, se muestra.
----------------------------------------------------------------------*/
function getCachedBlock($block){
global $CACHE_TIMINGS;
list($cached_contents,$gzip) = $this->checkCache($block,$CACHE_TIMINGS[$block],0);
if($cached_contents){
echo $cached_contents;
$this->logVisit($block, 'HIT', $gzip);
return true;
}
else
return false;
}
/*----------------------------------------------------------------------
Entrada: $block => nombre de bloque
$contents => el contenido del bloque
Salida : imprime el contenido
Efecto : guarda el contenido del bloque en cache y lo imprime. Si el bloque
no tiene asignado un tiempo de permanencia, se muestra un error.
----------------------------------------------------------------------*/
function setCachedBlock($block,$contents){
global $CACHE_TIMINGS;
if(!isset($CACHE_TIMINGS[$block])){
$this->logVisit($block, 'EXCL', 0);
return;
}
else{
$this->insertIntoCache($contents,$block);
$this->logVisit($block, 'MISS', 0);
}
}
}
?> |