bug #34762 [Config] never try loading failed classes twice with ClassExistenceResource (nicolas-grekas)
This PR was merged into the 3.4 branch.
Discussion
----------
[Config] never try loading failed classes twice with ClassExistenceResource
| Q | A
| ------------- | ---
| Branch? | 3.4
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Tickets | Fix #34658
| License | MIT
| Doc PR | -
Commits
-------
90c9a80863
[Config] never try loading failed classes twice with ClassExistenceResource
This commit is contained in:
commit
c0b2ade0a3
@ -36,7 +36,7 @@ class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializ
|
|||||||
{
|
{
|
||||||
$this->resource = $resource;
|
$this->resource = $resource;
|
||||||
if (null !== $exists) {
|
if (null !== $exists) {
|
||||||
$this->exists = (bool) $exists;
|
$this->exists = [(bool) $exists, null];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,9 +65,13 @@ class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializ
|
|||||||
{
|
{
|
||||||
$loaded = class_exists($this->resource, false) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
|
$loaded = class_exists($this->resource, false) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
|
||||||
|
|
||||||
if (null !== $exists = &self::$existsCache[(int) (0 >= $timestamp)][$this->resource]) {
|
if (null !== $exists = &self::$existsCache[$this->resource]) {
|
||||||
$exists = $exists || $loaded;
|
if ($loaded) {
|
||||||
} elseif (!$exists = $loaded) {
|
$exists = [true, null];
|
||||||
|
} elseif (0 >= $timestamp && !$exists[0] && null !== $exists[1]) {
|
||||||
|
throw new \ReflectionException($exists[1]);
|
||||||
|
}
|
||||||
|
} elseif ([false, null] === $exists = [$loaded, null]) {
|
||||||
if (!self::$autoloadLevel++) {
|
if (!self::$autoloadLevel++) {
|
||||||
spl_autoload_register(__CLASS__.'::throwOnRequiredClass');
|
spl_autoload_register(__CLASS__.'::throwOnRequiredClass');
|
||||||
}
|
}
|
||||||
@ -75,16 +79,19 @@ class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializ
|
|||||||
self::$autoloadedClass = ltrim($this->resource, '\\');
|
self::$autoloadedClass = ltrim($this->resource, '\\');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$exists = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
|
$exists[0] = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
$exists[1] = $e->getMessage();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
self::throwOnRequiredClass($this->resource, $e);
|
self::throwOnRequiredClass($this->resource, $e);
|
||||||
} catch (\ReflectionException $e) {
|
} catch (\ReflectionException $e) {
|
||||||
if (0 >= $timestamp) {
|
if (0 >= $timestamp) {
|
||||||
unset(self::$existsCache[1][$this->resource]);
|
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
$exists[1] = $e->getMessage();
|
||||||
} finally {
|
} finally {
|
||||||
self::$autoloadedClass = $autoloadedClass;
|
self::$autoloadedClass = $autoloadedClass;
|
||||||
if (!--self::$autoloadLevel) {
|
if (!--self::$autoloadLevel) {
|
||||||
@ -97,7 +104,7 @@ class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializ
|
|||||||
$this->exists = $exists;
|
$this->exists = $exists;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->exists xor !$exists;
|
return $this->exists[0] xor !$exists[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -118,6 +125,10 @@ class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializ
|
|||||||
public function unserialize($serialized)
|
public function unserialize($serialized)
|
||||||
{
|
{
|
||||||
list($this->resource, $this->exists) = unserialize($serialized);
|
list($this->resource, $this->exists) = unserialize($serialized);
|
||||||
|
|
||||||
|
if (\is_bool($this->exists)) {
|
||||||
|
$this->exists = [$this->exists, null];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -155,7 +166,17 @@ class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializ
|
|||||||
throw $previous;
|
throw $previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
$e = new \ReflectionException(sprintf('Class "%s" not found while loading "%s".', $class, self::$autoloadedClass), 0, $previous);
|
$message = sprintf('Class "%s" not found.', $class);
|
||||||
|
|
||||||
|
if (self::$autoloadedClass !== $class) {
|
||||||
|
$message = substr_replace($message, sprintf(' while loading "%s"', self::$autoloadedClass), -1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null !== $previous) {
|
||||||
|
$message = $previous->getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
$e = new \ReflectionException($message, 0, $previous);
|
||||||
|
|
||||||
if (null !== $previous) {
|
if (null !== $previous) {
|
||||||
throw $e;
|
throw $e;
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Symfony\Component\Config\Tests\Fixtures;
|
||||||
|
|
||||||
|
class FileNameMismatchOnPurpose
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \RuntimeException('Mismatch between file name and class name.');
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Config\Tests\Resource;
|
|||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Symfony\Component\Config\Resource\ClassExistenceResource;
|
use Symfony\Component\Config\Resource\ClassExistenceResource;
|
||||||
|
use Symfony\Component\Config\Tests\Fixtures\BadFileName;
|
||||||
use Symfony\Component\Config\Tests\Fixtures\BadParent;
|
use Symfony\Component\Config\Tests\Fixtures\BadParent;
|
||||||
use Symfony\Component\Config\Tests\Fixtures\Resource\ConditionalClass;
|
use Symfony\Component\Config\Tests\Fixtures\Resource\ConditionalClass;
|
||||||
|
|
||||||
@ -90,6 +91,24 @@ EOF
|
|||||||
$res->isFresh(0);
|
$res->isFresh(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testBadFileName()
|
||||||
|
{
|
||||||
|
$this->expectException('ReflectionException');
|
||||||
|
$this->expectExceptionMessage('Mismatch between file name and class name.');
|
||||||
|
|
||||||
|
$res = new ClassExistenceResource(BadFileName::class, false);
|
||||||
|
$res->isFresh(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBadFileNameBis()
|
||||||
|
{
|
||||||
|
$this->expectException('ReflectionException');
|
||||||
|
$this->expectExceptionMessage('Mismatch between file name and class name.');
|
||||||
|
|
||||||
|
$res = new ClassExistenceResource(BadFileName::class, false);
|
||||||
|
$res->isFresh(0);
|
||||||
|
}
|
||||||
|
|
||||||
public function testConditionalClass()
|
public function testConditionalClass()
|
||||||
{
|
{
|
||||||
$res = new ClassExistenceResource(ConditionalClass::class, false);
|
$res = new ClassExistenceResource(ConditionalClass::class, false);
|
||||||
|
Reference in New Issue
Block a user