pictcode_admin / lib / Cake / Cache / Engine / FileEngine.php @ 5ad38a95
履歴 | 表示 | アノテート | ダウンロード (11.108 KB)
| 1 |
<?php
|
|---|---|
| 2 |
/**
|
| 3 |
* File Storage engine for cache. Filestorage is the slowest cache storage
|
| 4 |
* to read and write. However, it is good for servers that don't have other storage
|
| 5 |
* engine available, or have content which is not performance sensitive.
|
| 6 |
*
|
| 7 |
* You can configure a FileEngine cache, using Cache::config()
|
| 8 |
*
|
| 9 |
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
|
| 10 |
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
| 11 |
*
|
| 12 |
* Licensed under The MIT License
|
| 13 |
* For full copyright and license information, please see the LICENSE.txt
|
| 14 |
* Redistributions of files must retain the above copyright notice.
|
| 15 |
*
|
| 16 |
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
|
| 17 |
* @link http://cakephp.org CakePHP(tm) Project
|
| 18 |
* @since CakePHP(tm) v 1.2.0.4933
|
| 19 |
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
| 20 |
*/
|
| 21 |
|
| 22 |
/**
|
| 23 |
* File Storage engine for cache. Filestorage is the slowest cache storage
|
| 24 |
* to read and write. However, it is good for servers that don't have other storage
|
| 25 |
* engine available, or have content which is not performance sensitive.
|
| 26 |
*
|
| 27 |
* You can configure a FileEngine cache, using Cache::config()
|
| 28 |
*
|
| 29 |
* @package Cake.Cache.Engine
|
| 30 |
*/
|
| 31 |
class FileEngine extends CacheEngine { |
| 32 |
|
| 33 |
/**
|
| 34 |
* Instance of SplFileObject class
|
| 35 |
*
|
| 36 |
* @var File
|
| 37 |
*/
|
| 38 |
protected $_File = null; |
| 39 |
|
| 40 |
/**
|
| 41 |
* Settings
|
| 42 |
*
|
| 43 |
* - path = absolute path to cache directory, default => CACHE
|
| 44 |
* - prefix = string prefix for filename, default => cake_
|
| 45 |
* - lock = enable file locking on write, default => true
|
| 46 |
* - serialize = serialize the data, default => true
|
| 47 |
*
|
| 48 |
* @var array
|
| 49 |
* @see CacheEngine::__defaults
|
| 50 |
*/
|
| 51 |
public $settings = array(); |
| 52 |
|
| 53 |
/**
|
| 54 |
* True unless FileEngine::__active(); fails
|
| 55 |
*
|
| 56 |
* @var bool
|
| 57 |
*/
|
| 58 |
protected $_init = true; |
| 59 |
|
| 60 |
/**
|
| 61 |
* Initialize the Cache Engine
|
| 62 |
*
|
| 63 |
* Called automatically by the cache frontend
|
| 64 |
* To reinitialize the settings call Cache::engine('EngineName', [optional] settings = array());
|
| 65 |
*
|
| 66 |
* @param array $settings array of setting for the engine
|
| 67 |
* @return bool True if the engine has been successfully initialized, false if not
|
| 68 |
*/
|
| 69 |
public function init($settings = array()) { |
| 70 |
$settings += array( |
| 71 |
'engine' => 'File', |
| 72 |
'path' => CACHE, |
| 73 |
'prefix' => 'cake_', |
| 74 |
'lock' => true, |
| 75 |
'serialize' => true, |
| 76 |
'isWindows' => false, |
| 77 |
'mask' => 0664 |
| 78 |
); |
| 79 |
parent::init($settings); |
| 80 |
|
| 81 |
if (DS === '\\') { |
| 82 |
$this->settings['isWindows'] = true; |
| 83 |
} |
| 84 |
if (substr($this->settings['path'], -1) !== DS) { |
| 85 |
$this->settings['path'] .= DS; |
| 86 |
} |
| 87 |
if (!empty($this->_groupPrefix)) { |
| 88 |
$this->_groupPrefix = str_replace('_', DS, $this->_groupPrefix); |
| 89 |
} |
| 90 |
return $this->_active(); |
| 91 |
} |
| 92 |
|
| 93 |
/**
|
| 94 |
* Garbage collection. Permanently remove all expired and deleted data
|
| 95 |
*
|
| 96 |
* @param int $expires [optional] An expires timestamp, invalidating all data before.
|
| 97 |
* @return bool True if garbage collection was successful, false on failure
|
| 98 |
*/
|
| 99 |
public function gc($expires = null) { |
| 100 |
return $this->clear(true); |
| 101 |
} |
| 102 |
|
| 103 |
/**
|
| 104 |
* Write data for key into cache
|
| 105 |
*
|
| 106 |
* @param string $key Identifier for the data
|
| 107 |
* @param mixed $data Data to be cached
|
| 108 |
* @param int $duration How long to cache the data, in seconds
|
| 109 |
* @return bool True if the data was successfully cached, false on failure
|
| 110 |
*/
|
| 111 |
public function write($key, $data, $duration) { |
| 112 |
if ($data === '' || !$this->_init) { |
| 113 |
return false; |
| 114 |
} |
| 115 |
|
| 116 |
if ($this->_setKey($key, true) === false) { |
| 117 |
return false; |
| 118 |
} |
| 119 |
|
| 120 |
$lineBreak = "\n"; |
| 121 |
|
| 122 |
if ($this->settings['isWindows']) { |
| 123 |
$lineBreak = "\r\n"; |
| 124 |
} |
| 125 |
|
| 126 |
if (!empty($this->settings['serialize'])) { |
| 127 |
if ($this->settings['isWindows']) { |
| 128 |
$data = str_replace('\\', '\\\\\\\\', serialize($data)); |
| 129 |
} else {
|
| 130 |
$data = serialize($data); |
| 131 |
} |
| 132 |
} |
| 133 |
|
| 134 |
$expires = time() + $duration; |
| 135 |
$contents = $expires . $lineBreak . $data . $lineBreak; |
| 136 |
|
| 137 |
if ($this->settings['lock']) { |
| 138 |
$this->_File->flock(LOCK_EX); |
| 139 |
} |
| 140 |
|
| 141 |
$this->_File->rewind(); |
| 142 |
$success = $this->_File->ftruncate(0) && $this->_File->fwrite($contents) && $this->_File->fflush(); |
| 143 |
|
| 144 |
if ($this->settings['lock']) { |
| 145 |
$this->_File->flock(LOCK_UN); |
| 146 |
} |
| 147 |
|
| 148 |
return $success; |
| 149 |
} |
| 150 |
|
| 151 |
/**
|
| 152 |
* Read a key from the cache
|
| 153 |
*
|
| 154 |
* @param string $key Identifier for the data
|
| 155 |
* @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
|
| 156 |
*/
|
| 157 |
public function read($key) { |
| 158 |
if (!$this->_init || $this->_setKey($key) === false) { |
| 159 |
return false; |
| 160 |
} |
| 161 |
|
| 162 |
if ($this->settings['lock']) { |
| 163 |
$this->_File->flock(LOCK_SH); |
| 164 |
} |
| 165 |
|
| 166 |
$this->_File->rewind(); |
| 167 |
$time = time(); |
| 168 |
$cachetime = (int)$this->_File->current(); |
| 169 |
|
| 170 |
if ($cachetime !== false && ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime)) { |
| 171 |
if ($this->settings['lock']) { |
| 172 |
$this->_File->flock(LOCK_UN); |
| 173 |
} |
| 174 |
return false; |
| 175 |
} |
| 176 |
|
| 177 |
$data = ''; |
| 178 |
$this->_File->next(); |
| 179 |
while ($this->_File->valid()) { |
| 180 |
$data .= $this->_File->current(); |
| 181 |
$this->_File->next(); |
| 182 |
} |
| 183 |
|
| 184 |
if ($this->settings['lock']) { |
| 185 |
$this->_File->flock(LOCK_UN); |
| 186 |
} |
| 187 |
|
| 188 |
$data = trim($data); |
| 189 |
|
| 190 |
if ($data !== '' && !empty($this->settings['serialize'])) { |
| 191 |
if ($this->settings['isWindows']) { |
| 192 |
$data = str_replace('\\\\\\\\', '\\', $data); |
| 193 |
} |
| 194 |
$data = unserialize((string)$data); |
| 195 |
} |
| 196 |
return $data; |
| 197 |
} |
| 198 |
|
| 199 |
/**
|
| 200 |
* Delete a key from the cache
|
| 201 |
*
|
| 202 |
* @param string $key Identifier for the data
|
| 203 |
* @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed
|
| 204 |
*/
|
| 205 |
public function delete($key) { |
| 206 |
if ($this->_setKey($key) === false || !$this->_init) { |
| 207 |
return false; |
| 208 |
} |
| 209 |
$path = $this->_File->getRealPath(); |
| 210 |
$this->_File = null; |
| 211 |
|
| 212 |
//@codingStandardsIgnoreStart
|
| 213 |
return @unlink($path); |
| 214 |
//@codingStandardsIgnoreEnd
|
| 215 |
} |
| 216 |
|
| 217 |
/**
|
| 218 |
* Delete all values from the cache
|
| 219 |
*
|
| 220 |
* @param bool $check Optional - only delete expired cache items
|
| 221 |
* @return bool True if the cache was successfully cleared, false otherwise
|
| 222 |
*/
|
| 223 |
public function clear($check) { |
| 224 |
if (!$this->_init) { |
| 225 |
return false; |
| 226 |
} |
| 227 |
$this->_File = null; |
| 228 |
|
| 229 |
$threshold = $now = false; |
| 230 |
if ($check) { |
| 231 |
$now = time(); |
| 232 |
$threshold = $now - $this->settings['duration']; |
| 233 |
} |
| 234 |
|
| 235 |
$this->_clearDirectory($this->settings['path'], $now, $threshold); |
| 236 |
|
| 237 |
$directory = new RecursiveDirectoryIterator($this->settings['path']); |
| 238 |
$contents = new RecursiveIteratorIterator($directory, RecursiveIteratorIterator::SELF_FIRST); |
| 239 |
$cleared = array(); |
| 240 |
foreach ($contents as $path) { |
| 241 |
if ($path->isFile()) { |
| 242 |
continue;
|
| 243 |
} |
| 244 |
|
| 245 |
$path = $path->getRealPath() . DS; |
| 246 |
if (!in_array($path, $cleared)) { |
| 247 |
$this->_clearDirectory($path, $now, $threshold); |
| 248 |
$cleared[] = $path; |
| 249 |
} |
| 250 |
} |
| 251 |
return true; |
| 252 |
} |
| 253 |
|
| 254 |
/**
|
| 255 |
* Used to clear a directory of matching files.
|
| 256 |
*
|
| 257 |
* @param string $path The path to search.
|
| 258 |
* @param int $now The current timestamp
|
| 259 |
* @param int $threshold Any file not modified after this value will be deleted.
|
| 260 |
* @return void
|
| 261 |
*/
|
| 262 |
protected function _clearDirectory($path, $now, $threshold) { |
| 263 |
$prefixLength = strlen($this->settings['prefix']); |
| 264 |
|
| 265 |
if (!is_dir($path)) { |
| 266 |
return;
|
| 267 |
} |
| 268 |
|
| 269 |
$dir = dir($path); |
| 270 |
while (($entry = $dir->read()) !== false) { |
| 271 |
if (substr($entry, 0, $prefixLength) !== $this->settings['prefix']) { |
| 272 |
continue;
|
| 273 |
} |
| 274 |
|
| 275 |
try {
|
| 276 |
$file = new SplFileObject($path . $entry, 'r'); |
| 277 |
} catch (Exception $e) { |
| 278 |
continue;
|
| 279 |
} |
| 280 |
|
| 281 |
if ($threshold) { |
| 282 |
$mtime = $file->getMTime(); |
| 283 |
|
| 284 |
if ($mtime > $threshold) { |
| 285 |
continue;
|
| 286 |
} |
| 287 |
$expires = (int)$file->current(); |
| 288 |
|
| 289 |
if ($expires > $now) { |
| 290 |
continue;
|
| 291 |
} |
| 292 |
} |
| 293 |
if ($file->isFile()) { |
| 294 |
$filePath = $file->getRealPath(); |
| 295 |
$file = null; |
| 296 |
|
| 297 |
//@codingStandardsIgnoreStart
|
| 298 |
@unlink($filePath); |
| 299 |
//@codingStandardsIgnoreEnd
|
| 300 |
} |
| 301 |
} |
| 302 |
} |
| 303 |
|
| 304 |
/**
|
| 305 |
* Not implemented
|
| 306 |
*
|
| 307 |
* @param string $key The key to decrement
|
| 308 |
* @param int $offset The number to offset
|
| 309 |
* @return void
|
| 310 |
* @throws CacheException
|
| 311 |
*/
|
| 312 |
public function decrement($key, $offset = 1) { |
| 313 |
throw new CacheException(__d('cake_dev', 'Files cannot be atomically decremented.')); |
| 314 |
} |
| 315 |
|
| 316 |
/**
|
| 317 |
* Not implemented
|
| 318 |
*
|
| 319 |
* @param string $key The key to decrement
|
| 320 |
* @param int $offset The number to offset
|
| 321 |
* @return void
|
| 322 |
* @throws CacheException
|
| 323 |
*/
|
| 324 |
public function increment($key, $offset = 1) { |
| 325 |
throw new CacheException(__d('cake_dev', 'Files cannot be atomically incremented.')); |
| 326 |
} |
| 327 |
|
| 328 |
/**
|
| 329 |
* Sets the current cache key this class is managing, and creates a writable SplFileObject
|
| 330 |
* for the cache file the key is referring to.
|
| 331 |
*
|
| 332 |
* @param string $key The key
|
| 333 |
* @param bool $createKey Whether the key should be created if it doesn't exists, or not
|
| 334 |
* @return bool true if the cache key could be set, false otherwise
|
| 335 |
*/
|
| 336 |
protected function _setKey($key, $createKey = false) { |
| 337 |
$groups = null; |
| 338 |
if (!empty($this->_groupPrefix)) { |
| 339 |
$groups = vsprintf($this->_groupPrefix, $this->groups()); |
| 340 |
} |
| 341 |
$dir = $this->settings['path'] . $groups; |
| 342 |
|
| 343 |
if (!is_dir($dir)) { |
| 344 |
mkdir($dir, 0775, true); |
| 345 |
} |
| 346 |
$path = new SplFileInfo($dir . $key); |
| 347 |
|
| 348 |
if (!$createKey && !$path->isFile()) { |
| 349 |
return false; |
| 350 |
} |
| 351 |
if (empty($this->_File) || $this->_File->getBaseName() !== $key) { |
| 352 |
$exists = file_exists($path->getPathname()); |
| 353 |
try {
|
| 354 |
$this->_File = $path->openFile('c+'); |
| 355 |
} catch (Exception $e) { |
| 356 |
trigger_error($e->getMessage(), E_USER_WARNING); |
| 357 |
return false; |
| 358 |
} |
| 359 |
unset($path); |
| 360 |
|
| 361 |
if (!$exists && !chmod($this->_File->getPathname(), (int)$this->settings['mask'])) { |
| 362 |
trigger_error(__d(
|
| 363 |
'cake_dev', 'Could not apply permission mask "%s" on cache file "%s"', |
| 364 |
array($this->_File->getPathname(), $this->settings['mask'])), E_USER_WARNING); |
| 365 |
} |
| 366 |
} |
| 367 |
return true; |
| 368 |
} |
| 369 |
|
| 370 |
/**
|
| 371 |
* Determine is cache directory is writable
|
| 372 |
*
|
| 373 |
* @return bool
|
| 374 |
*/
|
| 375 |
protected function _active() { |
| 376 |
$dir = new SplFileInfo($this->settings['path']); |
| 377 |
if (Configure::read('debug')) { |
| 378 |
$path = $dir->getPathname(); |
| 379 |
if (!is_dir($path)) { |
| 380 |
mkdir($path, 0775, true); |
| 381 |
} |
| 382 |
} |
| 383 |
if ($this->_init && !($dir->isDir() && $dir->isWritable())) { |
| 384 |
$this->_init = false; |
| 385 |
trigger_error(__d('cake_dev', '%s is not writable', $this->settings['path']), E_USER_WARNING); |
| 386 |
return false; |
| 387 |
} |
| 388 |
return true; |
| 389 |
} |
| 390 |
|
| 391 |
/**
|
| 392 |
* Generates a safe key for use with cache engine storage engines.
|
| 393 |
*
|
| 394 |
* @param string $key the key passed over
|
| 395 |
* @return mixed string $key or false
|
| 396 |
*/
|
| 397 |
public function key($key) { |
| 398 |
if (empty($key)) { |
| 399 |
return false; |
| 400 |
} |
| 401 |
|
| 402 |
$key = Inflector::underscore(str_replace(array(DS, '/', '.', '<', '>', '?', ':', '|', '*', '"'), '_', strval($key))); |
| 403 |
return $key; |
| 404 |
} |
| 405 |
|
| 406 |
/**
|
| 407 |
* Recursively deletes all files under any directory named as $group
|
| 408 |
*
|
| 409 |
* @param string $group The group to clear.
|
| 410 |
* @return bool success
|
| 411 |
*/
|
| 412 |
public function clearGroup($group) { |
| 413 |
$this->_File = null; |
| 414 |
$directoryIterator = new RecursiveDirectoryIterator($this->settings['path']); |
| 415 |
$contents = new RecursiveIteratorIterator($directoryIterator, RecursiveIteratorIterator::CHILD_FIRST); |
| 416 |
foreach ($contents as $object) { |
| 417 |
$containsGroup = strpos($object->getPathName(), DS . $group . DS) !== false; |
| 418 |
$hasPrefix = true; |
| 419 |
if (strlen($this->settings['prefix']) !== 0) { |
| 420 |
$hasPrefix = strpos($object->getBaseName(), $this->settings['prefix']) === 0; |
| 421 |
} |
| 422 |
if ($object->isFile() && $containsGroup && $hasPrefix) { |
| 423 |
$path = $object->getPathName(); |
| 424 |
$object = null; |
| 425 |
//@codingStandardsIgnoreStart
|
| 426 |
@unlink($path); |
| 427 |
//@codingStandardsIgnoreEnd
|
| 428 |
} |
| 429 |
} |
| 430 |
return true; |
| 431 |
} |
| 432 |
} |