From 75b11d5d2369e74277e2361cd0ba58686bd83965 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 22 May 2017 10:33:24 -0400 Subject: [PATCH 1/2] Fixing a bug where inlined services did not get autowiring exceptions --- .../Compiler/AutowireExceptionPass.php | 7 ++- .../Compiler/InlineServiceDefinitionsPass.php | 13 ++++++ .../Compiler/PassConfig.php | 4 +- .../Compiler/AutowireExceptionPassTest.php | 46 ++++++++++++++++++- .../InlineServiceDefinitionsPassTest.php | 24 ++++++++++ 5 files changed, 88 insertions(+), 6 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php index 31e407cd630a..cf70fabb4e74 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php @@ -21,16 +21,19 @@ 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) { + $inlinedIds = $this->inlineServicePass->getInlinedServiceIds(); foreach ($this->autowirePass->getAutowiringExceptions() as $exception) { - if ($container->hasDefinition($exception->getServiceId())) { + 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 cec68cbb573a..1ceee6d13cda 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; @@ -23,6 +24,7 @@ class InlineServiceDefinitionsPass extends AbstractRecursivePass implements RepeatablePassInterface { private $repeatedPass; + private $inlinedServiceIds = array(); /** * {@inheritdoc} @@ -32,6 +34,16 @@ public function setRepeatedPass(RepeatedPass $repeatedPass) $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 +58,7 @@ protected function processValue($value, $isRoot = false) 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 1e80a6060af6..5ee8f9885143 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/PassConfig.php @@ -73,11 +73,11 @@ public function __construct() 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 4859db3a64e0..092b6401c48e 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireExceptionPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowireExceptionPassTest.php @@ -14,6 +14,7 @@ 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 @@ public function testThrowsException() ->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 @@ public function testNoExceptionIfServiceRemoved() ->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 c5e7272235f9..d241758b0442 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/InlineServiceDefinitionsPassTest.php @@ -252,6 +252,30 @@ public function testProcessDoesNotSetLazyArgumentValuesAfterInlining() $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())); From aeb38f642a73e37635c91b73d49c682e1173def6 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Mon, 22 May 2017 11:18:07 -0400 Subject: [PATCH 2/2] Freeing up some memory references --- .../Compiler/AutowireExceptionPass.php | 13 ++++++++++++- .../Compiler/InlineServiceDefinitionsPass.php | 1 - 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php index cf70fabb4e74..2ee9427a1509 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowireExceptionPass.php @@ -31,8 +31,19 @@ public function __construct(AutowirePass $autowirePass, InlineServiceDefinitions public function process(ContainerBuilder $container) { + // the pass should only be run once + if (null === $this->autowirePass || null === $this->inlineServicePass) { + return; + } + $inlinedIds = $this->inlineServicePass->getInlinedServiceIds(); - foreach ($this->autowirePass->getAutowiringExceptions() as $exception) { + $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 1ceee6d13cda..b084d7e4dce2 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/InlineServiceDefinitionsPass.php @@ -12,7 +12,6 @@ namespace Symfony\Component\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference;