[Config] Always protect ClassExistenceResource against bad parents

This commit is contained in:
Nicolas Grekas 2017-06-02 19:23:28 +02:00
parent 349e5fe9b8
commit 86bf26c466
5 changed files with 28 additions and 36 deletions

View File

@ -21,25 +21,21 @@ namespace Symfony\Component\Config\Resource;
*/
class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializable
{
const EXISTS_OK = 1;
const EXISTS_KO = 0;
const EXISTS_KO_WITH_THROWING_AUTOLOADER = -1;
private $resource;
private $existsStatus;
private $exists;
private static $autoloadLevel = 0;
private static $existsCache = array();
/**
* @param string $resource The fully-qualified class name
* @param int|null $existsStatus One of the self::EXISTS_* const if the existency check has already been done
* @param string $resource The fully-qualified class name
* @param bool|null $exists Boolean when the existency check has already been done
*/
public function __construct($resource, $existsStatus = null)
public function __construct($resource, $exists = null)
{
$this->resource = $resource;
if (null !== $existsStatus) {
$this->existsStatus = (int) $existsStatus;
if (null !== $exists) {
$this->exists = (bool) $exists;
}
}
@ -64,11 +60,13 @@ class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializ
*/
public function isFresh($timestamp)
{
$loaded = class_exists($this->resource, false) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
if (null !== $exists = &self::$existsCache[$this->resource]) {
$exists = $exists || class_exists($this->resource, false) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
} elseif (self::EXISTS_KO_WITH_THROWING_AUTOLOADER === $this->existsStatus) {
$exists = $exists || $loaded;
} elseif (!$exists = $loaded) {
if (!self::$autoloadLevel++) {
spl_autoload_register('Symfony\Component\Config\Resource\ClassExistenceResource::throwOnRequiredClass');
spl_autoload_register(__CLASS__.'::throwOnRequiredClass');
}
try {
@ -77,18 +75,16 @@ class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializ
$exists = false;
} finally {
if (!--self::$autoloadLevel) {
spl_autoload_unregister('Symfony\Component\Config\Resource\ClassExistenceResource::throwOnRequiredClass');
spl_autoload_unregister(__CLASS__.'::throwOnRequiredClass');
}
}
} else {
$exists = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
}
if (null === $this->existsStatus) {
$this->existsStatus = $exists ? self::EXISTS_OK : self::EXISTS_KO;
if (null === $this->exists) {
$this->exists = $exists;
}
return self::EXISTS_OK === $this->existsStatus xor !$exists;
return $this->exists xor !$exists;
}
/**
@ -96,11 +92,11 @@ class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializ
*/
public function serialize()
{
if (null === $this->existsStatus) {
if (null === $this->exists) {
$this->isFresh(0);
}
return serialize(array($this->resource, $this->existsStatus));
return serialize(array($this->resource, $this->exists));
}
/**
@ -108,7 +104,7 @@ class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializ
*/
public function unserialize($serialized)
{
list($this->resource, $this->existsStatus) = unserialize($serialized);
list($this->resource, $this->exists) = unserialize($serialized);
}
/**

View File

@ -66,7 +66,7 @@ EOF
$loadedClass = 123;
$res = new ClassExistenceResource('MissingFooClass', ClassExistenceResource::EXISTS_KO);
$res = new ClassExistenceResource('MissingFooClass', false);
$this->assertSame(123, $loadedClass);
} finally {
@ -76,7 +76,7 @@ EOF
public function testConditionalClass()
{
$res = new ClassExistenceResource(ConditionalClass::class, ClassExistenceResource::EXISTS_KO_WITH_THROWING_AUTOLOADER);
$res = new ClassExistenceResource(ConditionalClass::class, false);
$this->assertFalse($res->isFresh(0));
}

View File

@ -388,7 +388,7 @@ class AutowirePass extends AbstractRecursivePass
unset($this->ambiguousServiceTypes[$type]);
}
if ($definition->isDeprecated() || !$reflectionClass = $this->container->getReflectionClass($definition->getClass(), true)) {
if ($definition->isDeprecated() || !$reflectionClass = $this->container->getReflectionClass($definition->getClass())) {
return;
}
@ -444,7 +444,7 @@ class AutowirePass extends AbstractRecursivePass
*/
private function createAutowiredDefinition($type)
{
if (!($typeHint = $this->container->getReflectionClass($type, true)) || !$typeHint->isInstantiable()) {
if (!($typeHint = $this->container->getReflectionClass($type)) || !$typeHint->isInstantiable()) {
return;
}
@ -478,7 +478,7 @@ class AutowirePass extends AbstractRecursivePass
private function createTypeNotFoundMessage(TypedReference $reference, $label)
{
if (!$r = $this->container->getReflectionClass($type = $reference->getType(), true)) {
if (!$r = $this->container->getReflectionClass($type = $reference->getType())) {
$message = sprintf('has type "%s" but this class does not exist.', $type);
} else {
$message = $this->container->has($type) ? 'this service is abstract' : 'no such service exists';

View File

@ -337,13 +337,12 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
* Retrieves the requested reflection class and registers it for resource tracking.
*
* @param string $class
* @param bool $koWithThrowingAutoloader Whether autoload should be protected against bad parents or not
*
* @return \ReflectionClass|null
*
* @final
*/
public function getReflectionClass($class, $koWithThrowingAutoloader = false)
public function getReflectionClass($class)
{
if (!$class = $this->getParameterBag()->resolveValue($class)) {
return;
@ -353,12 +352,9 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
try {
if (isset($this->classReflectors[$class])) {
$classReflector = $this->classReflectors[$class];
} elseif ($koWithThrowingAutoloader) {
$resource = new ClassExistenceResource($class, ClassExistenceResource::EXISTS_KO_WITH_THROWING_AUTOLOADER);
$classReflector = $resource->isFresh(0) ? false : new \ReflectionClass($class);
} else {
$classReflector = new \ReflectionClass($class);
$resource = new ClassExistenceResource($class, false);
$classReflector = $resource->isFresh(0) ? false : new \ReflectionClass($class);
}
} catch (\ReflectionException $e) {
$classReflector = false;
@ -366,7 +362,7 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
if ($this->trackResources) {
if (!$classReflector) {
$this->addResource($resource ?: new ClassExistenceResource($class, ClassExistenceResource::EXISTS_KO));
$this->addResource($resource ?: new ClassExistenceResource($class, false));
} elseif (!$classReflector->isInternal()) {
$path = $classReflector->getFileName();

View File

@ -32,7 +32,7 @@
"symfony/proxy-manager-bridge": "Generate service proxies to lazy load them"
},
"conflict": {
"symfony/config": "<3.3",
"symfony/config": "<3.3.1",
"symfony/finder": "<3.3",
"symfony/yaml": "<3.3"
},