diff --git a/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php b/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php new file mode 100644 index 0000000000000..db6d9ce4cf4e7 --- /dev/null +++ b/src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php @@ -0,0 +1,145 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Adapter; + +use Symfony\Component\Cache\Exception\InvalidArgumentException; + +/** + * @author Nicolas Grekas
+ */ +class FilesystemAdapter extends AbstractAdapter +{ + private $directory; + + public function __construct($directory, $defaultLifetime = null) + { + parent::__construct('', $defaultLifetime); + + if (!file_exists($dir = $directory.'/.')) { + @mkdir($directory, 0777, true); + } + if (false === $dir = realpath($dir)) { + throw new InvalidArgumentException(sprintf('Cache directory does not exist (%s)', $directory)); + } + if (!is_writable($dir .= DIRECTORY_SEPARATOR)) { + throw new InvalidArgumentException(sprintf('Cache directory is not writable (%s)', $directory)); + } + // On Windows the whole path is limited to 258 chars + if ('\\' === DIRECTORY_SEPARATOR && strlen($dir) > 190) { + throw new InvalidArgumentException(sprintf('Cache directory too long (%s)', $directory)); + } + + $this->directory = $dir; + } + + /** + * {@inheritdoc} + */ + protected function doFetch(array $ids) + { + $values = array(); + $now = time(); + + foreach ($ids as $id) { + $file = $this->getFile($id); + if (!$h = @fopen($file, 'rb')) { + continue; + } + flock($h, LOCK_SH); + if ($now >= (int) $expiresAt = fgets($h)) { + flock($h, LOCK_UN); + fclose($h); + if (isset($expiresAt[0])) { + @unlink($file); + } + } else { + $value = stream_get_contents($h); + flock($h, LOCK_UN); + fclose($h); + $values[$id] = unserialize($value); + } + } + + return $values; + } + + /** + * {@inheritdoc} + */ + protected function doHave($id) + { + $file = $this->getFile($id); + + return file_exists($file) && (@filemtime($file) > time() || $this->doFetch(array($id))); + } + + /** + * {@inheritdoc} + */ + protected function doClear($namespace) + { + $ok = true; + + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS)) as $file) { + $ok = ($file->isDir() || @unlink($file) || !file_exists($file)) && $ok; + } + + return $ok; + } + + /** + * {@inheritdoc} + */ + protected function doDelete(array $ids) + { + $ok = true; + + foreach ($ids as $id) { + $file = $this->getFile($id); + $ok = (!file_exists($file) || @unlink($file) || !file_exists($file)) && $ok; + } + + return $ok; + } + + /** + * {@inheritdoc} + */ + protected function doSave(array $values, $lifetime) + { + $ok = true; + $expiresAt = $lifetime ? time() + $lifetime : PHP_INT_MAX; + + foreach ($values as $id => $value) { + $file = $this->getFile($id); + $dir = dirname($file); + if (!file_exists($dir)) { + @mkdir($dir, 0777, true); + } + $value = $expiresAt."\n".serialize($value); + if (false !== @file_put_contents($file, $value, LOCK_EX)) { + @touch($file, $expiresAt); + } else { + $ok = false; + } + } + + return $ok; + } + + private function getFile($id) + { + $hash = hash('sha256', $id); + + return $this->directory.$hash[0].DIRECTORY_SEPARATOR.$hash[1].DIRECTORY_SEPARATOR.$hash; + } +} diff --git a/src/Symfony/Component/Cache/Tests/Adapter/FilesystemTest.php b/src/Symfony/Component/Cache/Tests/Adapter/FilesystemTest.php new file mode 100644 index 0000000000000..28786501a86ec --- /dev/null +++ b/src/Symfony/Component/Cache/Tests/Adapter/FilesystemTest.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Cache\Tests\Adapter; + +use Cache\IntegrationTests\CachePoolTest; +use Symfony\Component\Cache\Adapter\FilesystemAdapter; + +/** + * @group time-sensitive + */ +class FilesystemAdapterTest extends CachePoolTest +{ + public function createCachePool() + { + if (defined('HHVM_VERSION')) { + $this->skippedTests['testDeferredSaveWithoutCommit'] = 'Fails on HHVM'; + } + + return new FilesystemAdapter(sys_get_temp_dir().DIRECTORY_SEPARATOR.'sf-cache'); + } +}
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: