diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php index 31e407cd63..2ee9427a15 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php @@ -21,16 +21,30 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; class AutowireExceptionPass implements CompilerPassInterface { private $autowirePass; + private $inlineServicePass; - public function __construct(AutowirePass $autowirePass) + public function __construct(AutowirePass $autowirePass, InlineServiceDefinitionsPass $inlineServicePass) { $this->autowirePass = $autowirePass; + $this->inlineServicePass = $inlineServicePass; } public function process(ContainerBuilder $container) { - foreach ($this->autowirePass->getAutowiringExceptions() as $exception) { - if ($container->hasDefinition($exception->getServiceId())) { + // the pass should only be run once + if (null === $this->autowirePass || null === $this->inlineServicePass) { + return; + } + + $inlinedIds = $this->inlineServicePass->getInlinedServiceIds(); + $exceptions = $this->autowirePass->getAutowiringExceptions(); + + // free up references + $this->autowirePass = null; + $this->inlineServicePass = null; + + foreach ($exceptions as $exception) { + if ($container->hasDefinition($exception->getServiceId()) || in_array($exception->getServiceId(), $inlinedIds)) { throw $exception; } } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php index cec68cbb57..b084d7e4dc 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php @@ -23,6 +23,7 @@ use Symfony\Component\DependencyInjection\Reference; class InlineServiceDefinitionsPass extends AbstractRecursivePass implements RepeatablePassInterface { private $repeatedPass; + private $inlinedServiceIds = array(); /** * {@inheritdoc} @@ -32,6 +33,16 @@ class InlineServiceDefinitionsPass extends AbstractRecursivePass implements Repe $this->repeatedPass = $repeatedPass; } + /** + * Returns an array of all services inlined by this pass. + * + * @return array Service id strings + */ + public function getInlinedServiceIds() + { + return $this->inlinedServiceIds; + } + /** * {@inheritdoc} */ @@ -46,6 +57,7 @@ class InlineServiceDefinitionsPass extends AbstractRecursivePass implements Repe if ($this->isInlineableDefinition($id, $definition, $this->container->getCompiler()->getServiceReferenceGraph())) { $this->container->log($this, sprintf('Inlined service "%s" to "%s".', $id, $this->currentId)); + $this->inlinedServiceIds[] = $id; if ($definition->isShared()) { return $definition; diff --git a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php index 1e80a6060a..5ee8f98851 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -73,11 +73,11 @@ class PassConfig new RemoveAbstractDefinitionsPass(), new RepeatedPass(array( new AnalyzeServiceReferencesPass(), - new InlineServiceDefinitionsPass(), + $inlinedServicePass = new InlineServiceDefinitionsPass(), new AnalyzeServiceReferencesPass(), new RemoveUnusedDefinitionsPass(), )), - new AutowireExceptionPass($autowirePass), + new AutowireExceptionPass($autowirePass, $inlinedServicePass), new CheckExceptionOnInvalidReferenceBehaviorPass(), )); } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireExceptionPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireExceptionPassTest.php index 4859db3a64..092b6401c4 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireExceptionPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireExceptionPassTest.php @@ -14,6 +14,7 @@ namespace Symfony\Component\DependencyInjection\Tests\Compiler; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\Compiler\AutowireExceptionPass; use Symfony\Component\DependencyInjection\Compiler\AutowirePass; +use Symfony\Component\DependencyInjection\Compiler\InlineServiceDefinitionsPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\AutowiringFailedException; @@ -29,10 +30,45 @@ class AutowireExceptionPassTest extends TestCase ->method('getAutowiringExceptions') ->will($this->returnValue(array($autowireException))); + $inlinePass = $this->getMockBuilder(InlineServiceDefinitionsPass::class) + ->getMock(); + $inlinePass->expects($this->any()) + ->method('getInlinedServiceIds') + ->will($this->returnValue(array())); + $container = new ContainerBuilder(); $container->register('foo_service_id'); - $pass = new AutowireExceptionPass($autowirePass); + $pass = new AutowireExceptionPass($autowirePass, $inlinePass); + + try { + $pass->process($container); + $this->fail('->process() should throw the exception if the service id exists'); + } catch (\Exception $e) { + $this->assertSame($autowireException, $e); + } + } + + public function testThrowExceptionIfServiceInlined() + { + $autowirePass = $this->getMockBuilder(AutowirePass::class) + ->getMock(); + + $autowireException = new AutowiringFailedException('foo_service_id', 'An autowiring exception message'); + $autowirePass->expects($this->any()) + ->method('getAutowiringExceptions') + ->will($this->returnValue(array($autowireException))); + + $inlinePass = $this->getMockBuilder(InlineServiceDefinitionsPass::class) + ->getMock(); + $inlinePass->expects($this->any()) + ->method('getInlinedServiceIds') + ->will($this->returnValue(array('foo_service_id'))); + + // don't register the foo_service_id service + $container = new ContainerBuilder(); + + $pass = new AutowireExceptionPass($autowirePass, $inlinePass); try { $pass->process($container); @@ -52,9 +88,15 @@ class AutowireExceptionPassTest extends TestCase ->method('getAutowiringExceptions') ->will($this->returnValue(array($autowireException))); + $inlinePass = $this->getMockBuilder(InlineServiceDefinitionsPass::class) + ->getMock(); + $inlinePass->expects($this->any()) + ->method('getInlinedServiceIds') + ->will($this->returnValue(array())); + $container = new ContainerBuilder(); - $pass = new AutowireExceptionPass($autowirePass); + $pass = new AutowireExceptionPass($autowirePass, $inlinePass); $pass->process($container); // mark the test as passed diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php index c5e7272235..d241758b04 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php @@ -252,6 +252,30 @@ class InlineServiceDefinitionsPassTest extends TestCase $this->assertSame('inline', (string) $values[0]); } + public function testGetInlinedServiceIds() + { + $container = new ContainerBuilder(); + $container + ->register('inlinable.service') + ->setPublic(false) + ; + $container + ->register('non_inlinable.service') + ->setPublic(true) + ; + + $container + ->register('service') + ->setArguments(array(new Reference('inlinable.service'))) + ; + + $inlinePass = new InlineServiceDefinitionsPass(); + $repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), $inlinePass)); + $repeatedPass->process($container); + + $this->assertEquals(array('inlinable.service'), $inlinePass->getInlinedServiceIds()); + } + protected function process(ContainerBuilder $container) { $repeatedPass = new RepeatedPass(array(new AnalyzeServiceReferencesPass(), new InlineServiceDefinitionsPass()));