| Total Complexity | 40 |
| Total Lines | 282 |
| Duplicated Lines | 0 % |
| Changes | 6 | ||
| Bugs | 2 | Features | 0 |
Complex classes like FileCache often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use FileCache, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 27 | class FileCache extends BaseClass implements CacheInterface { |
||
| 28 | |||
| 29 | /** |
||
| 30 | * Whether to enable compression of the cache data file. |
||
| 31 | * @var boolean |
||
| 32 | */ |
||
| 33 | private $compressCacheData = true; |
||
| 34 | |||
| 35 | /** |
||
| 36 | * The path to file cache data |
||
| 37 | * @var string |
||
| 38 | */ |
||
| 39 | private $cacheFilePath = null; |
||
| 40 | |||
| 41 | /** |
||
| 42 | * Class constructor |
||
| 43 | * @param string|null $cacheFilePath the path to save cache data |
||
| 44 | */ |
||
| 45 | public function __construct($cacheFilePath = null) { |
||
| 46 | parent::__construct(); |
||
| 47 | $this->setCacheFilePath($cacheFilePath); |
||
| 48 | if (!$this->isSupported()) { |
||
| 49 | show_error('The cache for file system is not available. Check the cache directory if is exists or is writable.'); |
||
| 50 | } |
||
| 51 | |||
| 52 | //if Zlib extension is not loaded set compressCacheData to false |
||
| 53 | if (!extension_loaded('zlib')) { |
||
| 54 | $this->logger->warning('The zlib extension is not loaded set cache compress data to FALSE'); |
||
| 55 | $this->compressCacheData = false; |
||
| 56 | } |
||
| 57 | } |
||
| 58 | |||
| 59 | /** |
||
| 60 | * This is used to get the cache data using the key |
||
| 61 | * @param string $key the key to identify the cache data |
||
| 62 | * @return mixed the cache data if exists else return false |
||
| 63 | */ |
||
| 64 | public function get($key) { |
||
| 65 | $this->logger->debug('Getting cache data for key [' . $key . ']'); |
||
| 66 | $filePath = $this->getFilePath($key); |
||
| 67 | if (!file_exists($filePath)) { |
||
| 68 | $this->logger->info('No cache file found for the key [' . $key . '], return false'); |
||
| 69 | return false; |
||
| 70 | } |
||
| 71 | $this->logger->info('The cache file [' . $filePath . '] for the key [' . $key . '] exists, check if the cache data is valid'); |
||
| 72 | $handle = fopen($filePath, 'r'); |
||
| 73 | if (!is_resource($handle)) { |
||
| 74 | $this->logger->error('Can not open the file cache [' . $filePath . '] for the key [' . $key . '], return false'); |
||
| 75 | return false; |
||
| 76 | } |
||
| 77 | // Getting a shared lock |
||
| 78 | flock($handle, LOCK_SH); |
||
| 79 | $data = file_get_contents($filePath); |
||
| 80 | fclose($handle); |
||
| 81 | if ($this->compressCacheData) { |
||
| 82 | $data = gzinflate($data); |
||
| 83 | } |
||
| 84 | $data = @unserialize($data); |
||
| 85 | if (!$data) { |
||
| 86 | $this->logger->error('Can not unserialize the cache data for the key [' . $key . '], return false'); |
||
| 87 | // If unserializing somehow didn't work out, we'll delete the file |
||
| 88 | unlink($filePath); |
||
| 89 | return false; |
||
| 90 | } |
||
| 91 | if (time() > $data['expire']) { |
||
| 92 | $this->logger->info('The cache data for the key [' . $key . '] already expired delete the cache file [' . $filePath . ']'); |
||
| 93 | // Unlinking when the file was expired |
||
| 94 | unlink($filePath); |
||
| 95 | return false; |
||
| 96 | } |
||
| 97 | $this->logger->info('The cache not yet expire, now return the cache data for key [' . $key . '], the cache will expire at [' . date('Y-m-d H:i:s', $data['expire']) . ']'); |
||
| 98 | return $data['data']; |
||
| 99 | } |
||
| 100 | |||
| 101 | |||
| 102 | /** |
||
| 103 | * Save data to the cache |
||
| 104 | * @param string $key the key to identify this cache data |
||
| 105 | * @param mixed $data the cache data |
||
| 106 | * @param integer $ttl the cache life time |
||
| 107 | * @return boolean true if success otherwise will return false |
||
| 108 | */ |
||
| 109 | public function set($key, $data, $ttl = 0) { |
||
| 110 | $expire = time() + $ttl; |
||
| 111 | $this->logger->debug('Setting cache data for key [' . $key . '], time to live [' . $ttl . '], expire at [' . date('Y-m-d H:i:s', $expire) . ']'); |
||
| 112 | $filePath = $this->getFilePath($key); |
||
| 113 | $handle = fopen($filePath, 'w'); |
||
| 114 | if (!is_resource($handle)) { |
||
| 115 | $this->logger->error('Can not open the file cache [' . $filePath . '] for the key [' . $key . '], return false'); |
||
| 116 | return false; |
||
| 117 | } |
||
| 118 | flock($handle, LOCK_EX); // exclusive lock, will get released when the file is closed |
||
| 119 | //Serializing along with the TTL |
||
| 120 | $cacheData = serialize(array( |
||
| 121 | 'mtime' => time(), |
||
| 122 | 'expire' => $expire, |
||
| 123 | 'data' => $data, |
||
| 124 | 'ttl' => $ttl |
||
| 125 | ) |
||
| 126 | ); |
||
| 127 | if ($this->compressCacheData) { |
||
| 128 | $cacheData = gzdeflate($cacheData, 9); |
||
| 129 | } |
||
| 130 | $result = fwrite($handle, $cacheData); |
||
| 131 | if (!$result) { |
||
| 132 | $this->logger->error('Can not write cache data into file [' . $filePath . '] for the key [' . $key . '], return false'); |
||
| 133 | fclose($handle); |
||
| 134 | return false; |
||
| 135 | } |
||
| 136 | $this->logger->info('Cache data saved into file [' . $filePath . '] for the key [' . $key . ']'); |
||
| 137 | fclose($handle); |
||
| 138 | chmod($filePath, 0640); |
||
| 139 | return true; |
||
| 140 | } |
||
| 141 | |||
| 142 | |||
| 143 | /** |
||
| 144 | * Delete the cache data for given key |
||
| 145 | * @param string $key the key for cache to be deleted |
||
| 146 | * @return boolean true if the cache is delete, false if can't delete |
||
| 147 | * the cache or the cache with the given key not exist |
||
| 148 | */ |
||
| 149 | public function delete($key) { |
||
| 150 | $this->logger->debug('Deleting of cache data for key [' . $key . ']'); |
||
| 151 | $filePath = $this->getFilePath($key); |
||
| 152 | $this->logger->info('The file path for the key [' . $key . '] is [' . $filePath . ']'); |
||
| 153 | if (!file_exists($filePath)) { |
||
| 154 | $this->logger->info('This cache file does not exists skipping'); |
||
| 155 | return false; |
||
| 156 | } |
||
| 157 | $this->logger->info('Found cache file [' . $filePath . '] remove it'); |
||
| 158 | return unlink($filePath); |
||
| 159 | } |
||
| 160 | |||
| 161 | /** |
||
| 162 | * Get the cache information for given key |
||
| 163 | * @param string $key the key for cache to get the information for |
||
| 164 | * @return boolean|array the cache information. The associative array and must contains the following information: |
||
| 165 | * 'mtime' => creation time of the cache (Unix timestamp), |
||
| 166 | * 'expire' => expiration time of the cache (Unix timestamp), |
||
| 167 | * 'ttl' => the time to live of the cache in second |
||
| 168 | */ |
||
| 169 | public function getInfo($key) { |
||
| 170 | $this->logger->debug('Getting of cache info for key [' . $key . ']'); |
||
| 171 | $filePath = $this->getFilePath($key); |
||
| 172 | $this->logger->info('The file path for the key [' . $key . '] is [' . $filePath . ']'); |
||
| 173 | if (!file_exists($filePath)) { |
||
| 174 | $this->logger->info('This cache file does not exists skipping'); |
||
| 175 | return false; |
||
| 176 | } |
||
| 177 | $this->logger->info('Found cache file [' . $filePath . '] check the validity'); |
||
| 178 | $data = file_get_contents($filePath); |
||
| 179 | if ($this->compressCacheData) { |
||
| 180 | $data = gzinflate($data); |
||
| 181 | } |
||
| 182 | $data = @unserialize($data); |
||
| 183 | if (!$data) { |
||
| 184 | $this->logger->warning('Can not unserialize the cache data for file [' . $filePath . ']'); |
||
| 185 | return false; |
||
| 186 | } |
||
| 187 | $this->logger->info('This cache data is OK check for expire'); |
||
| 188 | if ($data['expire'] > time()) { |
||
| 189 | $this->logger->info('This cache not yet expired return cache informations'); |
||
| 190 | $info = array( |
||
| 191 | 'mtime' => $data['mtime'], |
||
| 192 | 'expire' => $data['expire'], |
||
| 193 | 'ttl' => $data['ttl'] |
||
| 194 | ); |
||
| 195 | return $info; |
||
| 196 | } |
||
| 197 | $this->logger->info('This cache already expired return false'); |
||
| 198 | return false; |
||
| 199 | } |
||
| 200 | |||
| 201 | |||
| 202 | /** |
||
| 203 | * Used to delete expired cache data |
||
| 204 | */ |
||
| 205 | public function deleteExpiredCache() { |
||
| 206 | $this->logger->debug('Deleting of expired cache files'); |
||
| 207 | $list = glob($this->cacheFilePath . '*.cache'); |
||
| 208 | if (!$list) { |
||
| 209 | $this->logger->info('No cache files were found skipping'); |
||
| 210 | return; |
||
| 211 | } |
||
| 212 | $this->logger->info('Found [' . count($list) . '] cache files to remove if expired'); |
||
| 213 | foreach ($list as $file) { |
||
| 214 | $this->logger->debug('Processing the cache file [' . $file . ']'); |
||
| 215 | $data = file_get_contents($file); |
||
| 216 | if ($this->compressCacheData) { |
||
| 217 | $data = gzinflate($data); |
||
| 218 | } |
||
| 219 | $data = @unserialize($data); |
||
| 220 | if (!$data) { |
||
| 221 | $this->logger->warning('Can not unserialize the cache data for file [' . $file . ']'); |
||
| 222 | } else if (time() > $data['expire']) { |
||
| 223 | $this->logger->info('The cache data for file [' . $file . '] already expired remove it'); |
||
| 224 | unlink($file); |
||
| 225 | } else { |
||
| 226 | $this->logger->info('The cache data for file [' . $file . '] not yet expired skip it'); |
||
| 227 | } |
||
| 228 | } |
||
| 229 | } |
||
| 230 | |||
| 231 | /** |
||
| 232 | * Remove all file from cache folder |
||
| 233 | */ |
||
| 234 | public function clean() { |
||
| 235 | $this->logger->debug('Deleting of all cache files'); |
||
| 236 | $list = glob($this->cacheFilePath . '*.cache'); |
||
| 237 | if (!$list) { |
||
| 238 | $this->logger->info('No cache files were found skipping'); |
||
| 239 | return; |
||
| 240 | } |
||
| 241 | $this->logger->info('Found [' . count($list) . '] cache files to remove'); |
||
| 242 | foreach ($list as $file) { |
||
| 243 | $this->logger->debug('Processing the cache file [' . $file . ']'); |
||
| 244 | unlink($file); |
||
| 245 | } |
||
| 246 | } |
||
| 247 | |||
| 248 | /** |
||
| 249 | * @return boolean |
||
| 250 | */ |
||
| 251 | public function isCompressCacheData() { |
||
| 252 | return $this->compressCacheData; |
||
| 253 | } |
||
| 254 | |||
| 255 | /** |
||
| 256 | * @param boolean $compressCacheData |
||
| 257 | * |
||
| 258 | * @return object |
||
| 259 | */ |
||
| 260 | public function setCompressCacheData($status = true) { |
||
| 261 | //if Zlib extension is not loaded set compressCacheData to false |
||
| 262 | if ($status === true && !extension_loaded('zlib')) { |
||
| 263 | $this->logger->warning('The zlib extension is not loaded set cache compress data to FALSE'); |
||
| 264 | $this->compressCacheData = false; |
||
| 265 | } else { |
||
| 266 | $this->compressCacheData = $status; |
||
| 267 | } |
||
| 268 | return $this; |
||
| 269 | } |
||
| 270 | |||
| 271 | /** |
||
| 272 | * Check whether the cache feature for the handle is supported |
||
| 273 | * |
||
| 274 | * @return bool |
||
| 275 | */ |
||
| 276 | public function isSupported() { |
||
| 277 | return $this->cacheFilePath |
||
| 278 | && is_dir($this->cacheFilePath) |
||
| 279 | && is_writable($this->cacheFilePath); |
||
| 280 | } |
||
| 281 | |||
| 282 | /** |
||
| 283 | * Set the cache file path used to save the cache data |
||
| 284 | * @param string|null $path the file path if null will use the constant CACHE_PATH |
||
| 285 | * |
||
| 286 | * @return object the current instance |
||
| 287 | */ |
||
| 288 | public function setCacheFilePath($path = null) { |
||
| 289 | if ($path !== null) { |
||
| 290 | if (substr($path, -1) != DS) { |
||
| 291 | $path .= DS; |
||
| 292 | } |
||
| 293 | $this->cacheFilePath = $path; |
||
| 294 | return $this; |
||
| 295 | } |
||
| 296 | $this->cacheFilePath = CACHE_PATH; |
||
| 297 | return $this; |
||
| 298 | } |
||
| 299 | |||
| 300 | |||
| 301 | /** |
||
| 302 | * Get the cache file full path for the given key |
||
| 303 | * |
||
| 304 | * @param string $key the cache item key |
||
| 305 | * @return string the full cache file path for this key |
||
| 306 | */ |
||
| 307 | private function getFilePath($key) { |
||
| 309 | } |
||
| 310 | } |
||
| 311 |