Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 37e4493

Browse files
[DI][Config] Add & use ReflectionClassResource
1 parent 35a49fb commit 37e4493

File tree

26 files changed

+616
-176
lines changed

26 files changed

+616
-176
lines changed

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

Lines 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 <[email protected]>
@@ -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

Lines 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

Lines 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

Lines 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

Lines 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
}
Lines 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 <[email protected]>
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 <[email protected]>
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

Lines 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)