diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveClassPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveClassPass.php index b5c4bba2d516..c40d6f5d3d0f 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveClassPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveClassPass.php @@ -13,6 +13,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ChildDefinition; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; /** * @author Nicolas Grekas @@ -27,10 +28,13 @@ class ResolveClassPass implements CompilerPassInterface public function process(ContainerBuilder $container) { foreach ($container->getDefinitions() as $id => $definition) { - if ($definition instanceof ChildDefinition || $definition->isSynthetic() || null !== $definition->getClass()) { + if ($definition->isSynthetic() || null !== $definition->getClass()) { continue; } if (preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+(?:\\\\[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*+)++$/', $id)) { + if ($definition instanceof ChildDefinition && !class_exists($id)) { + throw new InvalidArgumentException(sprintf('Service definition "%s" has a parent but no class, and its name looks like a FQCN. Either the class is missing or you want to inherit it from the parent service. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class.', $id)); + } $this->changes[strtolower($id)] = $id; $definition->setClass($id); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveClassPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveClassPassTest.php index 8a4d99e774d8..acbcf1dda2d8 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveClassPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveClassPassTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\ResolveClassPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Tests\Fixtures\CaseSensitiveClass; @@ -23,11 +24,10 @@ class ResolveClassPassTest extends TestCase */ public function testResolveClassFromId($serviceId) { - $pass = new ResolveClassPass(); $container = new ContainerBuilder(); $def = $container->register($serviceId); - $pass->process($container); + (new ResolveClassPass())->process($container); $this->assertSame($serviceId, $def->getClass()); } @@ -43,11 +43,10 @@ public function provideValidClassId() */ public function testWontResolveClassFromId($serviceId) { - $pass = new ResolveClassPass(); $container = new ContainerBuilder(); $def = $container->register($serviceId); - $pass->process($container); + (new ResolveClassPass())->process($container); $this->assertNull($def->getClass()); } @@ -58,4 +57,41 @@ public function provideInvalidClassId() yield array('bar'); yield array('\DateTime'); } + + public function testNonFqcnChildDefinition() + { + $container = new ContainerBuilder(); + $parent = $container->register('App\Foo', null); + $child = $container->setDefinition('App\Foo.child', new ChildDefinition('App\Foo')); + + (new ResolveClassPass())->process($container); + + $this->assertSame('App\Foo', $parent->getClass()); + $this->assertNull($child->getClass()); + } + + public function testClassFoundChildDefinition() + { + $container = new ContainerBuilder(); + $parent = $container->register('App\Foo', null); + $child = $container->setDefinition(self::class, new ChildDefinition('App\Foo')); + + (new ResolveClassPass())->process($container); + + $this->assertSame('App\Foo', $parent->getClass()); + $this->assertSame(self::class, $child->getClass()); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException + * @expectedExceptionMessage Service definition "App\Foo\Child" has a parent but no class, and its name looks like a FQCN. Either the class is missing or you want to inherit it from the parent service. To resolve this ambiguity, please rename this service to a non-FQCN (e.g. using dots), or create the missing class. + */ + public function testAmbiguousChildDefinition() + { + $container = new ContainerBuilder(); + $parent = $container->register('App\Foo', null); + $child = $container->setDefinition('App\Foo\Child', new ChildDefinition('App\Foo')); + + (new ResolveClassPass())->process($container); + } }