<?php
class FileCache
{
public $keyPrefix = '';
public $cachePath = '';
public $cacheFileSuffix = '.bin';
public $directoryLevel = 1;
public $gcProbability = 10;
public $fileMode;
public $dirMode = 0775;
function __construct()
{
$this->cachePath = HT::$cacheRoot.'htcache';
}
function FileCache()
{
$this->__construct();
}
public function set($key, $value, $duration = 0)
{
//将内容存入文件中一般以字符串形式存储。要序列化。
serialize()就是将PHP中的变量如对象(object),数组(array)
等等的值序列化为字符串后存储起来
$value = serialize([$value]);
$key = $this->buildKey($key);
return $this->setValue($key, $value, $duration);
}
public function get($key)
{
$key = $this->buildKey($key);
$value = $this->getValue($key);
if ($value === false) {
return $value;
} else {
$value = unserialize($value);
}
if (is_array($value)) {
return $value[0];
} else {
return false;
}
}
public function delete($key)
{
$key = $this->buildKey($key);
return $this->deleteValue($key);
}
public function buildKey($key)
{
if (is_string($key)) {
//Returns TRUE if every character in text is either a letter or a digit
$key = ctype_alnum($key) && mb_strlen($key, '8bit') <= 32 ? $key : md5($key);
} else {
$key = md5(json_encode($key, JSON_NUMERIC_CHECK));
}
return $this->keyPrefix . $key;
}
protected function setValue($key, $value, $duration)
{
$this->gc();
$cacheFile = $this->getCacheFile($key);
if ($this->directoryLevel > 0) {
@mkdir(dirname($cacheFile), $this->dirMode, true);
}
if (@file_put_contents($cacheFile, $value, LOCK_EX) !== false) {
if ($this->fileMode !== null) {
//改变文件模式mode 不会被自动当成八进制数值,要确保正确操作,需要给 mode 前面加上 0
@chmod($cacheFile, $this->fileMode);
}
if ($duration <= 0) {
$duration = 31536000; // 1 year
}
// touch — 设定文件的访问和修改时间
return @touch($cacheFile, $duration + time());
} else {
$error = error_get_last();
HT::warning("Unable to write cache file '{$cacheFile}': {$error['message']}", __METHOD__);
return false;
}
}
protected function getValue($key)
{
$cacheFile = $this->getCacheFile($key);
if (@filemtime($cacheFile) > time()) {
$fp = @fopen($cacheFile, 'r');
if ($fp !== false) {
@flock($fp, LOCK_SH);
$cacheValue = @stream_get_contents($fp);
@flock($fp, LOCK_UN);
@fclose($fp);
return $cacheValue;
}
}
return false;
}
protected function deleteValue($key)
{
$cacheFile = $this->getCacheFile($key);
return @unlink($cacheFile);
}
protected function getCacheFile($key)
{
if ($this->directoryLevel > 0) {
$base = $this->cachePath;
for ($i = 0; $i < $this->directoryLevel; ++$i) {
if (($prefix = substr($key, $i + $i, 2)) !== false) {
$base .= DIRECTORY_SEPARATOR . $prefix;
}
}
return $base . DIRECTORY_SEPARATOR . $key . $this->cacheFileSuffix;
} else {
return $this->cachePath . DIRECTORY_SEPARATOR . $key . $this->cacheFileSuffix;
}
}
public function gc($force = false, $expiredOnly = true)
{
if ($force || mt_rand(0, 1000000) < $this->gcProbability) {
$this->gcRecursive($this->cachePath, $expiredOnly);
}
}
protected function gcRecursive($path, $expiredOnly)
{
if (($handle = opendir($path)) !== false) {
while (($file = readdir($handle)) !== false) {
//$file[0] . ..两种目录
if ($file === '.' || $file==='..') {
continue;
}
$fullPath = $path . DIRECTORY_SEPARATOR . $file;
if (is_dir($fullPath)) {
$this->gcRecursive($fullPath, $expiredOnly);
if (!$expiredOnly) {
//删除路径
if (!@rmdir($fullPath)) {
$error = error_get_last();
HT::warning("Unable to remove directory '{$fullPath}': {$error['message']}", __METHOD__);
}
}
} elseif (!$expiredOnly || $expiredOnly && @filemtime($fullPath) < time()) {
if (!@unlink($fullPath)) {
//获取最近的一次错误;
$error = error_get_last();
HT::warning("Unable to remove file '{$fullPath}': {$error['message']}", __METHOD__);
}
}
}
closedir($handle);
}
}
}
?>