Ну и readCache с lockCache static protected function readCache($id, $items, $timeout = 500, $wLock = null)
{
$ok = false;
if (@is_file(CT_ROOT."/cache/{$id}.info")) {
$finfo = fopen(CT_ROOT."/cache/{$id}.info", 'rb');
flock($finfo, LOCK_SH);
fseek($finfo, 0, SEEK_SET);
$data = array('info' => @unserialize(stream_get_contents($finfo)));
if (is_array($data['info']) && isset($data['info']['timestamp']) && isset($data['info']['version']) && ($data['info']['version'] === CT_VERSION)) {
if (abs((microtime(true) - $data['info']['timestamp']) * 1000) < $timeout) {
# cache is ok, demote write lock early if it exists
if ($wLock !== null) self::unlockCache($wLock);
# read items now
$ok = true;
foreach ($items as $item)
if (@is_file(CT_ROOT."/cache/{$id}.{$item}")) $data[$item] = @unserialize(file_get_contents(CT_ROOT."/cache/{$id}.{$item}"));
}
}
flock($finfo, LOCK_UN);
fclose($finfo);
}
return $ok ? $data : false;
}
static public function lockCache($id)
{
$fh = fopen(CT_ROOT."/cache/{$id}.lock", 'ab+');
flock($fh, LOCK_EX);
return $fh;
}
static public function writeCache($id, &$data)
$data['info'] = array('version' => CT_VERSION, 'timestamp' => microtime(true));
foreach (array_keys($data) as $key) {
switch ($key) {
case 'valid':
case 'changed':
$data['info'][$key] = $data[$key];
unset($data[$key]);
break;
}
}
$finfo = fopen(CT_ROOT."/cache/{$id}.info", 'ab+');
flock($finfo, LOCK_EX);
fseek($finfo, 0, SEEK_SET);
ftruncate($finfo, 0);
fwrite($finfo, serialize($data['info']));
fflush($finfo);
foreach ($data as $key => $val) {
if ($key == 'info') continue;
file_put_contents(CT_ROOT."/cache/{$id}.{$key}", serialize($val));
}
flock($finfo, LOCK_UN);
fclose($finfo);
}
static public function unlockCache($fh)
{
flock($fh, LOCK_UN);
fclose($fh);
}
Нормально работает, коллизии исключаются блокировкой. Можно адаптировать хоть к сайту, хоть к чёрту лысому. Структура кэша в примере правда слегка хитрая, под конкретное приложение.