<?php
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
));
$this
-> isLocked(
$key
) &&
$this
-> unlock(
$key
);
return
$this
;
}
}
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
;
}
}
class
Cache_File
extends
Cache_Abstract {
public
$useSubdir
= false;
protected
$_cachesDir
= 'cache';
public
function
__construct() {
if
(defined('DATA_DIR')) {
$this
-> _setCacheDir(DATA_DIR . '/cache');
}
}
protected
function
_getCacheFile(
$key
) {
$subdir
=
$this
-> useSubdir ?
substr
(
$key
, 0, 2) . '/' : '';
return
$this
-> _cachesDir . '/' .
$subdir
.
$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
, 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
(!@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
, 0755, true)) {
if
(!
is_dir
(
$cacheDir
)) {
throw
new
CacheException(
"Could not make cache directory"
);
}
}
}
@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() {
$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
)) {
$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
;
}
}
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
;
}
}