fwolf /
fwlib
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | namespace Fwlib\Cache\Handler; |
||
| 3 | |||
| 4 | use Fwlib\Cache\AbstractHandler; |
||
| 5 | use Fwlib\Cache\OperateType; |
||
| 6 | use Fwlib\Util\UtilContainerAwareTrait; |
||
| 7 | |||
| 8 | /** |
||
| 9 | * Key-value cache system, data store in file |
||
| 10 | * |
||
| 11 | * Notice: Expired cache file is not deleted automatic. |
||
| 12 | * |
||
| 13 | * @copyright Copyright 2010-2015 Fwolf |
||
| 14 | * @license http://www.gnu.org/licenses/lgpl.html LGPL-3.0+ |
||
| 15 | */ |
||
| 16 | class File extends AbstractHandler |
||
| 17 | { |
||
| 18 | use UtilContainerAwareTrait; |
||
| 19 | |||
| 20 | |||
| 21 | /** |
||
| 22 | * Error in config check |
||
| 23 | * |
||
| 24 | * @var string[] |
||
| 25 | */ |
||
| 26 | protected $errorMessages = []; |
||
| 27 | |||
| 28 | |||
| 29 | /** |
||
| 30 | * Check if cache is ready for use |
||
| 31 | * |
||
| 32 | * @return boolean |
||
| 33 | */ |
||
| 34 | public function checkConfig() |
||
| 35 | { |
||
| 36 | $pass = true; |
||
| 37 | $this->errorMessages = []; |
||
| 38 | |||
| 39 | $dir = $this->getConfig('fileDir'); |
||
| 40 | View Code Duplication | if (empty($dir)) { |
|
|
0 ignored issues
–
show
|
|||
| 41 | $this->errorMessages[] = 'No cache file dir defined'; |
||
| 42 | $pass = false; |
||
| 43 | |||
| 44 | } else { |
||
| 45 | $message = $this->checkFileDirConfig($dir); |
||
| 46 | if (!empty($message)) { |
||
| 47 | $this->errorMessages[] = |
||
| 48 | 'Cache file directory config error: ' . $message; |
||
| 49 | $pass = false; |
||
| 50 | } |
||
| 51 | } |
||
| 52 | |||
| 53 | $rule = $this->getConfig('fileRule'); |
||
| 54 | View Code Duplication | if (empty($rule)) { |
|
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 55 | $this->errorMessages[] = 'No cache file rule defined'; |
||
| 56 | $pass = false; |
||
| 57 | |||
| 58 | } else { |
||
| 59 | $message = $this->checkFileRuleConfig($rule); |
||
| 60 | if (!empty($message)) { |
||
| 61 | $this->errorMessages[] = |
||
| 62 | 'Cache file rule config error: ' . $message; |
||
| 63 | $pass = false; |
||
| 64 | } |
||
| 65 | } |
||
| 66 | |||
| 67 | return $pass; |
||
| 68 | } |
||
| 69 | |||
| 70 | |||
| 71 | /** |
||
| 72 | * Check config/cache store dir valid and writable |
||
| 73 | * |
||
| 74 | * If error, return error msg, else return empty str. |
||
| 75 | * |
||
| 76 | * @param string $dir |
||
| 77 | * @return string |
||
| 78 | */ |
||
| 79 | protected function checkFileDirConfig($dir) |
||
| 80 | { |
||
| 81 | $message = ''; |
||
| 82 | |||
| 83 | View Code Duplication | if (!file_exists($dir)) { |
|
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 84 | if (false == mkdir($dir, 0755, true)) { |
||
|
0 ignored issues
–
show
|
|||
| 85 | $message = "Fail to create cache directory \"{$dir}\""; |
||
| 86 | } |
||
| 87 | |||
| 88 | } else { |
||
| 89 | if (!is_writable($dir)) { |
||
| 90 | $message = "Cache directory \"{$dir}\" is not writable."; |
||
| 91 | } |
||
| 92 | } |
||
| 93 | |||
| 94 | return $message; |
||
| 95 | } |
||
| 96 | |||
| 97 | |||
| 98 | /** |
||
| 99 | * Check cache rule exist and valid |
||
| 100 | * |
||
| 101 | * If error, return error msg, else return empty str. |
||
| 102 | * |
||
| 103 | * @param string $rule |
||
| 104 | * @return string |
||
| 105 | */ |
||
| 106 | View Code Duplication | protected function checkFileRuleConfig($rule) |
|
|
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 107 | { |
||
| 108 | if (2 > strlen($rule)) { |
||
| 109 | return('Cache rule is not defined or too short'); |
||
| 110 | } |
||
| 111 | |||
| 112 | if (0 != (strlen($rule) % 2)) { |
||
| 113 | return("Cache rule \"$rule\" may not right"); |
||
| 114 | } |
||
| 115 | |||
| 116 | return ''; |
||
| 117 | } |
||
| 118 | |||
| 119 | |||
| 120 | /** |
||
| 121 | * {@inheritdoc} |
||
| 122 | */ |
||
| 123 | public function delete($key) |
||
| 124 | { |
||
| 125 | $file = $this->getFilePath($key); |
||
| 126 | |||
| 127 | if (file_exists($file)) { |
||
| 128 | $success = unlink($file); |
||
| 129 | $this->log(OperateType::DELETE, $key, $success); |
||
| 130 | } |
||
| 131 | |||
| 132 | $this->log(OperateType::DELETE, $key, true); |
||
| 133 | |||
| 134 | return $this; |
||
| 135 | } |
||
| 136 | |||
| 137 | |||
| 138 | /** |
||
| 139 | * {@inheritdoc} |
||
| 140 | * |
||
| 141 | * File cache should check lifetime when get, return null when fail. |
||
| 142 | */ |
||
| 143 | public function get($key, $lifetime = null) |
||
| 144 | { |
||
| 145 | if ($this->isExpired($key, $lifetime)) { |
||
| 146 | $this->log(OperateType::GET, $key, false); |
||
| 147 | return null; |
||
| 148 | } |
||
| 149 | |||
| 150 | // Read from file and parse it. |
||
| 151 | $file = $this->getFilePath($key); |
||
| 152 | $content = file_get_contents($file); |
||
| 153 | $this->log(OperateType::GET, $key, !(false === $content)); |
||
| 154 | |||
| 155 | return $content; |
||
| 156 | } |
||
| 157 | |||
| 158 | |||
| 159 | /** |
||
| 160 | * {@inheritdoc} |
||
| 161 | */ |
||
| 162 | protected function getDefaultConfigs() |
||
| 163 | { |
||
| 164 | $configs = parent::getDefaultConfigs(); |
||
| 165 | |||
| 166 | |||
| 167 | // Dir where data file store |
||
| 168 | $configs['fileDir'] = '/tmp/cache/'; |
||
| 169 | |||
| 170 | /** |
||
| 171 | * Cache file store rule |
||
| 172 | * |
||
| 173 | * Group by every 2-chars, their means: |
||
| 174 | * 10 first 2 char of md5 hash, 16 * 16 = 256 |
||
| 175 | * 11 3-4 char of md5 hash |
||
| 176 | * 20 last 2 char of md5 hash |
||
| 177 | * 30 first 2 char of key |
||
| 178 | * 40 last 2 char of key |
||
| 179 | * 5n crc32, n=0..3, 16 * 16 = 256 |
||
| 180 | * Join these str with '/', got full path of cache file. |
||
| 181 | */ |
||
| 182 | $configs['fileRule'] = '10'; |
||
| 183 | |||
| 184 | |||
| 185 | return $configs; |
||
| 186 | } |
||
| 187 | |||
| 188 | |||
| 189 | /** |
||
| 190 | * @return string[] |
||
| 191 | */ |
||
| 192 | public function getErrorMessages() |
||
| 193 | { |
||
| 194 | return $this->errorMessages; |
||
| 195 | } |
||
| 196 | |||
| 197 | |||
| 198 | /** |
||
| 199 | * Compute data file name of a key |
||
| 200 | * |
||
| 201 | * @param string $key |
||
| 202 | * @return string |
||
| 203 | */ |
||
| 204 | protected function getFileName($key) |
||
| 205 | { |
||
| 206 | return substr(md5($key), 0, 8); |
||
| 207 | } |
||
| 208 | |||
| 209 | |||
| 210 | /** |
||
| 211 | * Compute data file path of a key |
||
| 212 | * |
||
| 213 | * @param string $key |
||
| 214 | * @return string |
||
| 215 | */ |
||
| 216 | protected function getFilePath($key) |
||
| 217 | { |
||
| 218 | $path = $this->getConfig('fileDir'); |
||
| 219 | |||
| 220 | $rules = str_split($this->getConfig('fileRule'), 2); |
||
| 221 | |||
| 222 | foreach ($rules as $rule) { |
||
| 223 | // Path section may be empty |
||
| 224 | $pathSection = $this->getFilePathSection($rule, $key); |
||
| 225 | if (!empty($pathSection)) { |
||
| 226 | $pathSection .= '/'; |
||
| 227 | } |
||
| 228 | |||
| 229 | $path .= $pathSection; |
||
| 230 | } |
||
| 231 | |||
| 232 | $path .= $this->getFileName($key); |
||
| 233 | |||
| 234 | return $path; |
||
| 235 | } |
||
| 236 | |||
| 237 | |||
| 238 | /** |
||
| 239 | * Compute path of a key by a single rule section |
||
| 240 | * |
||
| 241 | * @param string $rule |
||
| 242 | * @param string $key |
||
| 243 | * @return string |
||
| 244 | */ |
||
| 245 | protected function getFilePathSection($rule, $key) |
||
| 246 | { |
||
| 247 | $len = 2; |
||
| 248 | |||
| 249 | if ($len > strlen($rule)) { |
||
| 250 | return ''; |
||
| 251 | } |
||
| 252 | |||
| 253 | $seed = $key; |
||
| 254 | $start = 0; |
||
| 255 | $seq = intval($rule{1}); |
||
| 256 | if (1 == $rule{0}) { |
||
| 257 | // md5 from start |
||
| 258 | $start = $len * $seq; |
||
| 259 | $seed = md5($key); |
||
| 260 | |||
| 261 | View Code Duplication | } elseif (2 == $rule{0}) { |
|
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 262 | // md5 from end |
||
| 263 | $start = -1 * $len * ($seq + 1); |
||
| 264 | $seed = md5($key); |
||
| 265 | |||
| 266 | } elseif (3 == $rule{0}) { |
||
| 267 | // raw from start |
||
| 268 | $start = $len * $seq; |
||
| 269 | $seed = $key; |
||
| 270 | |||
| 271 | View Code Duplication | } elseif (4 == $rule{0}) { |
|
|
0 ignored issues
–
show
This code seems to be duplicated across your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. Loading history...
|
|||
| 272 | // raw from end |
||
| 273 | $start = -1 * $len * ($seq + 1); |
||
| 274 | $seed = $key; |
||
| 275 | |||
| 276 | } elseif (5 == $rule{0}) { |
||
| 277 | // crc32 |
||
| 278 | if (3 < $seq) { |
||
| 279 | $seq = $seq % 3; |
||
| 280 | } |
||
| 281 | $start = $len * $seq; |
||
| 282 | $seed = hash('crc32', $key); |
||
| 283 | } |
||
| 284 | |||
| 285 | return substr($seed, $start, 2); |
||
| 286 | } |
||
| 287 | |||
| 288 | |||
| 289 | /** |
||
| 290 | * {@inheritdoc} |
||
| 291 | * |
||
| 292 | * File cache does not keep lifetime in cache, so it need a lifetime from |
||
| 293 | * outside, or use default lifetime config. |
||
| 294 | */ |
||
| 295 | public function isExpired($key, $lifetime = null) |
||
| 296 | { |
||
| 297 | $file = $this->getFilePath($key); |
||
| 298 | |||
| 299 | // File doesn't exist |
||
| 300 | if (!file_exists($file)) { |
||
| 301 | return true; |
||
| 302 | } |
||
| 303 | |||
| 304 | if (0 == $lifetime) { |
||
|
0 ignored issues
–
show
|
|||
| 305 | return false; |
||
| 306 | } |
||
| 307 | |||
| 308 | $expireTime = $this->computeExpireTime($lifetime, filemtime($file)); |
||
| 309 | |||
| 310 | return (time() > $expireTime); |
||
| 311 | } |
||
| 312 | |||
| 313 | |||
| 314 | /** |
||
| 315 | * {@inheritdoc} |
||
| 316 | */ |
||
| 317 | public function set($key, $val, $lifetime = null) |
||
| 318 | { |
||
| 319 | $file = $this->getFilePath($key); |
||
| 320 | |||
| 321 | // Create each level dir if not exists |
||
| 322 | $fileSystemUtil = $this->getUtilContainer()->getFileSystem(); |
||
| 323 | $dir = $fileSystemUtil->getDirName($file); |
||
| 324 | if (!file_exists($dir)) { |
||
| 325 | mkdir($dir, 0755, true); |
||
| 326 | } |
||
| 327 | |||
| 328 | // Finally write file |
||
| 329 | $result = file_put_contents($file, $val, LOCK_EX); |
||
| 330 | |||
| 331 | $this->log(OperateType::SET, $key, false !== $result); |
||
| 332 | |||
| 333 | return $this; |
||
| 334 | } |
||
| 335 | } |
||
| 336 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.