<?php

include_once dirname(__FILE__)."/Lite.php";

class Cache_Lab extends Cache_Lite
{
   
    /**
    * Disable / Tune the automatic cleaning process
    *
    * The automatic cleaning process destroy too old (for the given life time)
    * cache files when a new cache file is written.
    * 0               => no automatic cache cleaning
    * 1               => systematic cache cleaning
    * x (integer) > 1 => automatic cleaning randomly 1 times on x cache write
    *
    * @var int $_automaticCleaning
    */
    var $_automaticCleaningFactor = 1000;
    var $host_id=null;
    protected static $instance;
	
    static function i()
    {
	    return self::instance();
    }
    
    static function instance()
    {
	    if (self::$instance === null)
	    {
		    self::$instance = new self;
	    }
	    return self::$instance;
    }
    
    private function __clone() {}
	
    function __construct()
    {
		parent::Cache_Lite(array('cacheDir' => LAB_PATH_DATA.'/cache/', 'automaticSerialization' => true));
		$this->host_id=LabConfig::$cfg['host_id'];
    }
    
    protected function _getKey($key)
    {
	    if ($this->host_id===null) return $key;
	    else return $this->host_id.'_'.$key;
    }
	
    /**
    * Test if a cache is available and (if yes) return it
    *
    * @param string $id cache id
    * @param string $group name of the cache group
    * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested
    * @return string data of the cache (else : false)
    * @access public
    */
    function get($id, $group = 'default', $doNotTestCacheValidity = false)
    {
        $this->_id = $this->_getKey($id);
        $this->_group = $group;
        $data = false;
        if ($this->_caching) {
            $this->_setRefreshTime();
            $this->_setFileName($this->_getKey($id), $group);
            clearstatcache();
            if ($this->_memoryCaching) {
                if (isset($this->_memoryCachingArray[$this->_file])) {
                    if ($this->_automaticSerialization) {
                        return unserialize($this->_memoryCachingArray[$this->_file]);
                    }
                    return $this->_memoryCachingArray[$this->_file];
                }
                if ($this->_onlyMemoryCaching) {
                    return false;
                }                
            }
            if (($doNotTestCacheValidity) || (is_null($this->_refreshTime))) {
                if (file_exists($this->_file)) {
                    $data = $this->_read();
                }
            } else {
				//проверяем не утратил ли файл актуальность (он был сохранен будущим числом)
                if ((file_exists($this->_file)) && (@filemtime($this->_file) > time())) {
                    $data = $this->_read();
                }
            }
            if (($data) and ($this->_memoryCaching)) {
                $this->_memoryCacheAdd($data);
            }
            if (($this->_automaticSerialization) and (is_string($data))) {
                $data = unserialize($data);
            }
            return $data;
        }
        return false;
    }
    
   
    /**
    * Save some data in a cache file
    *
    * @param string $data data to put in cache (can be another type than strings if automaticSerialization is on)
    * @param string $id cache id
    * @param string $group name of the cache group
    * @return boolean true if no problem (else : false or a PEAR_Error object)
    * @access public
    */
    function set($id, $data, $ttl=NULL, $group = 'default')
    {
		if ($ttl==-1) $ttl=60*60*24*365; //вместо "вечности" кэшируем на год
        if ($this->_caching) {
            if ($this->_automaticSerialization) {
                $data = serialize($data);
            }
            if (isset($id)) {
                $this->_setFileName($this->_getKey($id), $group);
            }
            if ($this->_memoryCaching) {
                $this->_memoryCacheAdd($data);
                if ($this->_onlyMemoryCaching) {
                    return true;
                }
            }
            if ($this->_automaticCleaningFactor>0 && ($this->_automaticCleaningFactor==1 || mt_rand(1, $this->_automaticCleaningFactor)==1)) {
				$this->clean(false, 'old');			
			}
            if ($this->_writeControl) {
                $res = $this->_writeAndControl($data);
                if (is_bool($res)) {
                    if ($res) {
						//ставим файлу дату, когда он должен утратить актуальность
						@touch($this->_file, time()+$ttl);
                        return true;  
                    }
                    // if $res if false, we need to invalidate the cache
                    @touch($this->_file, time() - 2*abs($this->_lifeTime));
                    return false;
                }            
            } else {
                $res = $this->_write($data);
				@touch($this->_file, time()+$ttl);
            }
            if (is_object($res)) {
                // $res is a PEAR_Error object 
                if (!($this->_errorHandlingAPIBreak)) {   
                    return false; // we return false (old API)
                }
            }
            return $res;
        }
        return false;
    }
	
    /**
    * Remove a cache file
    *
    * @param string $id cache id
    * @param string $group name of the cache group
    * @param boolean $checkbeforeunlink check if file exists before removing it
    * @return boolean true if no problem
    * @access public
    */
    function remove($id, $group = 'default', $checkbeforeunlink = false)
    {
	return parent::remove($this->_getKey($id), $group, $checkbeforeunlink);
    }
    
    /**
    * Recursive function for cleaning cache file in the given directory
    *
    * @param string $dir directory complete path (with a trailing slash)
    * @param string $group name of the cache group
    * @param string $mode flush cache mode : 'old', 'ingroup', 'notingroup',
                                             'callback_myFunction'
    * @return boolean true if no problem
    * @access private
    */
    function _cleanDir($dir, $group = false, $mode = 'ingroup')     
    {
        if ($this->_fileNameProtection) {
            $motif = ($group) ? 'cache_'.md5($group).'_' : 'cache_';
        } else {
            $motif = ($group) ? 'cache_'.$group.'_' : 'cache_';
        }
        if ($this->_memoryCaching) {
	    foreach($this->_memoryCachingArray as $key => $v) {
                if (strpos($key, $motif) !== false) {
                    unset($this->_memoryCachingArray[$key]);
                    $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1;
                }
            }
            if ($this->_onlyMemoryCaching) {
                return true;
            }
        }
        if (!($dh = opendir($dir))) {
            return $this->raiseError('Cache_Lite : Unable to open cache directory !', -4);
        }
        $result = true;
        while ($file = readdir($dh)) {
            if (($file != '.') && ($file != '..')) {
                if (substr($file, 0, 6)=='cache_') {
                    $file2 = $dir . $file;
                    if (is_file($file2)) {
                        switch (substr($mode, 0, 9)) {
                            case 'old':
                                // files older than lifeTime get deleted from cache
                                if (!is_null($this->_lifeTime)) {
                                    if (time() > @filemtime($file2)) {
                                        $result = ($result and ($this->_unlink($file2)));
                                    }
                                }
                                break;
                            case 'notingrou':
                                if (strpos($file2, $motif) === false) {
                                    $result = ($result and ($this->_unlink($file2)));
                                }
                                break;
                            case 'callback_':
                                $func = substr($mode, 9, strlen($mode) - 9);
                                if ($func($file2, $group)) {
                                    $result = ($result and ($this->_unlink($file2)));
                                }
                                break;
                            case 'ingroup':
                            default:
                                if (strpos($file2, $motif) !== false) {
                                    $result = ($result and ($this->_unlink($file2)));
                                }
                                break;
                        }
                    }
                    if ((is_dir($file2)) and ($this->_hashedDirectoryLevel>0)) {
                        $result = ($result and ($this->_cleanDir($file2 . '/', $group, $mode)));
                    }
                }
            }
        }
        return $result;
    }
} 

