├── .gitattributes ├── README.md └── filecache.php /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | filecache php 文件缓存类 2 | ========= 3 | 4 | * 支持key=>value 5 | * 支持片段缓存 6 | 7 | ## 使用方法 8 | 9 | ```php 10 | include 'filecache.php'; 11 | $cache = new FileCache(); 12 | 13 | //数据缓存 14 | $cache->set('test', 'file cache test', 3600); // key, value, expired 15 | $cache->get('test'); 16 | $cache->delete('test'); 17 | 18 | //片段缓存 19 | if($cache->startCache('html', 3600)) // key, expired 20 | { 21 | //缓存内容…… 任意输出 22 | 23 | $cache->endCache(); 24 | } 25 | ``` 26 | 27 | ## 配置 28 | ```php 29 | 30 | 31 | /** 32 | * @param string $path 缓存文件存放路径 33 | * @param int $max_path 缓存的最大目录数 34 | * @param int $max_file 缓存最大文件数 35 | * @param int $gc_probality 执行set时遍历缓存以删除过期缓存数据操作的执行概率 百万分之 * 36 | */ 37 | $cache = new FileCache($path = "cache", $max_path = 100, $max_file = 50000, $gc_probality = 100); 38 | ``` 39 | -------------------------------------------------------------------------------- /filecache.php: -------------------------------------------------------------------------------- 1 | value 缓存 12 | * $c->set('test', 'filecache test', 100); //key, value, expired 13 | * $c->get('test'); 14 | * $c-delete('test'); 15 | * 16 | * //片段缓存 17 | * if ($c->startCache('html', 3600)) //key, expired 18 | * { 19 | * 任意输出内容 20 | * $c->endCache(); 21 | * } 22 | * 23 | */ 24 | 25 | class FileCache 26 | { 27 | /** 28 | * 缓存文件存放路径 29 | */ 30 | public $path; 31 | 32 | /** 33 | * 缓存存放目录数 34 | */ 35 | public $max_path; 36 | 37 | /** 38 | * 最多缓存多少个文件,按需配置,此值越大,GC消耗时间越久 39 | * 此值对cache命中率影响非常小,几乎可以忽略 40 | */ 41 | public $max_file; 42 | 43 | /** 44 | * GC 执行概率 百万分之 * 45 | */ 46 | public $gc_probality; 47 | 48 | private $basepath; 49 | 50 | public function __construct($path = "cache", $max_path = 100, $max_file = 50000, $gc_probality = 100) 51 | { 52 | $this->path = $path; 53 | $this->max_path = $max_path; 54 | $this->max_file = $max_file; 55 | $this->gc_probality = $gc_probality; 56 | $this->basepath = realpath($this->path) . DIRECTORY_SEPARATOR; 57 | } 58 | 59 | /** 60 | * 设置缓存 61 | * 62 | * @param string $key 保存的key,操作数据的唯一标识,不可重复 63 | * @param value $val 数据内容,可以是int/string/array/object/Boolean 其他没测过,如有需求自行测试 64 | * @param int $expired 过期时间,不设默认为一年 65 | * @return bool 66 | */ 67 | public function set($key, $val, $expired = 31536000) 68 | { 69 | if (rand(0, 1000000) < $this->gc_probality) $this->gc(); 70 | 71 | $key = strval($key); 72 | $cache_file = $this->_getCacheFile($key); 73 | 74 | $data = unserialize(file_get_contents($cache_file)); 75 | empty($data[$key]) && $data[$key] = array(); 76 | 77 | $data[$key]['data'] = $val; 78 | $data[$key]['expired'] = time() + $expired; 79 | 80 | file_put_contents($cache_file, serialize($data), LOCK_EX) or die("写入文件失败"); 81 | return @touch($cache_file, $data[$key]['expired']); 82 | } 83 | 84 | /** 85 | * 获得保存的缓存 86 | * 87 | * @param string $key key,操作数据的唯一标识 88 | * @return null/data 89 | */ 90 | public function get($key) 91 | { 92 | $key = strval($key); 93 | $cache_file = $this->_getCacheFile($key); 94 | $val = @file_get_contents($cache_file); 95 | 96 | if (!empty($val)) 97 | { 98 | $val = unserialize($val); 99 | if (!empty($val) && isset($val[$key])) 100 | { 101 | $data = (array) $val[$key]; 102 | if ($data['expired'] < time()) 103 | { 104 | $this->delete($key); 105 | return null; 106 | } 107 | return $data['data']; 108 | } 109 | } 110 | return null; 111 | } 112 | 113 | /** 114 | * 开始片段缓存 115 | * 必须配合endCache使用 116 | * 117 | * @param string $key 保存的key,操作数据的唯一标识,不可重复 118 | * @param int $expired 过期时间,不设默认为一年 119 | * @return bool 120 | */ 121 | public function startCache($key, $expired = 31536000) 122 | { 123 | $data = $this->get($key); 124 | if (!empty($data)) 125 | { 126 | print $data; 127 | return false; 128 | } 129 | else 130 | { 131 | ob_start(); 132 | print $key . "||" . $expired . "filecache_used::]"; 133 | return true; 134 | } 135 | } 136 | 137 | /** 138 | * 结束片段缓存 139 | * 140 | * @return bool 141 | */ 142 | public function endCache() 143 | { 144 | $data = ob_get_contents(); 145 | ob_end_clean(); 146 | preg_match("/(.*?)filecache_used::]/is", $data, $key); 147 | if (empty($key[1])) 148 | { 149 | return null; 150 | } 151 | $data = str_replace($key[0], '', $data); 152 | $t = explode("||", $key[1]); 153 | $key = $t[0]; 154 | $expired = $t[1]; 155 | $this->set($key, $data, $expired); 156 | print $data; 157 | } 158 | 159 | /** 160 | * 删除缓存 161 | * 162 | * @param string $key 保存的key,操作数据的唯一标识,不可重复 163 | * @return bool 164 | */ 165 | public function delete($key) 166 | { 167 | $key = strval($key); 168 | $cache_file = $this->_getCacheFile($key); 169 | 170 | $data = unserialize(file_get_contents($cache_file)); 171 | unset($data[$key]); 172 | if (empty($data)) 173 | { 174 | return @unlink($cache_file); 175 | } 176 | file_put_contents($cache_file, serialize($data), LOCK_EX); 177 | return true; 178 | } 179 | 180 | /** 181 | * 缓存回收机制 182 | * 遍历所有缓存文件,删除已过期文件,如果缓存文件存在不止一个缓存数据,照删不务…… 183 | * TODO 这里之前用hashtable做了数据索引,GC时间会比遍历快30%左右,但是会拖慢set和get的时间,据我测试set会慢一倍! 184 | * 185 | * @param string $path 缓存目录 186 | * @return void 187 | */ 188 | public function gc($path = null) 189 | { 190 | if($path === null) $path = $this->basepath; 191 | if(($handle = opendir($path)) === false) return; 192 | while(($file = readdir($handle)) !== false) 193 | { 194 | if($file[0] === '.') continue; 195 | $fullPath = $path . DIRECTORY_SEPARATOR . $file; 196 | if(is_dir($fullPath)) 197 | { 198 | $this->gc($fullPath); 199 | } 200 | elseif(@filemtime($fullPath) < time()) 201 | { 202 | @unlink($fullPath); 203 | } 204 | } 205 | closedir($handle); 206 | } 207 | 208 | 209 | private function _getCacheFile($key) 210 | { 211 | $hash = $this->hash32($key); 212 | $path = $this->basepath . $this->_getPathName($hash); 213 | $file = $path . DIRECTORY_SEPARATOR . $this->_getCacheFileName($hash); 214 | if (!file_exists($path)) 215 | { 216 | mkdir($path, 0777); 217 | } 218 | if (!file_exists($file)) 219 | { 220 | $handler = fopen($file, 'w'); 221 | fclose($handler); 222 | } 223 | return $file; 224 | } 225 | 226 | private function _getPathName($hash) 227 | { 228 | return $hash % $this->max_path; 229 | } 230 | 231 | private function _getCacheFileName($hash) 232 | { 233 | return $hash % $this->max_file; 234 | } 235 | 236 | private function hash32($str) 237 | { 238 | return crc32($str) >> 16 & 0x7FFFFFFF; 239 | } 240 | } --------------------------------------------------------------------------------