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 5aff4a6

Browse filesBrowse files
bug #34896 [Cache] fix memory leak when using PhpFilesAdapter (nicolas-grekas)
This PR was merged into the 4.3 branch. Discussion ---------- [Cache] fix memory leak when using PhpFilesAdapter | Q | A | ------------- | --- | Branch? | 4.3 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | Fix #34687 | License | MIT | Doc PR | - Similar to #34839 but for `PhpFilesAdapter`, as the "appendOnly" mode is a v4-only feature. Commits ------- 0b46226 [Cache] fix memory leak when using PhpFilesAdapter
2 parents a492e72 + 0b46226 commit 5aff4a6
Copy full SHA for 5aff4a6

File tree

3 files changed

+60
-12
lines changed
Filter options

3 files changed

+60
-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
+26-9Lines changed: 26 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,20 @@ 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+
if (isset(self::$valuesCache[$file])) {
127+
[$expiresAt, $this->values[$id]] = self::$valuesCache[$file];
128+
} elseif (\is_array($expiresAt = include $file)) {
129+
if ($this->appendOnly) {
130+
self::$valuesCache[$file] = $expiresAt;
131+
}
132+
127133
[$expiresAt, $this->values[$id]] = $expiresAt;
128134
} elseif ($now < $expiresAt) {
129135
$this->values[$id] = new LazyValue($file);
130136
}
131137

132138
if ($now >= $expiresAt) {
133-
unset($this->values[$id], $missingIds[$k]);
139+
unset($this->values[$id], $missingIds[$k], self::$valuesCache[$file]);
134140
}
135141
} catch (\ErrorException $e) {
136142
unset($missingIds[$k]);
@@ -159,7 +165,13 @@ protected function doHave($id)
159165
$file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
160166
$getExpiry = true;
161167

162-
if (\is_array($expiresAt = include $file)) {
168+
if (isset(self::$valuesCache[$file])) {
169+
[$expiresAt, $value] = self::$valuesCache[$file];
170+
} elseif (\is_array($expiresAt = include $file)) {
171+
if ($this->appendOnly) {
172+
self::$valuesCache[$file] = $expiresAt;
173+
}
174+
163175
[$expiresAt, $value] = $expiresAt;
164176
} elseif ($this->appendOnly) {
165177
$value = new LazyValue($file);
@@ -211,12 +223,14 @@ protected function doSave(array $values, $lifetime)
211223
$value = var_export($value, true);
212224
}
213225

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

222236
$file = $this->files[$key] = $this->getFile($key, true);
@@ -227,6 +241,7 @@ protected function doSave(array $values, $lifetime)
227241
@opcache_invalidate($file, true);
228242
@opcache_compile_file($file);
229243
}
244+
unset(self::$valuesCache[$file]);
230245
}
231246

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

261276
protected function doUnlink($file)
262277
{
278+
unset(self::$valuesCache[$file]);
279+
263280
if (self::isSupported()) {
264281
@opcache_invalidate($file, true);
265282
}

0 commit comments

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