Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d287508

Browse filesBrowse files
[DI][Config] Add & use ReflectionClassResource
1 parent b9b6ebd commit d287508
Copy full SHA for d287508

21 files changed

+551
-154
lines changed

‎src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+3-5Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
use Symfony\Component\Finder\Finder;
2929
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
3030
use Symfony\Component\Config\FileLocator;
31-
use Symfony\Component\Config\Resource\ClassExistenceResource;
3231
use Symfony\Component\PropertyAccess\PropertyAccessor;
3332
use Symfony\Component\Serializer\Encoder\YamlEncoder;
3433
use Symfony\Component\Serializer\Encoder\CsvEncoder;
@@ -85,8 +84,7 @@ public function load(array $configs, ContainerBuilder $container)
8584

8685
$loader->load('fragment_renderer.xml');
8786

88-
$container->addResource(new ClassExistenceResource(Application::class));
89-
if (class_exists(Application::class)) {
87+
if ($container->getClassExists(Application::class)) {
9088
$loader->load('console.xml');
9189
}
9290

@@ -525,7 +523,7 @@ private function registerDebugConfiguration(array $config, ContainerBuilder $con
525523
$definition->replaceArgument(4, $debug);
526524
$definition->replaceArgument(6, $debug);
527525

528-
if ($debug && class_exists(DebugProcessor::class)) {
526+
if ($debug && $container->getClassExists(DebugProcessor::class)) {
529527
$definition = new Definition(DebugProcessor::class);
530528
$definition->setPublic(false);
531529
$container->setDefinition('debug.log_processor', $definition);
@@ -1251,7 +1249,7 @@ private function registerSerializerConfiguration(array $config, ContainerBuilder
12511249
$container->getDefinition('serializer.mapping.class_metadata_factory')->replaceArgument(
12521250
1, new Reference($config['cache'])
12531251
);
1254-
} elseif (!$container->getParameter('kernel.debug') && class_exists(CacheClassMetadataFactory::class)) {
1252+
} elseif (!$container->getParameter('kernel.debug') && $container->getClassExists(CacheClassMetadataFactory::class)) {
12551253
$cacheMetadataFactory = new Definition(
12561254
CacheClassMetadataFactory::class,
12571255
array(

‎src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExceptionListenerPass.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExceptionListenerPass.php
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111

1212
namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler;
1313

14+
use Symfony\Component\Debug\Exception\FlattenException;
1415
use Symfony\Component\DependencyInjection\ContainerBuilder;
1516
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
17+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
1618

1719
/**
1820
* Registers the Twig exception listener if Twig is registered as a templating engine.
@@ -28,7 +30,7 @@ public function process(ContainerBuilder $container)
2830
}
2931

3032
// register the exception controller only if Twig is enabled and required dependencies do exist
31-
if (!class_exists('Symfony\Component\Debug\Exception\FlattenException') || !interface_exists('Symfony\Component\EventDispatcher\EventSubscriberInterface')) {
33+
if (!$container->getClassExists(FlattenException::class) || !$container->getClassExists(EventSubscriberInterface::class)) {
3234
$container->removeDefinition('twig.exception_listener');
3335
} elseif ($container->hasParameter('templating.engines')) {
3436
$engines = $container->getParameter('templating.engines');

‎src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/TwigBundle/DependencyInjection/Compiler/ExtensionPass.php
+12-12Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111

1212
namespace Symfony\Bundle\TwigBundle\DependencyInjection\Compiler;
1313

14-
use Symfony\Component\Config\Resource\ClassExistenceResource;
14+
use Symfony\Component\Asset\Packages;
1515
use Symfony\Component\DependencyInjection\Alias;
1616
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1717
use Symfony\Component\DependencyInjection\ContainerBuilder;
1818
use Symfony\Component\DependencyInjection\Reference;
1919
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
20+
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
2021
use Symfony\Component\Stopwatch\Stopwatch;
22+
use Symfony\Component\Translation\TranslatorInterface;
2123
use Symfony\Component\Yaml\Parser as YamlParser;
2224

2325
/**
@@ -27,22 +29,23 @@ class ExtensionPass implements CompilerPassInterface
2729
{
2830
public function process(ContainerBuilder $container)
2931
{
30-
if (!class_exists('Symfony\Component\Asset\Packages')) {
32+
if (!$container->getClassExists(Packages::class)) {
3133
$container->removeDefinition('twig.extension.assets');
3234
}
3335

34-
if (!class_exists('Symfony\Component\ExpressionLanguage\Expression')) {
36+
if (!$container->getClassExists(ExpressionLanguage::class)) {
3537
$container->removeDefinition('twig.extension.expression');
3638
}
3739

38-
if (!interface_exists('Symfony\Component\Routing\Generator\UrlGeneratorInterface')) {
40+
if (!$container->getClassExists(UrlGeneratorInterface::class)) {
3941
$container->removeDefinition('twig.extension.routing');
4042
}
41-
if (!interface_exists('Symfony\Component\Translation\TranslatorInterface')) {
43+
44+
if (!$container->getClassExists(TranslatorInterface::class)) {
4245
$container->removeDefinition('twig.extension.trans');
4346
}
4447

45-
if (!class_exists('Symfony\Component\Yaml\Yaml')) {
48+
if (!$container->getClassExists(YamlParser::class)) {
4649
$container->removeDefinition('twig.extension.yaml');
4750
}
4851

@@ -101,18 +104,15 @@ public function process(ContainerBuilder $container)
101104
$container->getDefinition('twig.extension.assets')->addTag('twig.extension');
102105
}
103106

104-
$container->addResource(new ClassExistenceResource(YamlParser::class));
105-
if (class_exists(YamlParser::class)) {
107+
if ($container->hasDefinition('twig.extension.yaml')) {
106108
$container->getDefinition('twig.extension.yaml')->addTag('twig.extension');
107109
}
108110

109-
$container->addResource(new ClassExistenceResource(Stopwatch::class));
110-
if (class_exists(Stopwatch::class)) {
111+
if ($container->getClassExists(Stopwatch::class)) {
111112
$container->getDefinition('twig.extension.debug.stopwatch')->addTag('twig.extension');
112113
}
113114

114-
$container->addResource(new ClassExistenceResource(ExpressionLanguage::class));
115-
if (class_exists(ExpressionLanguage::class)) {
115+
if ($container->hasDefinition('twig.extension.expression')) {
116116
$container->getDefinition('twig.extension.expression')->addTag('twig.extension');
117117
}
118118
}

‎src/Symfony/Bundle/TwigBundle/composer.json

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/TwigBundle/composer.json
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
],
1818
"require": {
1919
"php": ">=5.5.9",
20-
"symfony/config": "~3.2",
20+
"symfony/config": "~3.3",
2121
"symfony/twig-bridge": "^3.2.1",
2222
"symfony/http-foundation": "~2.8|~3.0",
2323
"symfony/http-kernel": "~2.8.16|~3.1.9|^3.2.2",
@@ -26,7 +26,7 @@
2626
"require-dev": {
2727
"symfony/asset": "~2.8|~3.0",
2828
"symfony/stopwatch": "~2.8|~3.0",
29-
"symfony/dependency-injection": "~2.8|~3.0",
29+
"symfony/dependency-injection": "~3.3",
3030
"symfony/expression-language": "~2.8|~3.0",
3131
"symfony/finder": "~2.8|~3.0",
3232
"symfony/form": "~2.8|~3.0",

‎src/Symfony/Component/Config/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/Config/CHANGELOG.md
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
CHANGELOG
22
=========
33

4+
3.3.0
5+
-----
6+
7+
* added `ReflectionClassResource` class
8+
* added second `$exists` constructor argument to `ClassExistenceResource`
9+
* made `ClassExistenceResource` also work with interfaces and traits
10+
411
3.0.0
512
-----
613

‎src/Symfony/Component/Config/Resource/ClassExistenceResource.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Config/Resource/ClassExistenceResource.php
+37-4Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,25 @@
2121
*/
2222
class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializable
2323
{
24+
const EXISTS_OK = 1;
25+
const EXISTS_KO = 0;
26+
const EXISTS_KO_WITH_THROWING_AUTOLOADER = -1;
27+
2428
private $resource;
2529
private $exists;
2630

31+
private static $checkingLevel = 0;
32+
private static $throwingAutoloader;
33+
private static $existsCache = array();
34+
2735
/**
28-
* @param string $resource The fully-qualified class name
36+
* @param string $resource The fully-qualified class name
37+
* @param int|null $exists One of the self::EXISTS_* const if the existency check has already been done
2938
*/
30-
public function __construct($resource)
39+
public function __construct($resource, $exists = null)
3140
{
3241
$this->resource = $resource;
33-
$this->exists = class_exists($resource);
42+
$this->exists = null !== $exists ? (int) $exists : (class_exists($resource) || interface_exists($resource, false) || trait_exists($resource, false));
3443
}
3544

3645
/**
@@ -54,7 +63,31 @@ public function getResource()
5463
*/
5564
public function isFresh($timestamp)
5665
{
57-
return class_exists($this->resource) === $this->exists;
66+
if (isset(self::$existsCache[$this->resource])) {
67+
// no-op
68+
} elseif (self::EXISTS_KO_WITH_THROWING_AUTOLOADER === $this->exists) {
69+
if (null === self::$throwingAutoloader) {
70+
$signalingException = new \ReflectionException();
71+
self::$throwingAutoloader = function () use ($signalingException) { throw $signalingException; };
72+
}
73+
if (!self::$checkingLevel++) {
74+
spl_autoload_register(self::$throwingAutoloader);
75+
}
76+
77+
try {
78+
self::$existsCache[$this->resource] = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
79+
} catch (\ReflectionException $e) {
80+
self::$existsCache[$this->resource] = false;
81+
} finally {
82+
if (!--self::$checkingLevel) {
83+
spl_autoload_unregister(self::$throwingAutoloader);
84+
}
85+
}
86+
} else {
87+
self::$existsCache[$this->resource] = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
88+
}
89+
90+
return self::EXISTS_OK == $this->exists xor !self::$existsCache[$this->resource];
5891
}
5992

6093
/**
+143Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Config\Resource;
13+
14+
/**
15+
* @author Nicolas Grekas <p@tchwork.com>
16+
*/
17+
class ReflectionClassResource implements SelfCheckingResourceInterface, \Serializable
18+
{
19+
private $file;
20+
private $className;
21+
private $classReflector;
22+
private $hash;
23+
24+
public function __construct(\ReflectionClass $classReflector)
25+
{
26+
$this->file = $classReflector->getFileName();
27+
if ($this->file && !file_exists($this->file)) {
28+
$this->file = false;
29+
}
30+
$this->className = $classReflector->name;
31+
$this->classReflector = $classReflector;
32+
$this->hash = $this->computeHash();
33+
}
34+
35+
public function isFresh($timestamp)
36+
{
37+
if ($this->file) {
38+
if (!file_exists($this->file)) {
39+
return false;
40+
}
41+
42+
if (@filemtime($this->file) <= $timestamp) {
43+
return true;
44+
}
45+
}
46+
47+
return $this->hash === $this->computeHash();
48+
}
49+
50+
public function __toString()
51+
{
52+
return 'reflection.'.$this->className;
53+
}
54+
55+
public function serialize()
56+
{
57+
return serialize(array($this->file, $this->className, $this->hash));
58+
}
59+
60+
public function unserialize($serialized)
61+
{
62+
list($this->file, $this->className, $this->hash) = unserialize($serialized);
63+
}
64+
65+
private function computeHash()
66+
{
67+
if (null === $this->classReflector) {
68+
try {
69+
$this->classReflector = new \ReflectionClass($this->className);
70+
} catch (\ReflectionException $e) {
71+
// the class does not exist anymore
72+
return false;
73+
}
74+
}
75+
$hash = hash_init('md5');
76+
77+
foreach ($this->generateSignature($this->classReflector) as $info) {
78+
hash_update($hash, $info);
79+
}
80+
81+
return hash_final($hash);
82+
}
83+
84+
private function generateSignature(\ReflectionClass $class)
85+
{
86+
yield $class->getDocComment().$class->getModifiers();
87+
yield print_r(class_parents($class->name), true);
88+
yield print_r(class_implements($class->name), true);
89+
yield print_r($class->getConstants(), true);
90+
91+
$defaults = $class->getDefaultProperties();
92+
93+
foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED) as $p) {
94+
yield $p->getDocComment().$p;
95+
yield print_r($defaults[$p->name], true);
96+
}
97+
98+
if (defined('HHVM_VERSION')) {
99+
foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) {
100+
// workaround HHVM bug with variadics, see https://github.com/facebook/hhvm/issues/5762
101+
yield preg_replace('/^ @@.*/m', '', new ReflectionMethodHhvmWrapper($m->class, $m->name));
102+
}
103+
} else {
104+
foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) {
105+
yield preg_replace('/^ @@.*/m', '', $m);
106+
107+
$defaults = array();
108+
foreach ($m->getParameters() as $p) {
109+
$defaults[$p->name] = $p->isDefaultValueAvailable() ? $p->getDefaultValue() : null;
110+
}
111+
yield print_r($defaults, true);
112+
}
113+
}
114+
}
115+
}
116+
117+
/**
118+
* @internal
119+
*/
120+
class ReflectionMethodHhvmWrapper extends \ReflectionMethod
121+
{
122+
public function getParameters()
123+
{
124+
$params = array();
125+
126+
foreach (parent::getParameters() as $i => $p) {
127+
$params[] = new ReflectionParameterHhvmWrapper(array($this->class, $this->name), $i);
128+
}
129+
130+
return $params;
131+
}
132+
}
133+
134+
/**
135+
* @internal
136+
*/
137+
class ReflectionParameterHhvmWrapper extends \ReflectionParameter
138+
{
139+
public function getDefaultValue()
140+
{
141+
return array($this->isVariadic(), $this->isDefaultValueAvailable() ? parent::getDefaultValue() : null);
142+
}
143+
}

‎src/Symfony/Component/Config/Tests/Resource/ClassExistenceResourceTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Config/Tests/Resource/ClassExistenceResourceTest.php
+17-1Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public function testIsFreshWhenClassDoesNotExist()
3636
eval(<<<EOF
3737
namespace Symfony\Component\Config\Tests\Fixtures;
3838
39-
class BarClass
39+
interface BarClass
4040
{
4141
}
4242
EOF
@@ -50,5 +50,21 @@ public function testIsFreshWhenClassExists()
5050
$res = new ClassExistenceResource('Symfony\Component\Config\Tests\Resource\ClassExistenceResourceTest');
5151

5252
$this->assertTrue($res->isFresh(time()));
53+
$this->assertTrue(class_exists('Symfony\Component\Config\Tests\Resource\ClassExistenceResourceTest', false));
54+
}
55+
56+
public function testExistsKo()
57+
{
58+
spl_autoload_register(function ($class) use (&$loadedClass) { $loadedClass = $class; });
59+
60+
$res = new ClassExistenceResource('MissingFooClass');
61+
62+
$this->assertSame('MissingFooClass', $loadedClass);
63+
64+
$loadedClass = 123;
65+
66+
$res = new ClassExistenceResource('MissingFooClass', ClassExistenceResource::EXISTS_KO);
67+
68+
$this->assertSame(123, $loadedClass);
5369
}
5470
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.