bug #32236 [Cache] work aroung PHP memory leak (nicolas-grekas)
This PR was merged into the 4.2 branch.
Discussion
----------
[Cache] work aroung PHP memory leak
| Q | A
| ------------- | ---
| Branch? | 4.2
| Bug fix? | yes
| New feature? | no
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| Fixed tickets | #32220
| License | MIT
| Doc PR | -
Works around https://bugs.php.net/76982, as identified by @goetas in the linked issue.
Commits
-------
5d55b91fae
[Cache] work aroung PHP memory leak
This commit is contained in:
commit
3f165064eb
@ -50,12 +50,15 @@ trait PhpFilesTrait
|
|||||||
{
|
{
|
||||||
$time = time();
|
$time = time();
|
||||||
$pruned = true;
|
$pruned = true;
|
||||||
|
$getExpiry = true;
|
||||||
|
|
||||||
set_error_handler($this->includeHandler);
|
set_error_handler($this->includeHandler);
|
||||||
try {
|
try {
|
||||||
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
|
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
|
||||||
try {
|
try {
|
||||||
list($expiresAt) = include $file;
|
if (\is_array($expiresAt = include $file)) {
|
||||||
|
$expiresAt = $expiresAt[0];
|
||||||
|
}
|
||||||
} catch (\ErrorException $e) {
|
} catch (\ErrorException $e) {
|
||||||
$expiresAt = $time;
|
$expiresAt = $time;
|
||||||
}
|
}
|
||||||
@ -87,15 +90,21 @@ trait PhpFilesTrait
|
|||||||
$values = [];
|
$values = [];
|
||||||
|
|
||||||
begin:
|
begin:
|
||||||
|
$getExpiry = false;
|
||||||
|
|
||||||
foreach ($ids as $id) {
|
foreach ($ids as $id) {
|
||||||
if (null === $value = $this->values[$id] ?? null) {
|
if (null === $value = $this->values[$id] ?? null) {
|
||||||
$missingIds[] = $id;
|
$missingIds[] = $id;
|
||||||
} elseif ('N;' === $value) {
|
} elseif ('N;' === $value) {
|
||||||
$values[$id] = null;
|
$values[$id] = null;
|
||||||
} elseif ($value instanceof \Closure) {
|
} elseif (!\is_object($value)) {
|
||||||
$values[$id] = $value();
|
|
||||||
} else {
|
|
||||||
$values[$id] = $value;
|
$values[$id] = $value;
|
||||||
|
} elseif (!$value instanceof LazyValue) {
|
||||||
|
// calling a Closure is for @deprecated BC and should be removed in Symfony 5.0
|
||||||
|
$values[$id] = $value();
|
||||||
|
} elseif (false === $values[$id] = include $value->file) {
|
||||||
|
unset($values[$id], $this->values[$id]);
|
||||||
|
$missingIds[] = $id;
|
||||||
}
|
}
|
||||||
if (!$this->appendOnly) {
|
if (!$this->appendOnly) {
|
||||||
unset($this->values[$id]);
|
unset($this->values[$id]);
|
||||||
@ -108,10 +117,18 @@ trait PhpFilesTrait
|
|||||||
|
|
||||||
set_error_handler($this->includeHandler);
|
set_error_handler($this->includeHandler);
|
||||||
try {
|
try {
|
||||||
|
$getExpiry = true;
|
||||||
|
|
||||||
foreach ($missingIds as $k => $id) {
|
foreach ($missingIds as $k => $id) {
|
||||||
try {
|
try {
|
||||||
$file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
|
$file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
|
||||||
list($expiresAt, $this->values[$id]) = include $file;
|
|
||||||
|
if (\is_array($expiresAt = include $file)) {
|
||||||
|
[$expiresAt, $this->values[$id]] = $expiresAt;
|
||||||
|
} elseif ($now < $expiresAt) {
|
||||||
|
$this->values[$id] = new LazyValue($file);
|
||||||
|
}
|
||||||
|
|
||||||
if ($now >= $expiresAt) {
|
if ($now >= $expiresAt) {
|
||||||
unset($this->values[$id], $missingIds[$k]);
|
unset($this->values[$id], $missingIds[$k]);
|
||||||
}
|
}
|
||||||
@ -140,7 +157,13 @@ trait PhpFilesTrait
|
|||||||
set_error_handler($this->includeHandler);
|
set_error_handler($this->includeHandler);
|
||||||
try {
|
try {
|
||||||
$file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
|
$file = $this->files[$id] ?? $this->files[$id] = $this->getFile($id);
|
||||||
list($expiresAt, $value) = include $file;
|
$getExpiry = true;
|
||||||
|
|
||||||
|
if (\is_array($expiresAt = include $file)) {
|
||||||
|
[$expiresAt, $value] = $expiresAt;
|
||||||
|
} elseif ($this->appendOnly) {
|
||||||
|
$value = new LazyValue($file);
|
||||||
|
}
|
||||||
} catch (\ErrorException $e) {
|
} catch (\ErrorException $e) {
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
@ -189,13 +212,16 @@ trait PhpFilesTrait
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!$isStaticValue) {
|
if (!$isStaticValue) {
|
||||||
$value = str_replace("\n", "\n ", $value);
|
// We cannot use a closure here because of https://bugs.php.net/76982
|
||||||
$value = "static function () {\n\n return {$value};\n\n}";
|
$value = str_replace('\Symfony\Component\VarExporter\Internal\\', '', $value);
|
||||||
|
$value = "<?php\n\nnamespace Symfony\Component\VarExporter\Internal;\n\nreturn \$getExpiry ? {$expiry} : {$value};\n";
|
||||||
|
} else {
|
||||||
|
$value = "<?php return [{$expiry}, {$value}];\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
$file = $this->files[$key] = $this->getFile($key, true);
|
$file = $this->files[$key] = $this->getFile($key, true);
|
||||||
// Since OPcache only compiles files older than the script execution start, set the file's mtime in the past
|
// Since OPcache only compiles files older than the script execution start, set the file's mtime in the past
|
||||||
$ok = $this->write($file, "<?php return [{$expiry}, {$value}];\n", self::$startTime - 10) && $ok;
|
$ok = $this->write($file, $value, self::$startTime - 10) && $ok;
|
||||||
|
|
||||||
if ($allowCompile) {
|
if ($allowCompile) {
|
||||||
@opcache_invalidate($file, true);
|
@opcache_invalidate($file, true);
|
||||||
@ -241,3 +267,16 @@ trait PhpFilesTrait
|
|||||||
return @unlink($file);
|
return @unlink($file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class LazyValue
|
||||||
|
{
|
||||||
|
public $file;
|
||||||
|
|
||||||
|
public function __construct($file)
|
||||||
|
{
|
||||||
|
$this->file = $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user