PHP cache implementation code and detailed comments

  • 2020-03-31 20:44:31
  • OfStack


class CacheException extends Exception {} 
 
abstract class Cache_Abstract { 
 
abstract public function fetch($key); 

 
abstract public function store($key, $value); 

 
abstract public function delete($key); 

 
abstract public function clear(); 

 
abstract public function lock($key); 
 
abstract public function unlock($key); 
 
abstract public function isLocked($key); 
 
public function checkLock($key) { 
if (!$this->isLocked($key)) { 
return $this; 
} 

$tries = 10; 
$count = 0; 
do { 
usleep(200); 
$count ++; 
} while ($count <= $tries && $this->isLocked($key)); //Do a maximum of 10 sleep waits to be unlocked
$this->isLocked($key) && $this->unlock($key); 

return $this; 
} 
} 

/** 
* APC Extended cache implementation  
* 
* 
* @category Mjie 
* @package Cache 
* @author  Water Meng Chun  
* @copyright Copyright (c) 2008- <cmpan(at)qq.com> 
* @license New BSD License 
* @version $Id: Cache/Apc.php  The version number  2010-04-18 23:02 cmpan $ 
*/ 
class Cache_Apc extends Cache_Abstract { 

protected $_prefix = 'cache.mjie.net'; 

public function __construct() { 
if (!function_exists('apc_cache_info')) { 
throw new CacheException('apc extension didn't installed'); 
} 
} 

 
public function store($key, $value) { 
return apc_store($this->_storageKey($key), $value); 
} 

 
public function fetch($key) { 
return apc_fetch($this->_storageKey($key)); 
} 

 
public function clear() { 
apc_clear_cache(); 
return $this; 
} 

 
public function delete($key) { 
apc_delete($this->_storageKey($key)); 
return $this; 
} 

 
public function isLocked($key) { 
if ((apc_fetch($this->_storageKey($key) . '.lock')) === false) { 
return false; 
} 

return true; 
} 

 
public function lock($key) { 
apc_store($this->_storageKey($key) . '.lock', '', 5); 
return $this; 
} 

 
public function unlock($key) { 
apc_delete($this->_storageKey($key) . '.lock'); 
return $this; 
} 

 
private function _storageKey($key) { 
return $this->_prefix . '_' . $key; 
} 
} 
/** 
*  File cache implementation  
* 
* 
* @category Mjie 
* @package Cache 
* @author  Water Meng Chun  
* @copyright Copyright (c) 2008- <cmpan(at)qq.com> 
* @license New BSD License 
* @version $Id: Cache/File.php  The version number  2010-04-18 16:46 cmpan $ 
*/ 
class Cache_File extends Cache_Abstract { 

protected $_cachesDir = 'cache'; 

public function __construct() { 
if (defined('DATA_DIR')) { 
$this->_setCacheDir(DATA_DIR . '/cache'); 
} 
} 

 
protected function _getCacheFile($key) { 
return $this->_cachesDir . '/' . substr($key, 0, 2) . '/' . $key . '.php'; 
} 
 
public function fetch($key) { 
$cacheFile = self::_getCacheFile($key); 
if (file_exists($cacheFile) && is_readable($cacheFile)) { 
return unserialize(@file_get_contents($cacheFile, false, NULL, 13)); 
} 
return false; 
} 
 
public function store($key, $value) { 
$cacheFile = self::_getCacheFile($key); 
$cacheDir = dirname($cacheFile); 
if(!is_dir($cacheDir)) { 
if(mkdir($cacheDir" target="_blank">!@mkdir($cacheDir, 0755, true)) { 
throw new CacheException("Could not make cache directory"); 
} 
} 
return @file_put_contents($cacheFile, '<?php exit;?>' . serialize($value)); 
} 
 
public function delete($key) { 
if(emptyempty($key)) { 
throw new CacheException("Missing argument 1 for Cache_File::delete()"); 
} 

$cacheFile = self::_getCacheFile($key); 
if($cacheFile" target="_blank">!@unlink($cacheFile)) { 
throw new CacheException("Cache file could not be deleted"); 
} 
return $this; 
} 
 
public function isLocked($key) { 
$cacheFile = self::_getCacheFile($key); 
clearstatcache(); 
return file_exists($cacheFile . '.lock'); 
} 
 
public function lock($key) { 
$cacheFile = self::_getCacheFile($key); 
$cacheDir = dirname($cacheFile); 
if(!is_dir($cacheDir)) { 
if(mkdir($cacheDir" target="_blank">!@mkdir($cacheDir, 0755, true)) { 
if(!is_dir($cacheDir)) { 
throw new CacheException("Could not make cache directory"); 
} 
} 
} 
//Sets the access and modification time of the cache lock file
@touch($cacheFile . '.lock'); 
return $this; 
} 

 
public function unlock($key) { 
$cacheFile = self::_getCacheFile($key); 
@unlink($cacheFile . '.lock'); 
return $this; 
} 
 
protected function _setCacheDir($dir) { 
$this->_cachesDir = rtrim(str_replace('\', '/', trim($dir)), '/'); 
clearstatcache(); 
if(!is_dir($this->_cachesDir)) { 
mkdir($this->_cachesDir, 0755, true); 
} 
// 
return $this; 
} 

 
public function clear() { 
//Traversing the directory clears the cache
$cacheDir = $this->_cachesDir; 
$d = dir($cacheDir); 
while(false !== ($entry = $d->read())) { 
if('.' == $entry[0]) { 
continue; 
} 

$cacheEntry = $cacheDir . '/' . $entry; 
if(is_file($cacheEntry)) { 
@unlink($cacheEntry); 
} elseif(is_dir($cacheEntry)) { 
//The cache folder has two levels
$d2 = dir($cacheEntry); 
while(false !== ($entry = $d2->read())) { 
if('.' == $entry[0]) { 
continue; 
} 

$cacheEntry .= '/' . $entry; 
if(is_file($cacheEntry)) { 
@unlink($cacheEntry); 
} 
} 
$d2->close(); 
} 
} 
$d->close(); 

return $this; 
} 
} 
/** 
*  Cache the data structure of the cell  
* array( 
* 'time' => time(), //Cache the timestamp at write time
* 'expire' => $expire, //Cache expiration time
* 'valid' => true, //Whether the cache is valid or not
* 'data' => $value //The value of the cache
* ); 
*/ 
final class Cache { 
 
private $_expire = 3600; 
 
private $_storage = null; 
 
static public function createCache($cacheClass = 'Cache_File') { 
return new self($cacheClass); 
} 
private function __construct($cacheClass) { 
$this->_storage = new $cacheClass(); 
} 
 
public function set($key, $value, $expire = false) { 
if (!$expire) { 
$expire = $this->_expire; 
} 

$this->_storage->checkLock($key); 

$data = array('time' => time(), 'expire' => $expire, 'valid' => true, 'data' => $value); 
$this->_storage->lock($key); 

try { 
$this->_storage->store($key, $data); 
$this->_storage->unlock($key); 
} catch (CacheException $e) { 
$this->_storage->unlock($key); 
throw $e; 
} 
} 
 
public function get($key) { 
$data = $this->fetch($key); 
if ($data && $data['valid'] && !$data['isExpired']) { 
return $data['data']; 
} 

return false; 
} 
 
public function fetch($key) { 
$this->_storage->checkLock($key); 
$data = $this->_storage->fetch($key); 
if ($data) { 
$data['isExpired'] = (time() - $data['time']) > $data['expire'] ? true : false; 
return $data; 
} 

return false; 
} 
 
public function delete($key) { 
$this->_storage->checkLock($key) 
->lock($key) 
->delete($key) 
->unlock($key); 
} 

public function clear() { 
$this->_storage->clear(); 
} 
 
public function setInvalidate($key) { 
$this->_storage->checkLock($key) 
->lock($key); 
try { 
$data = $this->_storage->fetch($key); 
if ($data) { 
$data['valid'] = false; 
$this->_storage->store($key, $data); 
} 
$this->_storage->unlock($key); 
} catch (CacheException $e) { 
$this->_storage->unlock($key); 
throw $e; 
} 
} 

 
public function setExpire($expire) { 
$this->_expire = (int) $expire; 
return $this; 
} 
} 

Related articles: