Skip to content

Commit 247a597

Browse files
Merge branch '3.4' into 4.3
* 3.4: [Cache] fix memory leak when using PhpArrayAdapter fix parsing negative octal numbers [SecurityBundle] Properly escape regex in AddSessionDomainConstraintPass [Config] never try loading failed classes twice with ClassExistenceResource
2 parents 9822766 + 320cdc2 commit 247a597

File tree

3 files changed

+65
-8
lines changed

3 files changed

+65
-8
lines changed

Resource/ClassExistenceResource.php

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ class ClassExistenceResource implements SelfCheckingResourceInterface
3737
public function __construct(string $resource, bool $exists = null)
3838
{
3939
$this->resource = $resource;
40-
$this->exists = $exists;
40+
if (null !== $exists) {
41+
$this->exists = [(bool) $exists, null];
42+
}
4143
}
4244

4345
/**
@@ -65,26 +67,33 @@ public function isFresh($timestamp)
6567
{
6668
$loaded = class_exists($this->resource, false) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
6769

68-
if (null !== $exists = &self::$existsCache[(int) (0 >= $timestamp)][$this->resource]) {
69-
$exists = $exists || $loaded;
70-
} elseif (!$exists = $loaded) {
70+
if (null !== $exists = &self::$existsCache[$this->resource]) {
71+
if ($loaded) {
72+
$exists = [true, null];
73+
} elseif (0 >= $timestamp && !$exists[0] && null !== $exists[1]) {
74+
throw new \ReflectionException($exists[1]);
75+
}
76+
} elseif ([false, null] === $exists = [$loaded, null]) {
7177
if (!self::$autoloadLevel++) {
7278
spl_autoload_register(__CLASS__.'::throwOnRequiredClass');
7379
}
7480
$autoloadedClass = self::$autoloadedClass;
7581
self::$autoloadedClass = ltrim($this->resource, '\\');
7682

7783
try {
78-
$exists = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
84+
$exists[0] = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
7985
} catch (\Exception $e) {
86+
$exists[1] = $e->getMessage();
87+
8088
try {
8189
self::throwOnRequiredClass($this->resource, $e);
8290
} catch (\ReflectionException $e) {
8391
if (0 >= $timestamp) {
84-
unset(self::$existsCache[1][$this->resource]);
8592
throw $e;
8693
}
8794
}
95+
} catch (\Throwable $e) {
96+
$exists[1] = $e->getMessage();
8897
} finally {
8998
self::$autoloadedClass = $autoloadedClass;
9099
if (!--self::$autoloadLevel) {
@@ -97,7 +106,7 @@ public function isFresh($timestamp)
97106
$this->exists = $exists;
98107
}
99108

100-
return $this->exists xor !$exists;
109+
return $this->exists[0] xor !$exists[0];
101110
}
102111

103112
/**
@@ -112,6 +121,16 @@ public function __sleep(): array
112121
return ['resource', 'exists'];
113122
}
114123

124+
/**
125+
* @internal
126+
*/
127+
public function __wakeup()
128+
{
129+
if (\is_bool($this->exists)) {
130+
$this->exists = [$this->exists, null];
131+
}
132+
}
133+
115134
/**
116135
* Throws a reflection exception when the passed class does not exist but is required.
117136
*
@@ -147,7 +166,17 @@ public static function throwOnRequiredClass($class, \Exception $previous = null)
147166
throw $previous;
148167
}
149168

150-
$e = new \ReflectionException(sprintf('Class "%s" not found while loading "%s".', $class, self::$autoloadedClass), 0, $previous);
169+
$message = sprintf('Class "%s" not found.', $class);
170+
171+
if (self::$autoloadedClass !== $class) {
172+
$message = substr_replace($message, sprintf(' while loading "%s"', self::$autoloadedClass), -1, 0);
173+
}
174+
175+
if (null !== $previous) {
176+
$message = $previous->getMessage();
177+
}
178+
179+
$e = new \ReflectionException($message, 0, $previous);
151180

152181
if (null !== $previous) {
153182
throw $e;

Tests/Fixtures/BadFileName.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
namespace Symfony\Component\Config\Tests\Fixtures;
4+
5+
class FileNameMismatchOnPurpose
6+
{
7+
}
8+
9+
throw new \RuntimeException('Mismatch between file name and class name.');

Tests/Resource/ClassExistenceResourceTest.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Config\Resource\ClassExistenceResource;
16+
use Symfony\Component\Config\Tests\Fixtures\BadFileName;
1617
use Symfony\Component\Config\Tests\Fixtures\BadParent;
1718
use Symfony\Component\Config\Tests\Fixtures\Resource\ConditionalClass;
1819

@@ -90,6 +91,24 @@ public function testBadParentWithNoTimestamp()
9091
$res->isFresh(0);
9192
}
9293

94+
public function testBadFileName()
95+
{
96+
$this->expectException('ReflectionException');
97+
$this->expectExceptionMessage('Mismatch between file name and class name.');
98+
99+
$res = new ClassExistenceResource(BadFileName::class, false);
100+
$res->isFresh(0);
101+
}
102+
103+
public function testBadFileNameBis()
104+
{
105+
$this->expectException('ReflectionException');
106+
$this->expectExceptionMessage('Mismatch between file name and class name.');
107+
108+
$res = new ClassExistenceResource(BadFileName::class, false);
109+
$res->isFresh(0);
110+
}
111+
93112
public function testConditionalClass()
94113
{
95114
$res = new ClassExistenceResource(ConditionalClass::class, false);

0 commit comments

Comments
 (0)