Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0b46226

Browse files
[Cache] fix memory leak when using PhpFilesAdapter
1 parent 9a025b4 commit 0b46226

File tree

3 files changed

+60
-12
lines changed

3 files changed

+60
-12
lines changed
Lines 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 <[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\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

Lines 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

Lines 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)