Last days I’m working making many changes in Open Classifieds and one of them is this new class to handle the cache.
Few days a go I wrote about a cache class and longer a go about application variables for php. This is a mix of both in just one powerful class.
Explanation:
In this class we have 2 different kinds of cache.
First the normal file cache, where we store values in a single file. This is good for example to cache an entire page.
Second we have an APPLICATION kind integrated in the cache. This means that whatever you store in the APP cache it would be kept in the same file as all the other APP. Really useful to store small amount of data, for example menus, counters etc… Remember that this file is loaded everytime yo create a new object fileCache, you need to be careful to not store many things on it.
The latest version of the class chooses which is the best option of storage engine, based on the size of the variable. Then for example an entire page HTML will be in a single file, and a number or a person name would be in the APP cache.
Usage:
$cache = fileCache::GetInstance(30,'cache/');//creating new instance singleton
$cache->setDebug(true);//this runs the debug and in destruct prints it
//normal usage
$cache->cache('test','test values for the var!!!!');
echo $cache->cache('test');//get
//overloads
$cache->anyvar='tes asd asd aasdasdasda sada ast';//overload of set and get methods
echo $cache->anyvar;//get overload
var_dump(isset($cache->anyvar));//isset and unset are overloaded
unset($cache->anyvar);
//clear
$cache->deleteCache();//deletes all the cache
$cache->deleteCache(60);//deletes any cache older than X seconds
Updated 17 October 2010
The Class: (Download)
start_time=microtime(true);//time starts
$this->cache_expire=$exp_time;
if ( ! is_writable($path) ) trigger_error('Path not writable:'.$path);
else $this->cache_path=$path;
$this->APP_start();//starting application cache
}
public function __destruct() {
$this->addLog('destruct');
$this->APP_write();//on destruct we write if needed
$this->returnDebug();
}
// Prevent users to clone the instance
public function __clone(){
trigger_error('Clone is not allowed.', E_USER_ERROR);
}
//deletes cache from folder
public function deleteCache($older_than=''){
$this->addLog('delete cache');
if (!is_numeric($older_than)) $older_than=$this->cache_expire;
$files = scandir($this->cache_path);
foreach($files as $file){
if (strlen($file)>2 && time() > (filemtime($this->cache_path.$file) + $older_than) ) {
unlink($this->cache_path.$file);//echo "
-".$file;
$this->addLog('delete cache file:'.$this->cache_path.$file);
}
}
}
//writes or reads the cache
public function cache($key, $value=''){
if ($value!=''){//wants to write
if (strlen(serialize($value)) > 64 ){//write independent file it's a big result
$this->addLog('cache function write in file key:'. $key);
$this->put($key, $value);
}
else {
$this->addLog('cache function write in APP key:'. $key);
$this->APP($key,$value);//write in the APP cache
}
}
else{//reading value
if ( $this->APP($key)!=null ){
$this->addLog('cache function read APP key:'. $key);
return $this->APP($key);//returns from app cache
}
else {
$this->addLog('cache function read file key:'. $key);
return $this->get($key);//returns from file cache
}
}
}
//deletes a key from cache
public function delete($name){
if ( $this->APP($name)!=null ){//unset the APP var
$this->addLog('unset APP key:'. $name);
unset($this->application[md5($name)]);
$this->application_write=true;//says that we have changes to later save the APP
}
elseif ( file_exists($this->fileName($name)) ){//unlink filename
$this->addLog('unset File key:'. $name);
unlink($this->fileName($name));
}
}
// Overloading for the variables and automatically cached
public function __set($name, $value) {
$this->cache($name, $value);
}
public function __get($name) {
return $this->cache($name);
}
public function __isset($name) {//echo "Is '$name' set?n"
$this->addLog('isset key:'. $name);
$value=$this->cache($name);
return isset($value);
}
public function __unset($name) {//echo "Unsetting '$name'n";
$this->delete($name);
}
//end overloads
//////////Cache for files individually///////////////////
//creates new cache files with the given data, $key== name of the cache, data the info/values to store
private function put($key, $data){
if ( $this->get($key)!= $data ){//only write if it's different
$values = serialize($data);
$filename = $this->fileName($key);
$file = fopen($filename, 'w');
if ($file){//able to create the file
$this->addLog('writting key: '.$key.' file: '.$filename);
fwrite($file, $values);
fclose($file);
}
else $this->addLog('unable to write key: '.$key.' file: '.$filename);
}//end if different
}
//returns cache for the given key
private function get($key){
$filename = $this->fileName($key);
if (!file_exists($filename) || !is_readable($filename)){//can't read the cache
$this->addLog('can't read key: '.$key.' file: '.$filename);
return null;
}
if ( time() < (filemtime($filename) + $this->cache_expire) ) {//cache for the key not expired
$file = fopen($filename, 'r');// read data file
if ($file){//able to open the file
$data = fread($file, filesize($filename));
fclose($file);
$this->addLog('reading key: '.$key.' file: '.$filename);
return unserialize($data);//return the values
}
else{
$this->addLog('unable to read key: '.$key.' file: '.$filename);
return null;
}
}
else{
$this->addLog('expired key: '.$key.' file: '.$filename);
unlink($filename);
return null;//was expired you need to create new
}
}
//returns the filename for the cache
private function fileName($key){
return $this->cache_path.md5($key);
}
//////////END Cache for files individually///////////////////
//////////Cache for APP variables///////////////////
//load variables from the file
private function APP_start ($app_file='application'){
$this->application_file=$app_file;
if (file_exists($this->cache_path.$this->application_file)){ // if data file exists, load the cached variables
//erase the cache every X minutes
$app_time=filemtime($this->cache_path.$this->application_file)+$this->cache_expire;
if (time()>$app_time){
$this->addLog('deleting APP file: '.$this->cache_path.$this->application_file);
unlink ($this->cache_path.$this->application_file);//erase the cache
}
else{//not expired
$filesize=filesize($this->cache_path.$this->application_file);
if ($filesize>0){
$file = fopen($this->cache_path.$this->application_file, 'r');// read data file
if ($file){
$this->addLog('reading APP file: '.$this->cache_path.$this->application_file);
$data = fread($file, $filesize);
fclose($file);
$this->application = unserialize($data);// build application variables from data file
}//en if file could open
}//end if file size
}
}
else {//if the file does not exist we create it
$this->addLog('creating APP file: '.$this->cache_path.$this->application_file);
fopen($this->cache_path.$this->application_file, 'w');
}
}
// write application data to file
private function APP_write(){
if ($this->application_write){
$data = serialize($this->application);
$file = fopen($this->cache_path.$this->application_file, 'w');
if ($file){
$this->addLog('writting APP file: '.$this->cache_path.$this->application_file);
fwrite($file, $data);
fclose($file);
}
}
}
//returns the value form APP cache or stores it
private function APP($var,$value=''){
if ($value!=''){//wants to write
if (is_array($this->application)){
if ( array_key_exists(md5($var), $this->application) ){//exist the value in the APP
$write=false;//we don't need to wirte
if ($this->application[md5($var)]!=$value)$write=true;//but exists and is different then we write
}
else $write=true;//not set we write!
}
else $write=false;
if ($write){
$this->addLog('writting APP key:'.$var);
$this->application[md5($var)]=$value;
$this->application_write=true;//says that we have changes to later save the APP
}
}
else {//reading
if ( !is_array($this->application) || ! array_key_exists(md5($var), $this->application) ){
$this->addLog('nothing found for APP key:'.$var);
return null;//nothing found not in array
}
else{
$this->addLog('reading APP key:'.$var);
return $this->application[md5($var)];//return value
}
}
}
//////////End Cache for APP variables///////////////////
////DEBUG
//sets debug on or off
public function setDebug($state){
$this->debug=(bool) $state;
}
public function returnDebug($type='HTML'){
if ($this->debug){
switch($type){
case 'array':
return $this->log;
break;
case 'HTML'://returns debug as HTML
echo '';
foreach($this->log as $key=>$value){//loop in the log var
echo '- '.$value.'
';
}
echo '
';
break;
}
}
else return false;
}
//add debug log
public function addLog($value){
if ($this->debug){//only if debug enabled
array_push($this->log, round((microtime(true) - $this->start_time),5).'s - '. $value);
}
}
}
?>