123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- <?php
- /**
- * 文件缓存类
- * 支持数据缓存和片段缓存
- *
- */
- class FileCache
- {
- /**
- * 缓存文件存放路径
- */
- public $path;
- /**
- * 缓存存放目录数
- */
- public $max_path;
- /**
- * 最多缓存多少个文件,按需配置,此值越大,GC消耗时间越久
- * 此值对cache命中率影响非常小,几乎可以忽略
- */
- public $max_file;
- /**
- * GC 执行概率 百万分之 *
- */
- public $gc_probality;
- private $basepath;
- public function __construct($path = "cache", $max_path = 100, $max_file = 50000, $gc_probality = 100)
- {
- $this->path = $path;
- $this->max_path = $max_path;
- $this->max_file = $max_file;
- $this->gc_probality = $gc_probality;
- $this->basepath = realpath($this->path) . DIRECTORY_SEPARATOR;
- }
- /**
- * 设置缓存
- *
- * @param string $key 保存的key,操作数据的唯一标识,不可重复
- * @param value $val 数据内容,可以是int/string/array/object/Boolean 其他没测过,如有需求自行测试
- * @param int $expired 过期时间,不设默认为一年
- * @return bool
- */
- public function set($key, $val, $expired = 31536000)
- {
- if (rand(0, 1000000) < $this->gc_probality) $this->gc();
- $key = strval($key);
- $cache_file = $this->_getCacheFile($key);
- $data = unserialize(file_get_contents($cache_file));
- empty($data[$key]) && $data[$key] = array();
- $data[$key]['data'] = $val;
- $data[$key]['expired'] = time() + $expired;
- file_put_contents($cache_file, serialize($data), LOCK_EX) or die("写入文件失败");
- return @touch($cache_file, $data[$key]['expired']);
- }
- /**
- * 获得保存的缓存
- *
- * @param string $key key,操作数据的唯一标识
- * @return null/data
- */
- public function get($key)
- {
- $key = strval($key);
- $cache_file = $this->_getCacheFile($key);
- $val = @file_get_contents($cache_file);
- if (!empty($val))
- {
- $val = unserialize($val);
- if (!empty($val) && isset($val[$key]))
- {
- $data = (array) $val[$key];
- if ($data['expired'] < time())
- {
- $this->delete($key);
- return null;
- }
- return $data['data'];
- }
- }
- return null;
- }
- /**
- * 开始片段缓存
- * 必须配合endCache使用
- *
- * @param string $key 保存的key,操作数据的唯一标识,不可重复
- * @param int $expired 过期时间,不设默认为一年
- * @return bool
- */
- public function startCache($key, $expired = 31536000)
- {
- $data = $this->get($key);
- if (!empty($data))
- {
- print $data;
- return false;
- }
- else
- {
- ob_start();
- print $key . "||" . $expired . "filecache_used::]";
- return true;
- }
- }
- /**
- * 结束片段缓存
- *
- * @return bool
- */
- public function endCache()
- {
- $data = ob_get_contents();
- ob_end_clean();
- preg_match("/(.*?)filecache_used::]/is", $data, $key);
- if (empty($key[1]))
- {
- return null;
- }
- $data = str_replace($key[0], '', $data);
- $t = explode("||", $key[1]);
- $key = $t[0];
- $expired = $t[1];
- $this->set($key, $data, $expired);
- print $data;
- }
- /**
- * 删除缓存
- *
- * @param string $key 保存的key,操作数据的唯一标识,不可重复
- * @return bool
- */
- public function delete($key)
- {
- $key = strval($key);
- $cache_file = $this->_getCacheFile($key);
- $data = unserialize(file_get_contents($cache_file));
- unset($data[$key]);
- if (empty($data))
- {
- return @unlink($cache_file);
- }
- file_put_contents($cache_file, serialize($data), LOCK_EX);
- return true;
- }
- /**
- * 缓存回收机制
- * 遍历所有缓存文件,删除已过期文件,如果缓存文件存在不止一个缓存数据,照删不务……
- * TODO 这里之前用hashtable做了数据索引,GC时间会比遍历快30%左右,但是会拖慢set和get的时间,据我测试set会慢一倍!
- *
- * @param string $path 缓存目录
- * @return void
- */
- public function gc($path = null)
- {
- if($path === null) $path = $this->basepath;
- if(($handle = opendir($path)) === false) return;
- while(($file = readdir($handle)) !== false)
- {
- if($file[0] === '.') continue;
- $fullPath = $path . DIRECTORY_SEPARATOR . $file;
- if(is_dir($fullPath))
- {
- $this->gc($fullPath);
- }
- elseif(@filemtime($fullPath) < time())
- {
- @unlink($fullPath);
- }
- }
- closedir($handle);
- }
- private function _getCacheFile($key)
- {
- $hash = $this->hash32($key);
- $path = $this->basepath . $this->_getPathName($hash);
- $file = $path . DIRECTORY_SEPARATOR . $this->_getCacheFileName($hash);
- if (!file_exists($path))
- {
- mkdir($path, 0777);
- }
- if (!file_exists($file))
- {
- $handler = fopen($file, 'w');
- fclose($handler);
- }
- return $file;
- }
- private function _getPathName($hash)
- {
- return $hash % $this->max_path;
- }
- private function _getCacheFileName($hash)
- {
- return $hash % $this->max_file;
- }
- private function hash32($str)
- {
- return crc32($str) >> 16 & 0x7FFFFFFF;
- }
- }
|