Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 98ea7b1

Browse filesBrowse files
[Cache] fix memory leak when using PhpFilesAdapter
1 parent 9a025b4 commit 98ea7b1
Copy full SHA for 98ea7b1

File tree

3 files changed

+58
-12
lines changed
Filter options

3 files changed

+58
-12
lines changed
+31Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Tests\Adapter;
13+
14+
use Psr\Cache\CacheItemPoolInterface;
15+
use Symfony\Component\Cache\Adapter\PhpFilesAdapter;
16+
17+
/**
18+
* @group time-sensitive
19+
*/
20+
class PhpFilesAdapterAppendOnlyTest extends PhpFilesAdapterTest
21+
{
22+
protected $skippedTests = [
23+
'testDefaultLifeTime' => 'PhpFilesAdapter does not allow configuring a default lifetime.',
24+
'testExpiration' => 'PhpFilesAdapter in append-only mode does not expiration.',
25+
];
26+
27+
public function createCachePool(): CacheItemPoolInterface
28+
{
29+
return new PhpFilesAdapter('sf-cache', 0, null, true);
30+
}
31+
}

‎src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php
+3-3Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ trait FilesystemCommonTrait
2626
private function init($namespace, $directory)
2727
{
2828
if (!isset($directory[0])) {
29-
$directory = sys_get_temp_dir().'/symfony-cache';
29+
$directory = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'symfony-cache';
3030
} else {
3131
$directory = realpath($directory) ?: $directory;
3232
}
@@ -55,8 +55,8 @@ protected function doClear($namespace)
5555
{
5656
$ok = true;
5757

58-
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS)) as $file) {
59-
$ok = ($file->isDir() || $this->doUnlink($file) || !file_exists($file)) && $ok;
58+
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
59+
$ok = ($this->doUnlink($file) || !file_exists($file)) && $ok;
6060
}
6161

6262
return $ok;

‎src/Symfony/Component/Cache/Traits/PhpFilesTrait.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Traits/PhpFilesTrait.php
+24-9Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,13 @@ trait PhpFilesTrait
3535
private $files = [];
3636

3737
private static $startTime;
38+
private static $valuesCache = [];
3839

3940
public static function isSupported()
4041
{
4142
self::$startTime = self::$startTime ?? $_SERVER['REQUEST_TIME'] ?? time();
4243

43-
return \function_exists('opcache_invalidate') && ('cli' !== \PHP_SAPI || filter_var(ini_get('opcache.enable_cli'), FILTER_VALIDATE_BOOLEAN)) && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN);
44+
return \function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) || filter_var(ini_get('opcache.enable_cli'), FILTER_VALIDATE_BOOLEAN));
4445
}
4546

4647
/**
@@ -54,7 +55,7 @@ public function prune()
5455

5556
set_error_handler($this->includeHandler);
5657
try {
57-
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
58+
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::CURRENT_AS_PATHNAME), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
5859
try {
5960
if (\is_array($expiresAt = include $file)) {
6061
$expiresAt = $expiresAt[0];
@@ -100,7 +101,6 @@ protected function doFetch(array $ids)
100101
} elseif (!\is_object($value)) {
101102
$values[$id] = $value;
102103
} elseif (!$value instanceof LazyValue) {
103-
// calling a Closure is for @deprecated BC and should be removed in Symfony 5.0
104104
$values[$id] = $value();
105105
} elseif (false === $values[$id] = include $value->file) {
106106
unset($values[$id], $this->values[$id]);
@@ -123,14 +123,18 @@ protected function doFetch(array $ids)
123123
try {
124124
$file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
125125

126-
if (\is_array($expiresAt = include $file)) {
126+
$expiresAt = $this->appendOnly && isset(self::$valuesCache[$file]) ? self::$valuesCache[$file] : include $file;
127+
128+
if (\is_array($expiresAt)) {
127129
[$expiresAt, $this->values[$id]] = $expiresAt;
128130
} elseif ($now < $expiresAt) {
129131
$this->values[$id] = new LazyValue($file);
130132
}
131133

132134
if ($now >= $expiresAt) {
133-
unset($this->values[$id], $missingIds[$k]);
135+
unset($this->values[$id], $missingIds[$k], self::$valuesCache[$file]);
136+
} elseif ($this->appendOnly && !isset(self::$valuesCache[$file]) && !$this->values[$id] instanceof LazyValue) {
137+
self::$valuesCache[$file] = [$expiresAt, $this->values[$id]];
134138
}
135139
} catch (\ErrorException $e) {
136140
unset($missingIds[$k]);
@@ -159,7 +163,9 @@ protected function doHave($id)
159163
$file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
160164
$getExpiry = true;
161165

162-
if (\is_array($expiresAt = include $file)) {
166+
$expiresAt = $this->appendOnly && isset(self::$valuesCache[$file]) ? self::$valuesCache[$file] : include $file;
167+
168+
if (\is_array($expiresAt)) {
163169
[$expiresAt, $value] = $expiresAt;
164170
} elseif ($this->appendOnly) {
165171
$value = new LazyValue($file);
@@ -172,6 +178,10 @@ protected function doHave($id)
172178
if ($this->appendOnly) {
173179
$now = 0;
174180
$this->values[$id] = $value;
181+
182+
if ($this->appendOnly && !isset(self::$valuesCache[$file]) && !$value instanceof LazyValue) {
183+
self::$valuesCache[$file] = [$expiresAt, $value];
184+
}
175185
} else {
176186
$now = time();
177187
}
@@ -211,12 +221,14 @@ protected function doSave(array $values, $lifetime)
211221
$value = var_export($value, true);
212222
}
213223

214-
if (!$isStaticValue) {
224+
if ($isStaticValue) {
225+
$value = "<?php return [{$expiry}, {$value}];\n";
226+
} elseif ($this->appendOnly) {
227+
$value = "<?php return [{$expiry}, static function () { return {$value}; }];\n";
228+
} else {
215229
// We cannot use a closure here because of https://bugs.php.net/76982
216230
$value = str_replace('\Symfony\Component\VarExporter\Internal\\', '', $value);
217231
$value = "<?php\n\nnamespace Symfony\Component\VarExporter\Internal;\n\nreturn \$getExpiry ? {$expiry} : {$value};\n";
218-
} else {
219-
$value = "<?php return [{$expiry}, {$value}];\n";
220232
}
221233

222234
$file = $this->files[$key] = $this->getFile($key, true);
@@ -227,6 +239,7 @@ protected function doSave(array $values, $lifetime)
227239
@opcache_invalidate($file, true);
228240
@opcache_compile_file($file);
229241
}
242+
unset(self::$valuesCache[$file]);
230243
}
231244

232245
if (!$ok && !is_writable($this->directory)) {
@@ -260,6 +273,8 @@ protected function doDelete(array $ids)
260273

261274
protected function doUnlink($file)
262275
{
276+
unset(self::$valuesCache[$file]);
277+
263278
if (self::isSupported()) {
264279
@opcache_invalidate($file, true);
265280
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.