Skip to content

Commit 547185f

Browse files
committed
bug symfony#23573 [Config] Make ClassExistenceResource throw on invalid parents (nicolas-grekas)
This PR was merged into the 3.3 branch. Discussion ---------- [Config] Make ClassExistenceResource throw on invalid parents | Q | A | ------------- | --- | Branch? | 3.3 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | symfony#23564 | License | MIT | Doc PR | - Let's throw a more specific exception when a parent class/interface/trait is missing. Fine tunes symfony#23041 Commits ------- 53b0190 [Config] Make ClassExistenceResource throw on invalid parents
2 parents cd25294 + 53b0190 commit 547185f

File tree

6 files changed

+40
-15
lines changed

6 files changed

+40
-15
lines changed

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

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class ClassExistenceResource implements SelfCheckingResourceInterface, \Serializ
2525
private $exists;
2626

2727
private static $autoloadLevel = 0;
28+
private static $autoloadedClass;
2829
private static $existsCache = array();
2930

3031
/**
@@ -57,6 +58,8 @@ public function getResource()
5758

5859
/**
5960
* {@inheritdoc}
61+
*
62+
* @throws \ReflectionException when a parent class/interface/trait is not found
6063
*/
6164
public function isFresh($timestamp)
6265
{
@@ -68,12 +71,13 @@ public function isFresh($timestamp)
6871
if (!self::$autoloadLevel++) {
6972
spl_autoload_register(__CLASS__.'::throwOnRequiredClass');
7073
}
74+
$autoloadedClass = self::$autoloadedClass;
75+
self::$autoloadedClass = $this->resource;
7176

7277
try {
7378
$exists = class_exists($this->resource) || interface_exists($this->resource, false) || trait_exists($this->resource, false);
74-
} catch (\ReflectionException $e) {
75-
$exists = false;
7679
} finally {
80+
self::$autoloadedClass = $autoloadedClass;
7781
if (!--self::$autoloadLevel) {
7882
spl_autoload_unregister(__CLASS__.'::throwOnRequiredClass');
7983
}
@@ -112,7 +116,10 @@ public function unserialize($serialized)
112116
*/
113117
private static function throwOnRequiredClass($class)
114118
{
115-
$e = new \ReflectionException("Class $class does not exist");
119+
if (self::$autoloadedClass === $class) {
120+
return;
121+
}
122+
$e = new \ReflectionException("Class $class not found");
116123
$trace = $e->getTrace();
117124
$autoloadFrame = array(
118125
'function' => 'spl_autoload_call',
@@ -138,6 +145,18 @@ private static function throwOnRequiredClass($class)
138145
case 'is_callable':
139146
return;
140147
}
148+
149+
$props = array(
150+
'file' => $trace[$i]['file'],
151+
'line' => $trace[$i]['line'],
152+
'trace' => array_slice($trace, 0, 1 + $i),
153+
);
154+
155+
foreach ($props as $p => $v) {
156+
$r = new \ReflectionProperty('Exception', $p);
157+
$r->setAccessible(true);
158+
$r->setValue($e, $v);
159+
}
141160
}
142161

143162
throw $e;

src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ private function doProcessValue($value, $isRoot = false)
124124
if (!$value instanceof Definition || !$value->isAutowired() || $value->isAbstract() || !$value->getClass()) {
125125
return $value;
126126
}
127-
if (!$reflectionClass = $this->container->getReflectionClass($value->getClass())) {
128-
$this->container->log($this, sprintf('Skipping service "%s": Class or interface "%s" does not exist.', $this->currentId, $value->getClass()));
127+
if (!$reflectionClass = $this->container->getReflectionClass($value->getClass(), false)) {
128+
$this->container->log($this, sprintf('Skipping service "%s": Class or interface "%s" cannot be loaded.', $this->currentId, $value->getClass()));
129129

130130
return $value;
131131
}
@@ -388,7 +388,7 @@ private function populateAvailableType($id, Definition $definition)
388388
unset($this->ambiguousServiceTypes[$type]);
389389
}
390390

391-
if ($definition->isDeprecated() || !$reflectionClass = $this->container->getReflectionClass($definition->getClass())) {
391+
if ($definition->isDeprecated() || !$reflectionClass = $this->container->getReflectionClass($definition->getClass(), false)) {
392392
return;
393393
}
394394

@@ -444,7 +444,7 @@ private function set($type, $id)
444444
*/
445445
private function createAutowiredDefinition($type)
446446
{
447-
if (!($typeHint = $this->container->getReflectionClass($type)) || !$typeHint->isInstantiable()) {
447+
if (!($typeHint = $this->container->getReflectionClass($type, false)) || !$typeHint->isInstantiable()) {
448448
return;
449449
}
450450

@@ -478,8 +478,8 @@ private function createAutowiredDefinition($type)
478478

479479
private function createTypeNotFoundMessage(TypedReference $reference, $label)
480480
{
481-
if (!$r = $this->container->getReflectionClass($type = $reference->getType())) {
482-
$message = sprintf('has type "%s" but this class does not exist.', $type);
481+
if (!$r = $this->container->getReflectionClass($type = $reference->getType(), false)) {
482+
$message = sprintf('has type "%s" but this class cannot be loaded.', $type);
483483
} else {
484484
$message = $this->container->has($type) ? 'this service is abstract' : 'no such service exists';
485485
$message = sprintf('references %s "%s" but %s.%s', $r->isInterface() ? 'interface' : 'class', $type, $message, $this->createTypeAlternatives($reference));

src/Symfony/Component/DependencyInjection/Compiler/FactoryReturnTypePass.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ private function updateDefinition(ContainerBuilder $container, $id, Definition $
8181
$class = $factory[0];
8282
}
8383

84-
if (!$m = $container->getReflectionClass($class)) {
84+
if (!$m = $container->getReflectionClass($class, false)) {
8585
return;
8686
}
8787
try {

src/Symfony/Component/DependencyInjection/Compiler/ResolveInstanceofConditionalsPass.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ private function processDefinition(ContainerBuilder $container, $id, Definition
6666
$instanceofTags = array();
6767

6868
foreach ($conditionals as $interface => $instanceofDefs) {
69-
if ($interface !== $class && (!$container->getReflectionClass($class))) {
69+
if ($interface !== $class && (!$container->getReflectionClass($class, false))) {
7070
continue;
7171
}
7272

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,12 +337,15 @@ public function addClassResource(\ReflectionClass $class)
337337
* Retrieves the requested reflection class and registers it for resource tracking.
338338
*
339339
* @param string $class
340+
* @param bool $throw
340341
*
341342
* @return \ReflectionClass|null
342343
*
344+
* @throws \ReflectionException when a parent class/interface/trait is not found and $throw is true
345+
*
343346
* @final
344347
*/
345-
public function getReflectionClass($class)
348+
public function getReflectionClass($class, $throw = true)
346349
{
347350
if (!$class = $this->getParameterBag()->resolveValue($class)) {
348351
return;
@@ -357,6 +360,9 @@ public function getReflectionClass($class)
357360
$classReflector = $resource->isFresh(0) ? false : new \ReflectionClass($class);
358361
}
359362
} catch (\ReflectionException $e) {
363+
if ($throw) {
364+
throw $e;
365+
}
360366
$classReflector = false;
361367
}
362368

src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ public function testDontTriggerAutowiring()
359359

360360
/**
361361
* @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
362-
* @expectedExceptionMessage Cannot autowire service "a": argument "$r" of method "Symfony\Component\DependencyInjection\Tests\Compiler\BadTypeHintedArgument::__construct()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\NotARealClass" but this class does not exist.
362+
* @expectedExceptionMessage Cannot autowire service "a": argument "$r" of method "Symfony\Component\DependencyInjection\Tests\Compiler\BadTypeHintedArgument::__construct()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\NotARealClass" but this class cannot be loaded.
363363
*/
364364
public function testClassNotFoundThrowsException()
365365
{
@@ -374,7 +374,7 @@ public function testClassNotFoundThrowsException()
374374

375375
/**
376376
* @expectedException \Symfony\Component\DependencyInjection\Exception\AutowiringFailedException
377-
* @expectedExceptionMessage Cannot autowire service "a": argument "$r" of method "Symfony\Component\DependencyInjection\Tests\Compiler\BadParentTypeHintedArgument::__construct()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\OptionalServiceClass" but this class does not exist.
377+
* @expectedExceptionMessage Cannot autowire service "a": argument "$r" of method "Symfony\Component\DependencyInjection\Tests\Compiler\BadParentTypeHintedArgument::__construct()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\OptionalServiceClass" but this class cannot be loaded.
378378
*/
379379
public function testParentClassNotFoundThrowsException()
380380
{
@@ -744,7 +744,7 @@ public function testNotWireableCalls($method, $expectedMsg)
744744
public function provideNotWireableCalls()
745745
{
746746
return array(
747-
array('setNotAutowireable', 'Cannot autowire service "foo": argument "$n" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setNotAutowireable()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\NotARealClass" but this class does not exist.'),
747+
array('setNotAutowireable', 'Cannot autowire service "foo": argument "$n" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setNotAutowireable()" has type "Symfony\Component\DependencyInjection\Tests\Compiler\NotARealClass" but this class cannot be loaded.'),
748748
array('setDifferentNamespace', 'Cannot autowire service "foo": argument "$n" of method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setDifferentNamespace()" references class "stdClass" but no such service exists. It cannot be auto-registered because it is from a different root namespace.'),
749749
array(null, 'Cannot autowire service "foo": method "Symfony\Component\DependencyInjection\Tests\Compiler\NotWireable::setProtectedMethod()" must be public.'),
750750
);

0 commit comments

Comments
 (0)