Skip to content

Commit

Permalink
bug #21527 [Config] Fix conditional class existence checks (nicolas-g…
Browse files Browse the repository at this point in the history
…rekas)

This PR was merged into the 3.3-dev branch.

Discussion
----------

[Config] Fix conditional class existence checks

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

Commits
-------

686af61 [Config] Fix conditional class existence checks
  • Loading branch information
nicolas-grekas committed Feb 5, 2017
2 parents ed6a2ed + 686af61 commit a736458
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 10 deletions.
51 changes: 41 additions & 10 deletions src/Symfony/Component/Config/Resource/ClassExistenceResource.php
Expand Up @@ -28,8 +28,7 @@ class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializ
private $resource;
private $existsStatus;

private static $checkingLevel = 0;
private static $throwingAutoloader;
private static $autoloadLevel = 0;
private static $existsCache = array();

/**
Expand Down Expand Up @@ -68,21 +67,17 @@ public function isFresh($timestamp)
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) {
if (null === self::$throwingAutoloader) {
$signalingException = new \ReflectionException();
self::$throwingAutoloader = function () use ($signalingException) { throw $signalingException; };
}
if (!self::$checkingLevel++) {
spl_autoload_register(self::$throwingAutoloader);
if (!self::$autoloadLevel++) {
spl_autoload_register('Symfony\Component\Config\Resource\ClassExistenceResource::throwOnRequiredClass');
}

try {
$exists = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
} catch (\ReflectionException $e) {
$exists = false;
} finally {
if (!--self::$checkingLevel) {
spl_autoload_unregister(self::$throwingAutoloader);
if (!--self::$autoloadLevel) {
spl_autoload_unregister('Symfony\Component\Config\Resource\ClassExistenceResource::throwOnRequiredClass');
}
}
} else {
Expand Down Expand Up @@ -115,4 +110,40 @@ public function unserialize($serialized)
{
list($this->resource, $this->existsStatus) = unserialize($serialized);
}

/**
* @throws \ReflectionException When $class is not found and is required
*/
private static function throwOnRequiredClass($class)
{
$e = new \ReflectionException("Class $class does not exist");
$trace = $e->getTrace();
$autoloadFrame = array(
'function' => 'spl_autoload_call',
'args' => array($class),
);
$i = 1 + array_search($autoloadFrame, $trace, true);

if (isset($trace[$i]['function']) && !isset($trace[$i]['class'])) {
switch ($trace[$i]['function']) {
case 'get_class_methods':
case 'get_class_vars':
case 'get_parent_class':
case 'is_a':
case 'is_subclass_of':
case 'class_exists':
case 'class_implements':
case 'class_parents':
case 'trait_exists':
case 'defined':
case 'interface_exists':
case 'method_exists':
case 'property_exists':
case 'is_callable':
return;
}
}

throw $e;
}
}
@@ -0,0 +1,9 @@
<?php

namespace Symfony\Component\Config\Tests\Fixtures\Resource;

if (!class_exists(MissingClass::class)) {
class ConditionalClass
{
}
}
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Config\Tests\Resource;

use Symfony\Component\Config\Resource\ClassExistenceResource;
use Symfony\Component\Config\Tests\Fixtures\Resource\ConditionalClass;

class ClassExistenceResourceTest extends \PHPUnit_Framework_TestCase
{
Expand Down Expand Up @@ -71,4 +72,11 @@ public function testExistsKo()
spl_autoload_unregister($autoloader);
}
}

public function testConditionalClass()
{
$res = new ClassExistenceResource(ConditionalClass::class, ClassExistenceResource::EXISTS_KO_WITH_THROWING_AUTOLOADER);

$this->assertFalse($res->isFresh(0));
}
}

0 comments on commit a736458

Please sign in to comment.