Skip to content

Instantly share code, notes, and snippets.

@nigrosimone
Last active May 7, 2023 16:18
Show Gist options
  • Save nigrosimone/401221c9439ae9d0c02bb5889562e3fd to your computer and use it in GitHub Desktop.
Save nigrosimone/401221c9439ae9d0c02bb5889562e3fd to your computer and use it in GitHub Desktop.
Simple php cache using php generated files and opcache
<?php
/**
* Simple php cache using php generated files and opcache
* Usage:
* $diskCache = new DiskCache();
* $diskCache->write('foo', 'bar');
* echo $diskCache->read('foo'); // print bar
*/
class DiskCache {
const CACHE_FRTNAME = 'cache_%s.php';
const DEFAULT_TTL = 3600;
/**
* @var callable
*/
private static $emptyErrorHandler;
/**
* @var string
*/
protected $cacheDir;
/**
* @var int
*/
protected $defaultTtl;
/**
* Constructor
* @param string $cacheDir where to store cache files
* @param integer $ttl time to live
*/
public function __construct($cacheDir = null, $ttl = self::DEFAULT_TTL) {
if( empty($cacheDir) ){
$cacheDir = sys_get_temp_dir();
}
$cacheDir = realpath(rtrim($cacheDir, DIRECTORY_SEPARATOR));
if( !is_dir($cacheDir) ) {
throw new InvalidArgumentException('Provided cache dir is not a directory');
}
if( !(is_readable($cacheDir) && is_writable($cacheDir)) ) {
throw new InvalidArgumentException('Provided cache dir is not writable and readable');
}
$this->cacheDir = $cacheDir;
$this->defaultTtl = (int) $ttl;
self::$emptyErrorHandler = function(){};
}
/**
* Read cache
* @param string $key the key
* @return mixed|false cached data
*/
public function read($key) {
$fileName = $this->getCacheFilename($key);
set_error_handler(self::$emptyErrorHandler);
$cached = include $fileName;
restore_error_handler();
if( $cached && isset($cached['timestamp'], $cached['ttl'], $cached['data']) ) {
if((time() - $cached['timestamp']) < $cached['ttl']){
return $cached['data'];
}
}
if( $cached ) {
$this->delete($key);
}
return false;
}
/**
* Write cache
* @param string $key the key
* @param mixed $data the data
* @param integer $ttl time to live
* @return boolean
*/
public function write($key, $data, $ttl = null) {
$ttl = $ttl > 0 ? (int) $ttl : $this->defaultTtl;
$fileName = $this->getCacheFilename($key);
$code = null;
$result = false;
$value = array(
'timestamp' => time(),
'ttl' => $ttl,
'data' => $data
);
if (is_object($data) && method_exists($data, '__set_state')) {
$code = sprintf('<?php return %s;', var_export($value, true));
} else {
$code = sprintf('<?php return unserialize(%s);', var_export(serialize($value), true));
}
if( $code ){
$result = (boolean) @file_put_contents($fileName, $code, LOCK_EX);
if( $result ){
// force opcache to invalidate the file cache
if( function_exists('opcache_invalidate') ){
opcache_invalidate($fileName, true);
}
}
}
return $result;
}
/**
* Delete cache
* @param string $key
* @return boolean
*/
public function delete($key) {
$fileName = $this->getCacheFilename($key);
$result = @unlink($fileName);
if( $result ){
if( function_exists('opcache_invalidate') ){
opcache_invalidate($fileName, true);
}
}
return $result;
}
/**
* Flush the cache
* @return boolean
*/
public function flush() {
$result = false;
$files = glob($this->cacheDir . DIRECTORY_SEPARATOR . sprintf(self::CACHE_FRTNAME, '*'));
if( $files ){
$result = true;
foreach ($files as $file){
if( !@unlink($file) ){
$result = false;
}
}
if( function_exists('opcache_reset') ){
opcache_reset();
}
}
return $result;
}
/**
* Return the cache filename
* @param string $key
* @throws InvalidArgumentException
* @return string
*/
public function getCacheFilename($key){
if( $key == '' ) {
throw new InvalidArgumentException('key is empty');
}
return $this->cacheDir . DIRECTORY_SEPARATOR . sprintf(self::CACHE_FRTNAME, md5($key));
}
}
@Or4ngm4n
Copy link

Or4ngm4n commented May 7, 2023

add in after line 185 }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment