Merge branch '3.4'

* 3.4:
  [Config] Always protect ClassExistenceResource against bad parents
This commit is contained in:
Nicolas Grekas 2017-06-02 20:47:32 +02:00
commit 9a8f779a5d
4 changed files with 27 additions and 35 deletions

View File

@ -21,25 +21,21 @@ namespace Symfony\Component\Config\Resource;
*/ */
class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializable class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializable
{ {
const EXISTS_OK = 1;
const EXISTS_KO = 0;
const EXISTS_KO_WITH_THROWING_AUTOLOADER = -1;
private $resource; private $resource;
private $existsStatus; private $exists;
private static $autoloadLevel = 0; private static $autoloadLevel = 0;
private static $existsCache = array(); private static $existsCache = array();
/** /**
* @param string $resource The fully-qualified class name * @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 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; $this->resource = $resource;
if (null !== $existsStatus) { if (null !== $exists) {
$this->existsStatus = (int) $existsStatus; $this->exists = (bool) $exists;
} }
} }
@ -64,11 +60,13 @@ class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializ
*/ */
public function isFresh($timestamp) 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]) { if (null !== $exists = &self::$existsCache[$this->resource]) {
$exists = $exists || class_exists($this->resource, false) || interface_exists($this->resource, false) || trait_exists($this->resource, false); $exists = $exists || $loaded;
} elseif (self::EXISTS_KO_WITH_THROWING_AUTOLOADER === $this->existsStatus) { } elseif (!$exists = $loaded) {
if (!self::$autoloadLevel++) { if (!self::$autoloadLevel++) {
spl_autoload_register('Symfony\Component\Config\Resource\ClassExistenceResource::throwOnRequiredClass'); spl_autoload_register(__CLASS__.'::throwOnRequiredClass');
} }
try { try {
@ -77,18 +75,16 @@ class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializ
$exists = false; $exists = false;
} finally { } finally {
if (!--self::$autoloadLevel) { 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) { if (null === $this->exists) {
$this->existsStatus = $exists ? self::EXISTS_OK : self::EXISTS_KO; $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() public function serialize()
{ {
if (null === $this->existsStatus) { if (null === $this->exists) {
$this->isFresh(0); $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) 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; $loadedClass = 123;
$res = new ClassExistenceResource('MissingFooClass', ClassExistenceResource::EXISTS_KO); $res = new ClassExistenceResource('MissingFooClass', false);
$this->assertSame(123, $loadedClass); $this->assertSame(123, $loadedClass);
} finally { } finally {
@ -76,7 +76,7 @@ EOF
public function testConditionalClass() 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)); $this->assertFalse($res->isFresh(0));
} }

View File

@ -376,7 +376,7 @@ class AutowirePass extends AbstractRecursivePass
return; return;
} }
if ($definition->isDeprecated() || !$reflectionClass = $this->container->getReflectionClass($definition->getClass(), true)) { if ($definition->isDeprecated() || !$reflectionClass = $this->container->getReflectionClass($definition->getClass())) {
return; return;
} }
@ -428,7 +428,7 @@ class AutowirePass extends AbstractRecursivePass
*/ */
private function createAutowiredDefinition($type) private function createAutowiredDefinition($type)
{ {
if (!($typeHint = $this->container->getReflectionClass($type, true)) || !$typeHint->isInstantiable()) { if (!($typeHint = $this->container->getReflectionClass($type)) || !$typeHint->isInstantiable()) {
return; return;
} }
@ -462,7 +462,7 @@ class AutowirePass extends AbstractRecursivePass
private function createTypeNotFoundMessage(TypedReference $reference, $label) 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); $message = sprintf('has type "%s" but this class does not exist.', $type);
} else { } else {
$message = $this->container->has($type) ? 'this service is abstract' : 'no such service exists'; $message = $this->container->has($type) ? 'this service is abstract' : 'no such service exists';

View File

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