<?php   
/**
* cat-cache.inc.php (Classe per gestire la cache del sistema)
*
* +----------------------------------------------------------------------+
* |                                                                      |
* | web-system 2.1 : Web Site Management System                          |
* |                                                                      |
* +----------------------------------------------------------------------+
* | Copyright (c) davcaffa@gmail.com                                     |
* +----------------------------------------------------------------------+
* | Authors: Davide Caffaratti <davcaffa@gmail.com>                      |
* |                                                                      |
* +----------------------------------------------------------------------+
*
*/ 

if (defined('Cat_Cache')) {
    return;
}
define('Cat_Cache'true);
             
// Definisco cartella default della cache
if (!defined('_CACHE_SYSTEM')) {
    
define('_CACHE_SYSTEM'PATH_CACHE.'system/');
}

/**
* @desc Classe per gestire la cache del sistema da estendere - Tutti i metodi sono statici
*
* @package Cat_Cache 
* @category Cache
* @author Davide Caffaratti <davcaffa@gmail.com>
* @version 1.1 
*/
class Cat_Cache
{   
    
/**
    * @desc Abilita/disabilita la cache
    * @var bool $cache_enable
    */
    
public static $cache_enable true
    
/**
    * @desc Path alla cartella cache
    * @var string $cache_path
    */
    
protected static $cache_path _CACHE_SYSTEM;       
    
/**
    * @desc Durata della cache in secondi. Default 3600 (1 ora)
    * @var string $cache_lifetime
    */
    
protected static $cache_lifetime 3600;   
    
/**
    * @desc Nome del gruppo della cache
    * @var string $cache_group
    */
    
protected static $cache_group 'default'
    
/**
    * @desc Prefisso del file di cache
    * @var string $cache_prefix
    */
    
protected static $cache_prefix 'cache_';  
    
/**
    * @desc Attiva/disattiva la protezione del nome gruppo
    * @var bool $cache_protect_group_name
    */
    
protected static $cache_protect_group_name true
    
/**
    * @desc Abilita/disabilita la serializzazione della cache
    * @var bool $cache_serialize
    */
    
protected static $cache_serialize true
    
/**
    * @desc Abilita/disabilita la cancellazione della vecchia cache
    * @var bool $cache_cleanup
    */
    
protected static $cache_cleanup true;
    
/**
    * @desc Modalità di pulizia della cache (possibili: 'garbage', 'web_timed')
    * @var string $cache_cleanup_mode
    */
    
protected static $cache_cleanup_mode 'web_timed'
    
/**
    * @desc Frequenza della pulizia timed. Usato solo se self::$cache_cleanup_mode == 'web_timed' default 12 ore
    * @var bool $cache_timed_cleanup_freq
    */
    
protected static $cache_timed_cleanup_freq 43200
    
/**
    * @desc Frequenza del garbage collector. Usato solo se self::$cache_cleanup_mode == 'garbage'
    * @var bool $cache_garbage_cleanup_freq
    */
    
protected static $cache_garbage_cleanup_freq 1
    
/**
    * @desc Livello errore dei messaggi in caso di errori nella cache
    * @var string $cache_write_err_level (possibili: 'warning','error','notice','none')
    */
    
protected static $cache_write_err_level 'warning';  
    
/**
    * @desc Esce e mostra errori in caso di errori non fatali nella cache
    * @var bool $cache_write_err_stop 
    */
    
protected static $cache_write_err_stop true

    
/**
    * @desc Scrive il contenuto della cache      
    *                                      
    * @param string $id      Id unico di questa cache 
    * @param string $content Contenuto della cache 
    * @param integer $lifeTime Se settato inserisce questo valore come tempo di vita del file differente da quello della classe
    */
    
protected static function write($id$content$lifeTime=null)
    {                       
        
// Se stiamo aggiornando la cache ritorno true ed esco
        
if (!self::$cache_enable) {
            return;
        }
        
        
// Serializzo contenuto se devo   
        
if (self::$cache_serialize) {
            
$content serialize($content);
        }
        
        
// Setto nome file
        
$filename self::get_fileName($id);
                          
        if ((
$fh = @fopen($filename'wb')) === false) { 
            
self::set_error('Cat_Cache::write() - Can\'t open the cache file '.$filenameself::$cache_write_err_level__LINE__);
            return 
false;
        }
        if (!@
flock($fhLOCK_EX)) { 
            
self::set_error('Cat_Cache::write() - Can\'t lock the cache file '.$filenameself::$cache_write_err_level__LINE__);
        }        
        if ((@
fwrite($fh$content)) === false) {                                         
            
self::set_error('Cat_Cache::write() - Can\'t write the cache file '.$filenameself::$cache_write_err_level__LINE__);
            return 
false;
        }
        if (!@
flock($fhLOCK_UN)) { 
            
self::set_error('Cat_Cache::write() - Can\'t unlock the cache file '.$filenameself::$cache_write_err_level__LINE__);
        }   
        @
fclose($fh);         
        
        
$life_time = ($lifeTime) ? $lifeTime self::$cache_lifetime;    
        
// Setto tempo di vita
        
touch($filenametime() + $life_time);
    }   
    
/**
    * @desc Legge il contenuto della cache 
    * 
    * @param string $id    Id unico di questa cache 
    * @return mixed  array|string|null in caso non sia settata la cache  
    */
    
protected static function read($id)
    {            
        if (!
self::$cache_enable) {
            return 
false;
        } 
                           
        
// Setto nome file
        
$filename self::get_fileName($id);
        
        if (
self::$cache_enable && is_file($filename)) {
            if (
filemtime($filename) > time()) {    
                if (!
$content file_get_contents($filename)) {
                    
self::set_error('read('.$id.') - Can\'t read the cache file '.$filenameself::$cache_write_err_level__LINE__);
                    return 
false;
                }
                else {
                    return (
self::$cache_serialize) ? unserialize($content) : $content;
                }
            }
            else {
                
self::del_file($filename);
                return 
false;            
            }
        }
        return 
false;    
    }      
    
/**
    * @desc Cancella un file della cache 
    * 
    * @param string $id    Id unico di questa cache
    * @return bool true se non ci sono problemi   
    */
    
protected static function remove($id)
    {              
        
// Setto nome file
        
$filename self::get_fileName($id);  
        
        if (
self::$cache_enable && is_file($filename)) { 
            return 
self::del_file($filename);
        }    
    }
    
/**
    * @desc Costruisce il nome del file della cache      
    *                                 
    * @param string $id    Id unico di questa cache    
    * @return string
    */
    
protected static function get_fileName($id)
    {         
        if (!
is_dir(self::$cache_path)) {   
            
self::set_error('Cat_Cache::get_fileName(); - '.self::$cache_path.' is not a valid directory !'E_USER_ERROR__LINE__);
        }          
        
$groupname = (self::$cache_protect_group_name) ? md5(self::$cache_group) : self::$cache_group;
        return 
self::$cache_path self::$cache_prefix $groupname '_' .md5($id);
    }
    
/**
    * @desc Metodo per settare opzioni nella classe 
    * 
    * @var array $options Opzioni della classe  
    * @return void
    */
    
protected static function set_options($options=null)
    {                     
        if (
is_array($options)) {            
            foreach(
$options as $key=>$value) { 
                switch(
$key) {
                    case 
'cache_path':
                        
self::$cache_path $value;
                    break;
                    case 
'cache_lifetime':
                        
self::$cache_lifetime $value;
                    break;
                    case 
'cache_group':
                        
self::$cache_group $value;
                    break;
                    case 
'cache_prefix':
                        
self::$cache_prefix $value;
                    break;
                    case 
'cache_protect_group_name':
                        
self::$cache_protect_group_name $value;
                    break;
                    case 
'cache_serialize':
                        
self::$cache_serialize $value;
                    break;
                    case 
'cache_cleanup':
                        
self::$cache_cleanup $value;
                    break;    
                    case 
'cache_cleanup_mode':
                        
self::$cache_cleanup_mode $value;
                    break;
                    case 
'cache_garbage_cleanup_freq':
                        
self::$cache_garbage_cleanup_freq $value;
                    break;
                    case 
'cache_write_err_level':
                        
self::$cache_write_err_level $value;
                    break;
                    case 
'cache_write_err_stop':
                        
self::$cache_write_err_stop $value;
                    break;
                    default:
                        
self::set_error('Cat_Cache::set_options($options); - '.$key.' is not a valid option !'E_USER_ERROR__LINE__); 
                }
            }
        }        
    }
    
/**
    * @desc Setta un errore nella classe e ritorna la risposta adeguata
    * 
    * @param string $mensage Il messaggio dell'errore
    * @param string $mode La modalità dell'errore
    * @param string $line Linea del codice in cui avviene l'errore
    */
    
protected static function set_error($mensage$mode$line) {        
        
// Scelta del modo errore
        
switch($mode) {
            case 
'none':
                return;
            break;
            case 
'error':
                
trigger_error($mensageE_USER_ERROR);
                exit;
            break;
            case 
'notice':                
                
trigger_error($mensageE_USER_NOTICE);
                if (
self::$cache_write_err_stop) {
                    exit;
                }
            break; 
            default:                 
                
trigger_error($mensageE_USER_WARNING);                 
                if (
self::$cache_write_err_stop) {
                    exit;
                }
        } 
    } 
    
/**
    * @desc Cancella un file
    *
    * @param string $file Path e nome del file 
    * @return bool true se non ci sono problemi 
    */
    
protected static function del_file($filename)
    {
        if (!@
unlink($filename)) {
            
self::set_error('Cat_Cache::del_file(\''.$filename.'\') - Unable to remove cache !'self::$cache_write_err_level__LINE__);
            return 
false;
        }
        return 
true;
    } 
    
/**
    * @desc Controlla se esiste il file di lock update della cache corrente   
    * @return bool
    */
    
protected static function is_updated($lockFilename=null
    {     
        if (!
$lockFilename) {
            
$lockFilename self::get_fileName('__cache_lock__');
        }         
        return (
file_exists($lockFilename));
    } 
    
/**
    * @desc Cancella i file scaduti della cache                                     
    * @param string $group Se presente cancella dalla cache solo il gruppo
    * @param bool $clearAll Se settato a true elimina tutti i dati dalla cache scaduti o no
    */
    
protected static function cleanup($group=false$clearAll=false)
    {            
        if (!
self::$cache_enable || !self::$cache_cleanup) {
            return;
        }
                
        
// Nome e path file di lock
        
$lockFilename self::get_fileName('__cache_lock__');
        
        
// Nome gruppo
        
if ($group && self::$cache_protect_group_name) {
            
$group md5($group);
        }
                           
        
// Prevengo doppio cleanup        
        
if (self::is_updated($lockFilename)) {             
            if (
filemtime($lockFilename) < time()) { 
                return;        
            }   
        } 
        
        
// Aggiorno scadenza file        
        
if ($group && self::$cache_cleanup_mode == 'web_timed') {
            
$fileTimed self::get_fileName('__cache_timed__');
            if (!
file_exists($fileTimed)) {     
                
self::write('__cache_timed__'''self::$cache_timed_cleanup_freq); 
            }
            
touch($fileTimedtime() + self::$cache_timed_cleanup_freq);
        }
         
        
// Setto file di blocco della durata massima di 4 minuti
        
self::write('__cache_lock__'''240);
        
        
// Handle file
        
if (!$handle = @opendir(self::$cache_path)) {
            
self::set_error('Cat_Cache::cleanup(); - can\'t open the dir '.self::$cache_pathself::$cache_write_err_level__LINE__);
            return; 
        } 
        
        while(
false !== ($file readdir($handle))) {   
            if(
$file != "." && $file != ".." && $file != basename(self::get_fileName('__cache_timed__')) && $file != 'index.html' && $file != '.htaccess') { 
                if (
is_file(self::$cache_path.$file)) {   
                                             
                    
// Cancellazione della cache del gruppo scaduta o no
                    
if ($clearAll && $group) {   
                        if (
strpos($fileself::$cache_prefix $group) !== false) {   
                            
self::del_file(self::$cache_path.$file); 
                        }
                    }
                    
// Cancellazione della cache scaduta del gruppo
                    
else if (!$clearAll && $group) {   
                        if (
strpos($fileself::$cache_prefix $group) !== false) {   
                            if (
filemtime(self::$cache_path.$file) < time()) {
                                
self::del_file(self::$cache_path.$file);
                            }
                        }
                    }                   
                    
// Cancellazione di tutta la cache presente scaduta o no
                    
else if ($clearAll && !$group) {   
                        
self::del_file(self::$cache_path.$file); 
                    }
                    
// Cancellazione della cache scaduta di qualsiasi gruppo
                    
else {
                        if (
filemtime(self::$cache_path.$file) < time()) {
                            
self::del_file(self::$cache_path.$file);
                        }                       
                    }   
                }
            }
        }
        
closedir($handle);        
        
clearstatcache(); 
        
        
// Cancello file di lock se esiste
        
if (file_exists($lockFilename)) { 
            
self::del_file($lockFilename); 
        }  
    }
    
/**
    * @desc Controlla se bisogna eseguire la pulizia della cache 
    */
    
protected static function check_cleanCache()
    {      
        if (!
self::$cache_cleanup) {
            return;
        }
            
        if (
self::$cache_cleanup_mode == 'garbage') {
            
self::clean_garbage();
        }
        else {
            
self::clean_timed();
        }
    }
    
/**
    * @desc Attiva il garbage collector della cache
    * @param integer $cleanup_freq Se settato inserisce una frequenza differente da quella della classe   
    * @return void
    */
    
private static function clean_garbage($cleanup_freq=null)
    {                
        
$cleanup_freq = ($cleanup_freq) ? $cleanup_freq self::$cache_garbage_cleanup_freq;
         
        
mt_srand((double)microtime()*1000000);
        if (
mt_rand(1,100) <= $cleanup_freq) {
            
// Cancello cache   
            
self::cleanup(); 
        }   
    }
    
/**
    * @desc Attiva la pulizia della cache una volta al giorno se devo  
    * @return void
    */
    
private static function clean_timed()
    {   
        
// Nome e path file cron
        
$timeFilename self::get_fileName('__cache_timed__');
        
        if (!
file_exists($timeFilename)) {              
            
// Setto file cron della durata di self::$cache_timed_cleanup_freq secondi
            
self::write('__cache_timed__'''self::$cache_timed_cleanup_freq);
            
touch($timeFilenametime() + self::$cache_timed_cleanup_freq);
        }
        else {
            if (
filemtime($timeFilename) < time()) { 
                
// Cancello cache usando valore random alto  
                
self::clean_garbage(100);
                
// Setto tempo di vita se abbiamo cancellato la vecchia cache
                
if (!self::$cache_enable) { 
                    
touch($timeFilenametime() + self::$cache_timed_cleanup_freq);
                } 
            }                       
        }
        
clearstatcache();  
    }
}
/**
* @desc Classe estesa per gestire la cache del sistema - Tutti i metodi sono statici
*
* @package Cat_Cache_Datas
* @uses Cat_Cache 
* @category Cache
* @author Davide Caffaratti <davcaffa@gmail.com>
* @version 1.0 

* @example:
* // Prende dati dalla cache se esistono
* $options = array(       
*            'cache_path' =>'/my-cache-path/', 
*            'cache_lifetime' =>7200,  
*            'cache_group' =>'news', 
*            'cache_prefix' =>'_my_prefix'
*            );
* if (!$content = Cat_Cache_Datas::get('unique id', $options)) {

*   $content = 'Bla bla bla';

*   Cat_Cache_Datas::save('unique id', $content, $options);
* }
* echo $content;
*                  
* // Cancella la cache di un gruppo scaduta o no
* $options = array(       
*            'cache_path' =>'/my-cache-path/'
*            );
* Cat_Cache_Datas::clean('my_group', false, $options);

* // Cancella la cache scaduta
* $options = array(       
*            'cache_path' =>'/my-cache-path/'
*            );
* Cat_Cache_Datas::clean(false, false, $options);

* // Cancella tutta la cache scaduta o no
* $options = array(       
*            'cache_path' =>'/my-cache-path/'
*            );
* Cat_Cache_Datas::clean(false, true, $options);

* // Cancella la cache con id specifico
* $options = array(       
*            'cache_path' =>'/my-cache-path/'
*            );
* Cat_Cache_Datas::del('my-cache-unique-id', $options);

* // Prende nome di un file di cache con id specifico
* $options = array(       
*            'cache_path' =>'/my-cache-path/'
*            );
* Cat_Cache_Datas::get_name('my-cache-unique-id');
*/
class Cat_Cache_Datas extends Cat_Cache

    
/**
    * @desc Prende i dati dalla cache
    * @param string $id    Id unico di questa cache       
    * @param array   $options Opzioni della cache   
    * $options = array(       
    *            'cache_path' => Path della cartella cache (string), 
    *            'cache_lifetime' => Tempo scadenza della cache (integer),  
    *            'cache_group' => Nome del gruppo della cache (string), 
    *            'cache_prefix' => Prefisso del file di cache (string), 
    *            'cache_protect_group_name' => Attiva/disattiva protezione nome gruppo (bool), 
    *            'cache_serialize' => Attiva/disattiva la serializzazione della cache (bool),
    *            'cache_cleanup' => Attiva/disattiva la cancellazione della vecchia cache (bool),
    *            'cache_cleanup_mode' => Modalità cancellazione vecchia cache (string),
    *            'cache_timed_cleanup_freq' => Frequenza pulizia cache web_based in secondi (integer),
    *            'cache_garbage_cleanup_freq' => Frequenza del garbage collector numeri da 1 a 100 (integer), 
    *            'cache_write_err_level' => Livello di errore nella scrittura di un file (string),
    *            'cache_write_err_stop' => Attiva/disattiva lo stop del programma ad ogni errore default false (bool)
    *            );     
    * @return mixed  array|string|boolean false in caso non sia settata la cache
    */
    
public static function get($id$options=null)
    {
        
// Setto opzioni
        
self::set_options($options); 
                
        
// Controlla che non stiamo effettuando la pulizia cache
        
if (!self::is_updated()) {
            
// Controlla ed esegue se deve la pulizia cache
            
self::check_cleanCache();
            
            return 
self::read($id);
        }
        return 
false;
    }    
    
/**
    * @desc Setta la cache                                        
    * @param string $id      Id unico di questa cache
    * @param mixed  $content Contenuto della cache       
    * @param array  $options Opzioni della cache  
    * $options = array(       
    *            'cache_path' => Path della cartella cache (string), 
    *            'cache_lifetime' => Tempo scadenza della cache (integer),  
    *            'cache_group' => Nome del gruppo della cache (string),
    *            'cache_prefix' => Prefisso del file di cache (string), 
    *            'cache_protect_group_name' => Attiva/disattiva protezione nome gruppo (bool),
    *            'cache_serialize' => Attiva/disattiva la serializzazione della cache (bool),
    *            'cache_cleanup' => Attiva/disattiva la cancellazione della vecchia cache (bool),  
    *            'cache_cleanup_mode' => Modalità cancellazione vecchia cache (string),
    *            'cache_timed_cleanup_freq' => Frequenza pulizia cache web_based in secondi (integer),
    *            'cache_garbage_cleanup_freq' => Frequenza del garbage collector numeri da 1 a 100 (integer), 
    *            'cache_write_err_level' => Livello di errore nella scrittura di un file (string),
    *            'cache_write_err_stop' => Attiva/disattiva lo stop del programma ad ogni errore default false (bool)
    *            );                        
    */
    
public static function save($id$content$options=null)
    {
        
// Setto opzioni
        
self::set_options($options);
                              
        
// Controlla che non stiamo effettuando la pulizia cache
        
if (!self::is_updated()) {
            
self::write($id$content);
        }
    }    
    
/**
    * @desc Cancella i file scaduti della cache 
    * @param string $group Se presente cancella dalla cache solo il gruppo        
    * @param bool $clearAll Se settato a true elimina tutti i dati dalla cache scaduti o no 
    * @param array  $options Opzioni della cache  
    * $options = array(       
    *            'cache_path' => Path della cartella cache (string), 
    *            'cache_lifetime' => Tempo scadenza della cache (integer),
    *            'cache_prefix' => Prefisso del file di cache (string), 
    *            'cache_protect_group_name' => Attiva/disattiva protezione nome gruppo (bool), 
    *            'cache_write_err_level' => Livello di errore nella scrittura di un file (string),
    *            'cache_write_err_stop' => Attiva/disattiva lo stop del programma ad ogni errore default false (bool)
    *            );     
    */
    
public static function clean($group=false$clearAll=false$options=null
    {
        
// Setto opzioni
        
self::set_options($options);
                     
        
// Controlla che non stiamo effettuando la pulizia cache
        
if (!self::is_updated()) {   
            
self::cleanup($group$clearAll);
        }
    }    
    
/**
    * @desc Cancella un file dalla cache 
    * @param string $id Id unico della cache        
    * @param array  $options Opzioni della cache  
    * $options = array(       
    *            'cache_path' => Path della cartella cache (string),      
    *            'cache_group' => Nome del gruppo della cache (string),
    *            'cache_prefix' => Prefisso del file di cache (string),  
    *            'cache_write_err_level' => Livello di errore nella scrittura di un file (string),
    *            'cache_write_err_stop' => Attiva/disattiva lo stop del programma ad ogni errore default false (bool)
    *            );     
    */
    
public static function del($id$options=null
    {
        
// Setto opzioni
        
self::set_options($options); 
                     
        
// Controlla che non stiamo effettuando la pulizia cache
        
if (!self::is_updated()) {
            
self::remove($id);
        }
    }
    
/**
    * @desc Ritorna il nome del file della cache      
    *                                 
    * @param string $id    Id unico di questa cache       
    * @param array  $options Opzioni della cache  
    * $options = array(       
    *            'cache_path' => Path della cartella cache (string),      
    *            'cache_group' => Nome del gruppo della cache (string),
    *            'cache_prefix' => Prefisso del file di cache (string)
    *            );        
    * @return string
    */
    
protected static function get_name($id$options=null)
    {  
        
// Setto opzioni
        
self::set_options($options);       
        
        return 
self::get_fileName($id);
    }
}
?>