Skip to content

Commit bb191cf

Browse files
[Cache] work aroung PHP memory leak
1 parent 2fdfa1a commit bb191cf

File tree

1 file changed

+49
-8
lines changed

1 file changed

+49
-8
lines changed

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

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,15 @@ public function prune()
5050
{
5151
$time = time();
5252
$pruned = true;
53+
$getExpiry = true;
5354

5455
set_error_handler($this->includeHandler);
5556
try {
5657
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
5758
try {
58-
list($expiresAt) = include $file;
59+
if (\is_array($expiresAt = include $file)) {
60+
$expiresAt = $expiresAt[0];
61+
}
5962
} catch (\ErrorException $e) {
6063
$expiresAt = $time;
6164
}
@@ -87,13 +90,21 @@ protected function doFetch(array $ids)
8790
$values = [];
8891

8992
begin:
93+
$getExpiry = false;
94+
9095
foreach ($ids as $id) {
9196
if (null === $value = $this->values[$id] ?? null) {
9297
$missingIds[] = $id;
9398
} elseif ('N;' === $value) {
9499
$values[$id] = null;
95-
} elseif ($value instanceof \Closure) {
96-
$values[$id] = $value();
100+
} elseif (\is_object($value)) {
101+
if (!$value instanceof LazyValue) {
102+
// calling a Closure is for @deprecated BC and should be removed in Symfony 5.0
103+
$values[$id] = $value();
104+
} elseif (false === $values[$id] = include $value->file) {
105+
unset($values[$id], $this->values[$id]);
106+
$missingIds[] = $id;
107+
}
97108
} else {
98109
$values[$id] = $value;
99110
}
@@ -108,10 +119,18 @@ protected function doFetch(array $ids)
108119

109120
set_error_handler($this->includeHandler);
110121
try {
122+
$getExpiry = true;
123+
111124
foreach ($missingIds as $k => $id) {
112125
try {
113126
$file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
114-
list($expiresAt, $this->values[$id]) = include $file;
127+
128+
if (\is_array($expiresAt = include $file)) {
129+
[$expiresAt, $this->values[$id]] = $expiresAt;
130+
} elseif ($now < $expiresAt) {
131+
$this->values[$id] = new LazyValue($file);
132+
}
133+
115134
if ($now >= $expiresAt) {
116135
unset($this->values[$id], $missingIds[$k]);
117136
}
@@ -140,7 +159,13 @@ protected function doHave($id)
140159
set_error_handler($this->includeHandler);
141160
try {
142161
$file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
143-
list($expiresAt, $value) = include $file;
162+
$getExpiry = true;
163+
164+
if (\is_array($expiresAt = include $file)) {
165+
[$expiresAt, $value] = $expiresAt;
166+
} elseif ($this->appendOnly) {
167+
$value = new LazyValue($file);
168+
}
144169
} catch (\ErrorException $e) {
145170
return false;
146171
} finally {
@@ -189,13 +214,16 @@ protected function doSave(array $values, $lifetime)
189214
}
190215

191216
if (!$isStaticValue) {
192-
$value = str_replace("\n", "\n ", $value);
193-
$value = "static function () {\n\n return {$value};\n\n}";
217+
// We cannot use a closure here because of https://bugs.php.net/76982
218+
$value = str_replace('\Symfony\Component\VarExporter\Internal\\', '', $value);
219+
$value = "<?php\n\nnamespace Symfony\Component\VarExporter\Internal;\n\nreturn \$getExpiry ? {$expiry} : {$value};\n";
220+
} else {
221+
$value = "<?php return [{$expiry}, {$value}];\n";
194222
}
195223

196224
$file = $this->files[$key] = $this->getFile($key, true);
197225
// Since OPcache only compiles files older than the script execution start, set the file's mtime in the past
198-
$ok = $this->write($file, "<?php return [{$expiry}, {$value}];\n", self::$startTime - 10) && $ok;
226+
$ok = $this->write($file, $value, self::$startTime - 10) && $ok;
199227

200228
if ($allowCompile) {
201229
@opcache_invalidate($file, true);
@@ -241,3 +269,16 @@ protected function doUnlink($file)
241269
return @unlink($file);
242270
}
243271
}
272+
273+
/**
274+
* @internal
275+
*/
276+
class LazyValue
277+
{
278+
public $file;
279+
280+
public function __construct($file)
281+
{
282+
$this->file = $file;
283+
}
284+
}

0 commit comments

Comments
 (0)