Skip to content

Commit 14bcd79

Browse files
trakosnicolas-grekas
authored andcommitted
[Cache] Added PhpFilesAdapter
1 parent c71868d commit 14bcd79

File tree

6 files changed

+661
-60
lines changed

6 files changed

+661
-60
lines changed

src/Symfony/Component/Cache/Adapter/FilesystemAdapter.php

Lines changed: 28 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -11,43 +11,27 @@
1111

1212
namespace Symfony\Component\Cache\Adapter;
1313

14-
use Symfony\Component\Cache\Exception\InvalidArgumentException;
14+
use Symfony\Component\Cache\Adapter\Helper\FilesCacheHelper;
1515

1616
/**
1717
* @author Nicolas Grekas <[email protected]>
1818
*/
1919
class FilesystemAdapter extends AbstractAdapter
2020
{
21-
private $directory;
21+
/**
22+
* @var FilesCacheHelper
23+
*/
24+
protected $filesCacheHelper;
2225

26+
/**
27+
* @param string $namespace Cache namespace
28+
* @param int $defaultLifetime Default lifetime for cache items
29+
* @param null $directory Path where cache items should be stored, defaults to sys_get_temp_dir().'/symfony-cache'
30+
*/
2331
public function __construct($namespace = '', $defaultLifetime = 0, $directory = null)
2432
{
25-
parent::__construct('', $defaultLifetime);
26-
27-
if (!isset($directory[0])) {
28-
$directory = sys_get_temp_dir().'/symfony-cache';
29-
}
30-
if (isset($namespace[0])) {
31-
if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) {
32-
throw new InvalidArgumentException(sprintf('FilesystemAdapter namespace contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0]));
33-
}
34-
$directory .= '/'.$namespace;
35-
}
36-
if (!file_exists($dir = $directory.'/.')) {
37-
@mkdir($directory, 0777, true);
38-
}
39-
if (false === $dir = realpath($dir)) {
40-
throw new InvalidArgumentException(sprintf('Cache directory does not exist (%s)', $directory));
41-
}
42-
if (!is_writable($dir .= DIRECTORY_SEPARATOR)) {
43-
throw new InvalidArgumentException(sprintf('Cache directory is not writable (%s)', $directory));
44-
}
45-
// On Windows the whole path is limited to 258 chars
46-
if ('\\' === DIRECTORY_SEPARATOR && strlen($dir) > 234) {
47-
throw new InvalidArgumentException(sprintf('Cache directory too long (%s)', $directory));
48-
}
49-
50-
$this->directory = $dir;
33+
parent::__construct($namespace, $defaultLifetime);
34+
$this->filesCacheHelper = new FilesCacheHelper($directory, $namespace);
5135
}
5236

5337
/**
@@ -59,7 +43,7 @@ protected function doFetch(array $ids)
5943
$now = time();
6044

6145
foreach ($ids as $id) {
62-
$file = $this->getFile($id);
46+
$file = $this->filesCacheHelper->getFilePath($id);
6347
if (!$h = @fopen($file, 'rb')) {
6448
continue;
6549
}
@@ -86,7 +70,7 @@ protected function doFetch(array $ids)
8670
*/
8771
protected function doHave($id)
8872
{
89-
$file = $this->getFile($id);
73+
$file = $this->filesCacheHelper->getFilePath($id);
9074

9175
return file_exists($file) && (@filemtime($file) > time() || $this->doFetch(array($id)));
9276
}
@@ -97,8 +81,9 @@ protected function doHave($id)
9781
protected function doClear($namespace)
9882
{
9983
$ok = true;
84+
$directory = $this->filesCacheHelper->getDirectory();
10085

101-
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS)) as $file) {
86+
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS)) as $file) {
10287
$ok = ($file->isDir() || @unlink($file) || !file_exists($file)) && $ok;
10388
}
10489

@@ -113,7 +98,7 @@ protected function doDelete(array $ids)
11398
$ok = true;
11499

115100
foreach ($ids as $id) {
116-
$file = $this->getFile($id);
101+
$file = $this->filesCacheHelper->getFilePath($id);
117102
$ok = (!file_exists($file) || @unlink($file) || !file_exists($file)) && $ok;
118103
}
119104

@@ -127,32 +112,24 @@ protected function doSave(array $values, $lifetime)
127112
{
128113
$ok = true;
129114
$expiresAt = $lifetime ? time() + $lifetime : PHP_INT_MAX;
130-
$tmp = $this->directory.uniqid('', true);
131115

132116
foreach ($values as $id => $value) {
133-
$file = $this->getFile($id, true);
134-
135-
$value = $expiresAt."\n".rawurlencode($id)."\n".serialize($value);
136-
if (false !== @file_put_contents($tmp, $value)) {
137-
@touch($tmp, $expiresAt);
138-
$ok = @rename($tmp, $file) && $ok;
139-
} else {
140-
$ok = false;
141-
}
117+
$fileContent = $this->createCacheFileContent($id, $value, $expiresAt);
118+
$ok = $this->filesCacheHelper->saveFileForId($id, $fileContent, $expiresAt) && $ok;
142119
}
143120

144121
return $ok;
145122
}
146123

147-
private function getFile($id, $mkdir = false)
124+
/**
125+
* @param string $id
126+
* @param mixed $value
127+
* @param int $expiresAt
128+
*
129+
* @return string
130+
*/
131+
protected function createCacheFileContent($id, $value, $expiresAt)
148132
{
149-
$hash = str_replace('/', '-', base64_encode(md5($id, true)));
150-
$dir = $this->directory.$hash[0].DIRECTORY_SEPARATOR.$hash[1].DIRECTORY_SEPARATOR;
151-
152-
if ($mkdir && !file_exists($dir)) {
153-
@mkdir($dir, 0777, true);
154-
}
155-
156-
return $dir.substr($hash, 2, -2);
133+
return $expiresAt."\n".rawurlencode($id)."\n".serialize($value);
157134
}
158135
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Cache\Adapter\Helper;
13+
14+
use Symfony\Component\Cache\Exception\InvalidArgumentException;
15+
16+
class FilesCacheHelper
17+
{
18+
/**
19+
* @var string
20+
*/
21+
private $fileSuffix;
22+
23+
/**
24+
* @var string
25+
*/
26+
private $directory;
27+
28+
/**
29+
* @param string $directory Path where cache items should be stored, defaults to sys_get_temp_dir().'/symfony-cache'
30+
* @param string $namespace Cache namespace
31+
* @param string $version Version (works the same way as namespace)
32+
* @param string $fileSuffix Suffix that will be appended to all file names
33+
*/
34+
public function __construct($directory = null, $namespace = null, $version = null, $fileSuffix = '')
35+
{
36+
if (!isset($directory[0])) {
37+
$directory = sys_get_temp_dir().'/symfony-cache';
38+
}
39+
if (isset($namespace[0])) {
40+
if (preg_match('#[^-+_.A-Za-z0-9]#', $namespace, $match)) {
41+
throw new InvalidArgumentException(sprintf('Cache namespace for filesystem cache contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0]));
42+
}
43+
$directory .= '/'.$namespace;
44+
}
45+
if (isset($version[0])) {
46+
if (preg_match('#[^-+_.A-Za-z0-9]#', $version, $match)) {
47+
throw new InvalidArgumentException(sprintf('Cache version contains "%s" but only characters in [-+_.A-Za-z0-9] are allowed.', $match[0]));
48+
}
49+
$directory .= '/'.$version;
50+
}
51+
if (!file_exists($dir = $directory.'/.')) {
52+
@mkdir($directory, 0777, true);
53+
}
54+
if (false === $dir = realpath($dir)) {
55+
throw new InvalidArgumentException(sprintf('Cache directory does not exist (%s)', $directory));
56+
}
57+
if (!is_writable($dir .= DIRECTORY_SEPARATOR)) {
58+
throw new InvalidArgumentException(sprintf('Cache directory is not writable (%s)', $directory));
59+
}
60+
// On Windows the whole path is limited to 258 chars
61+
if ('\\' === DIRECTORY_SEPARATOR && strlen($dir) + strlen($fileSuffix) > 234) {
62+
throw new InvalidArgumentException(sprintf('Cache directory too long (%s)', $directory));
63+
}
64+
65+
$this->fileSuffix = $fileSuffix;
66+
$this->directory = $dir;
67+
}
68+
69+
/**
70+
* Returns root cache directory.
71+
*
72+
* @return string
73+
*/
74+
public function getDirectory()
75+
{
76+
return $this->directory;
77+
}
78+
79+
/**
80+
* Saves entry in cache.
81+
*
82+
* @param string $id Id of the cache entry (used for obtaining file path to write to).
83+
* @param string $fileContent Content to write to cache file
84+
* @param int|null $modificationTime If this is not-null it will be passed to touch()
85+
*
86+
* @return bool
87+
*/
88+
public function saveFileForId($id, $fileContent, $modificationTime = null)
89+
{
90+
$file = $this->getFilePath($id, true);
91+
92+
return $this->saveFile($file, $fileContent, $modificationTime);
93+
}
94+
95+
/**
96+
* Saves entry in cache.
97+
*
98+
* @param string $file File path to cache entry.
99+
* @param string $fileContent Content to write to cache file
100+
* @param int|null $modificationTime If this is not-null it will be passed to touch()
101+
*
102+
* @return bool
103+
*/
104+
public function saveFile($file, $fileContent, $modificationTime = null)
105+
{
106+
$temporaryFile = $this->directory.uniqid('', true);
107+
if (false === @file_put_contents($temporaryFile, $fileContent)) {
108+
return false;
109+
}
110+
111+
if (null !== $modificationTime) {
112+
@touch($temporaryFile, $modificationTime);
113+
}
114+
115+
return @rename($temporaryFile, $file);
116+
}
117+
118+
/**
119+
* Returns file path to cache entry.
120+
*
121+
* @param string $id Cache entry id.
122+
* @param bool $mkdir Whether to create necessary directories before returning file path.
123+
*
124+
* @return string
125+
*/
126+
public function getFilePath($id, $mkdir = false)
127+
{
128+
$hash = str_replace('/', '-', base64_encode(md5($id, true)));
129+
$dir = $this->directory.$hash[0].DIRECTORY_SEPARATOR.$hash[1].DIRECTORY_SEPARATOR;
130+
131+
if ($mkdir && !file_exists($dir)) {
132+
@mkdir($dir, 0777, true);
133+
}
134+
135+
return $dir.substr($hash, 2, -2).$this->fileSuffix;
136+
}
137+
}

0 commit comments

Comments
 (0)