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 a8ce243

Browse filesBrowse files
[DI][Config] Add & use ReflectionClassResource
1 parent 46daa35 commit a8ce243
Copy full SHA for a8ce243

File tree

26 files changed

+581
-141
lines changed
Filter options

26 files changed

+581
-141
lines changed

‎src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/LoggingTranslatorPass.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/LoggingTranslatorPass.php
+7-1Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313

1414
use Symfony\Component\DependencyInjection\ContainerBuilder;
1515
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
16+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1617
use Symfony\Component\DependencyInjection\Reference;
18+
use Symfony\Component\Translation\TranslatorInterface;
19+
use Symfony\Component\Translation\TranslatorBagInterface;
1720

1821
/**
1922
* @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
@@ -31,7 +34,10 @@ public function process(ContainerBuilder $container)
3134
$definition = $container->getDefinition((string) $translatorAlias);
3235
$class = $container->getParameterBag()->resolveValue($definition->getClass());
3336

34-
if (is_subclass_of($class, 'Symfony\Component\Translation\TranslatorInterface') && is_subclass_of($class, 'Symfony\Component\Translation\TranslatorBagInterface')) {
37+
if (!$r = $container->getReflectionClass($class)) {
38+
throw new InvalidArgumentException(sprintf('Class "%s" used for service "%s" cannot be found.', $class, $translatorAlias));
39+
}
40+
if ($r->isSubclassOf(TranslatorInterface::class) && $r->isSubclassOf(TranslatorBagInterface::class)) {
3541
$container->getDefinition('translator.logging')->setDecoratedService('translator');
3642
$container->getDefinition('translation.warmer')->replaceArgument(0, new Reference('translator.logging.inner'));
3743
}

‎src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/LoggingTranslatorPassTest.php
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ public function testProcess()
5454
->method('getParameterBag')
5555
->will($this->returnValue($parameterBag));
5656

57+
$container->expects($this->once())
58+
->method('getReflectionClass')
59+
->with('Symfony\Bundle\FrameworkBundle\Translation\Translator')
60+
->will($this->returnValue(new \ReflectionClass('Symfony\Bundle\FrameworkBundle\Translation\Translator')));
61+
5762
$pass = new LoggingTranslatorPass();
5863
$pass->process($container);
5964
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/composer.json
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"symfony/dependency-injection": "~3.3",
2323
"symfony/config": "~3.3",
2424
"symfony/event-dispatcher": "~3.3",
25-
"symfony/http-foundation": "~3.1",
25+
"symfony/http-foundation": "~3.3",
2626
"symfony/http-kernel": "~3.3",
2727
"symfony/polyfill-mbstring": "~1.0",
2828
"symfony/filesystem": "~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` 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
+50-7Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,27 @@
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;
25-
private $exists;
29+
private $existsStatus;
30+
31+
private static $checkingLevel = 0;
32+
private static $throwingAutoloader;
33+
private static $existsCache = array();
2634

2735
/**
28-
* @param string $resource The fully-qualified class name
36+
* @param string $resource The fully-qualified class name
37+
* @param int|null $existsStatus 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, $existsStatus = null)
3140
{
3241
$this->resource = $resource;
33-
$this->exists = class_exists($resource);
42+
if (null !== $existsStatus) {
43+
$this->existsStatus = (int) $existsStatus;
44+
}
3445
}
3546

3647
/**
@@ -54,22 +65,54 @@ public function getResource()
5465
*/
5566
public function isFresh($timestamp)
5667
{
57-
return class_exists($this->resource) === $this->exists;
68+
if (null !== $exists = &self::$existsCache[$this->resource]) {
69+
$exists = $exists || class_exists($this->resource, false) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
70+
} elseif (self::EXISTS_KO_WITH_THROWING_AUTOLOADER === $this->existsStatus) {
71+
if (null === self::$throwingAutoloader) {
72+
$signalingException = new \ReflectionException();
73+
self::$throwingAutoloader = function () use ($signalingException) { throw $signalingException; };
74+
}
75+
if (!self::$checkingLevel++) {
76+
spl_autoload_register(self::$throwingAutoloader);
77+
}
78+
79+
try {
80+
$exists = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
81+
} catch (\ReflectionException $e) {
82+
$exists = false;
83+
} finally {
84+
if (!--self::$checkingLevel) {
85+
spl_autoload_unregister(self::$throwingAutoloader);
86+
}
87+
}
88+
} else {
89+
$exists = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
90+
}
91+
92+
if (null === $this->existsStatus) {
93+
$this->existsStatus = $exists ? self::EXISTS_OK : self::EXISTS_KO;
94+
}
95+
96+
return self::EXISTS_OK === $this->existsStatus xor !$exists;
5897
}
5998

6099
/**
61100
* {@inheritdoc}
62101
*/
63102
public function serialize()
64103
{
65-
return serialize(array($this->resource, $this->exists));
104+
if (null === $this->existsStatus) {
105+
$this->isFresh(0);
106+
}
107+
108+
return serialize(array($this->resource, $this->existsStatus));
66109
}
67110

68111
/**
69112
* {@inheritdoc}
70113
*/
71114
public function unserialize($serialized)
72115
{
73-
list($this->resource, $this->exists) = unserialize($serialized);
116+
list($this->resource, $this->existsStatus) = unserialize($serialized);
74117
}
75118
}
+171Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
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 $files = array();
20+
private $className;
21+
private $classReflector;
22+
private $hash;
23+
24+
public function __construct(\ReflectionClass $classReflector)
25+
{
26+
$this->className = $classReflector->name;
27+
$this->classReflector = $classReflector;
28+
}
29+
30+
public function isFresh($timestamp)
31+
{
32+
if (null === $this->hash) {
33+
$this->hash = $this->computeHash();
34+
$this->loadFiles($this->classReflector);
35+
}
36+
37+
foreach ($this->files as $file => $v) {
38+
if (!file_exists($file)) {
39+
return false;
40+
}
41+
42+
if (@filemtime($file) > $timestamp) {
43+
return $this->hash === $this->computeHash();
44+
}
45+
}
46+
47+
return true;
48+
}
49+
50+
public function __toString()
51+
{
52+
return 'reflection.'.$this->className;
53+
}
54+
55+
public function serialize()
56+
{
57+
if (null === $this->hash) {
58+
$this->hash = $this->computeHash();
59+
$this->loadFiles($this->classReflector);
60+
}
61+
62+
return serialize(array($this->files, $this->className, $this->hash));
63+
}
64+
65+
public function unserialize($serialized)
66+
{
67+
list($this->files, $this->className, $this->hash) = unserialize($serialized);
68+
}
69+
70+
private function loadFiles(\ReflectionClass $class)
71+
{
72+
foreach ($class->getInterfaces() as $v) {
73+
$this->loadFiles($v);
74+
}
75+
do {
76+
$file = $class->getFileName();
77+
if (false !== $file && file_exists($file)) {
78+
$this->files[$file] = null;
79+
}
80+
foreach ($class->getTraits() as $v) {
81+
$this->loadFiles($v);
82+
}
83+
} while ($class = $class->getParentClass());
84+
}
85+
86+
private function computeHash()
87+
{
88+
if (null === $this->classReflector) {
89+
try {
90+
$this->classReflector = new \ReflectionClass($this->className);
91+
} catch (\ReflectionException $e) {
92+
// the class does not exist anymore
93+
return false;
94+
}
95+
}
96+
$hash = hash_init('md5');
97+
98+
foreach ($this->generateSignature($this->classReflector) as $info) {
99+
hash_update($hash, $info);
100+
}
101+
102+
return hash_final($hash);
103+
}
104+
105+
private function generateSignature(\ReflectionClass $class)
106+
{
107+
yield $class->getDocComment().$class->getModifiers();
108+
109+
if ($class->isTrait()) {
110+
yield print_r(class_uses($class->name), true);
111+
} else {
112+
yield print_r(class_parents($class->name), true);
113+
yield print_r(class_implements($class->name), true);
114+
yield print_r($class->getConstants(), true);
115+
}
116+
117+
if (!$class->isInterface()) {
118+
$defaults = $class->getDefaultProperties();
119+
120+
foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC | \ReflectionProperty::IS_PROTECTED) as $p) {
121+
yield $p->getDocComment().$p;
122+
yield print_r($defaults[$p->name], true);
123+
}
124+
}
125+
126+
if (defined('HHVM_VERSION')) {
127+
foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) {
128+
// workaround HHVM bug with variadics, see https://github.com/facebook/hhvm/issues/5762
129+
yield preg_replace('/^ @@.*/m', '', new ReflectionMethodHhvmWrapper($m->class, $m->name));
130+
}
131+
} else {
132+
foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $m) {
133+
yield preg_replace('/^ @@.*/m', '', $m);
134+
135+
$defaults = array();
136+
foreach ($m->getParameters() as $p) {
137+
$defaults[$p->name] = $p->isDefaultValueAvailable() ? $p->getDefaultValue() : null;
138+
}
139+
yield print_r($defaults, true);
140+
}
141+
}
142+
}
143+
}
144+
145+
/**
146+
* @internal
147+
*/
148+
class ReflectionMethodHhvmWrapper extends \ReflectionMethod
149+
{
150+
public function getParameters()
151+
{
152+
$params = array();
153+
154+
foreach (parent::getParameters() as $i => $p) {
155+
$params[] = new ReflectionParameterHhvmWrapper(array($this->class, $this->name), $i);
156+
}
157+
158+
return $params;
159+
}
160+
}
161+
162+
/**
163+
* @internal
164+
*/
165+
class ReflectionParameterHhvmWrapper extends \ReflectionParameter
166+
{
167+
public function getDefaultValue()
168+
{
169+
return array($this->isVariadic(), $this->isDefaultValueAvailable() ? parent::getDefaultValue() : null);
170+
}
171+
}

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Config/Tests/Resource/ClassExistenceResourceTest.php
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,24 @@ public function testIsFreshWhenClassExists()
5151

5252
$this->assertTrue($res->isFresh(time()));
5353
}
54+
55+
public function testExistsKo()
56+
{
57+
spl_autoload_register($autoloader = function ($class) use (&$loadedClass) { $loadedClass = $class; });
58+
59+
try {
60+
$res = new ClassExistenceResource('MissingFooClass');
61+
$this->assertTrue($res->isFresh(0));
62+
63+
$this->assertSame('MissingFooClass', $loadedClass);
64+
65+
$loadedClass = 123;
66+
67+
$res = new ClassExistenceResource('MissingFooClass', ClassExistenceResource::EXISTS_KO);
68+
69+
$this->assertSame(123, $loadedClass);
70+
} finally {
71+
spl_autoload_unregister($autoloader);
72+
}
73+
}
5474
}

0 commit comments

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