Merge branch '4.1'
* 4.1: [DI] fix analyzing lazy refs involved in circular loops [DI] Fix autowire inner service
This commit is contained in:
commit
00e8b493d1
@ -32,6 +32,7 @@ class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements Repe
|
|||||||
private $graph;
|
private $graph;
|
||||||
private $currentDefinition;
|
private $currentDefinition;
|
||||||
private $onlyConstructorArguments;
|
private $onlyConstructorArguments;
|
||||||
|
private $hasProxyDumper;
|
||||||
private $lazy;
|
private $lazy;
|
||||||
private $definitions;
|
private $definitions;
|
||||||
private $aliases;
|
private $aliases;
|
||||||
@ -39,9 +40,10 @@ class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements Repe
|
|||||||
/**
|
/**
|
||||||
* @param bool $onlyConstructorArguments Sets this Service Reference pass to ignore method calls
|
* @param bool $onlyConstructorArguments Sets this Service Reference pass to ignore method calls
|
||||||
*/
|
*/
|
||||||
public function __construct(bool $onlyConstructorArguments = false)
|
public function __construct(bool $onlyConstructorArguments = false, bool $hasProxyDumper = true)
|
||||||
{
|
{
|
||||||
$this->onlyConstructorArguments = $onlyConstructorArguments;
|
$this->onlyConstructorArguments = $onlyConstructorArguments;
|
||||||
|
$this->hasProxyDumper = $hasProxyDumper;
|
||||||
$this->enableExpressionProcessing();
|
$this->enableExpressionProcessing();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +100,7 @@ class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements Repe
|
|||||||
$targetId,
|
$targetId,
|
||||||
$targetDefinition,
|
$targetDefinition,
|
||||||
$value,
|
$value,
|
||||||
$this->lazy || ($targetDefinition && $targetDefinition->isLazy()),
|
$this->lazy || ($this->hasProxyDumper && $targetDefinition && $targetDefinition->isLazy()),
|
||||||
ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior()
|
ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE === $value->getInvalidBehavior()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -144,11 +144,12 @@ class AutowirePass extends AbstractRecursivePass
|
|||||||
*/
|
*/
|
||||||
private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot): array
|
private function autowireCalls(\ReflectionClass $reflectionClass, bool $isRoot): array
|
||||||
{
|
{
|
||||||
|
$this->decoratedId = null;
|
||||||
|
$this->decoratedClass = null;
|
||||||
|
$this->getPreviousValue = null;
|
||||||
|
|
||||||
if ($isRoot && ($definition = $this->container->getDefinition($this->currentId)) && $this->container->has($this->decoratedId = $definition->innerServiceId)) {
|
if ($isRoot && ($definition = $this->container->getDefinition($this->currentId)) && $this->container->has($this->decoratedId = $definition->innerServiceId)) {
|
||||||
$this->decoratedClass = $this->container->findDefinition($this->decoratedId)->getClass();
|
$this->decoratedClass = $this->container->findDefinition($this->decoratedId)->getClass();
|
||||||
} else {
|
|
||||||
$this->decoratedId = null;
|
|
||||||
$this->decoratedClass = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->methodCalls as $i => $call) {
|
foreach ($this->methodCalls as $i => $call) {
|
||||||
|
@ -17,6 +17,7 @@ use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument;
|
|||||||
use Symfony\Component\DependencyInjection\Argument\ServiceLocator;
|
use Symfony\Component\DependencyInjection\Argument\ServiceLocator;
|
||||||
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
|
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
|
||||||
use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass;
|
use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass;
|
||||||
|
use Symfony\Component\DependencyInjection\Compiler\CheckCircularReferencesPass;
|
||||||
use Symfony\Component\DependencyInjection\Compiler\ServiceReferenceGraphNode;
|
use Symfony\Component\DependencyInjection\Compiler\ServiceReferenceGraphNode;
|
||||||
use Symfony\Component\DependencyInjection\Container;
|
use Symfony\Component\DependencyInjection\Container;
|
||||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||||
@ -152,29 +153,19 @@ class PhpDumper extends Dumper
|
|||||||
$this->initializeMethodNamesMap('Container' === $baseClass ? Container::class : $baseClass);
|
$this->initializeMethodNamesMap('Container' === $baseClass ? Container::class : $baseClass);
|
||||||
|
|
||||||
if ($this->getProxyDumper() instanceof NullDumper) {
|
if ($this->getProxyDumper() instanceof NullDumper) {
|
||||||
(new AnalyzeServiceReferencesPass(true))->process($this->container);
|
(new AnalyzeServiceReferencesPass(true, false))->process($this->container);
|
||||||
$this->circularReferences = array();
|
try {
|
||||||
$checkedNodes = array();
|
(new CheckCircularReferencesPass())->process($this->container);
|
||||||
foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) {
|
} catch (ServiceCircularReferenceException $e) {
|
||||||
$currentPath = array($id => $id);
|
$path = $e->getPath();
|
||||||
$this->analyzeCircularReferences($node->getOutEdges(), $checkedNodes, $currentPath);
|
end($path);
|
||||||
}
|
$path[key($path)] .= '". Try running "composer require symfony/proxy-manager-bridge';
|
||||||
foreach ($this->circularReferences as $parent => $ids) {
|
|
||||||
$path = array($parent);
|
|
||||||
while (!isset($ids[$parent])) {
|
|
||||||
foreach ($ids as $id) {
|
|
||||||
$path[] = $id;
|
|
||||||
$ids = $this->circularReferences[$id];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$path[] = $parent.'". Try running "composer require symfony/proxy-manager-bridge';
|
|
||||||
|
|
||||||
throw new ServiceCircularReferenceException($parent, $path);
|
throw new ServiceCircularReferenceException($e->getServiceId(), $path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(new AnalyzeServiceReferencesPass())->process($this->container);
|
(new AnalyzeServiceReferencesPass(false, !$this->getProxyDumper() instanceof NullDumper))->process($this->container);
|
||||||
$this->circularReferences = array();
|
$this->circularReferences = array();
|
||||||
$this->singleUsePrivateIds = array();
|
$this->singleUsePrivateIds = array();
|
||||||
$checkedNodes = array();
|
$checkedNodes = array();
|
||||||
@ -386,7 +377,7 @@ EOTXT
|
|||||||
$node = $edge->getDestNode();
|
$node = $edge->getDestNode();
|
||||||
$id = $node->getId();
|
$id = $node->getId();
|
||||||
|
|
||||||
if ($node->getValue() && (($edge->isLazy() && !$this->getProxyDumper() instanceof NullDumper) || $edge->isWeak())) {
|
if ($node->getValue() && ($edge->isLazy() || $edge->isWeak())) {
|
||||||
// no-op
|
// no-op
|
||||||
} elseif (isset($currentPath[$id])) {
|
} elseif (isset($currentPath[$id])) {
|
||||||
foreach (array_reverse($currentPath) as $parentId) {
|
foreach (array_reverse($currentPath) as $parentId) {
|
||||||
|
@ -834,6 +834,29 @@ class AutowirePassTest extends TestCase
|
|||||||
$this->assertSame(Decorator::class.'.inner', (string) $definition->getArgument(1));
|
$this->assertSame(Decorator::class.'.inner', (string) $definition->getArgument(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testAutowireDecoratorChain()
|
||||||
|
{
|
||||||
|
$container = new ContainerBuilder();
|
||||||
|
$container->register(LoggerInterface::class, NullLogger::class);
|
||||||
|
$container->register(Decorated::class, Decorated::class);
|
||||||
|
$container
|
||||||
|
->register(Decorator::class, Decorator::class)
|
||||||
|
->setDecoratedService(Decorated::class)
|
||||||
|
->setAutowired(true)
|
||||||
|
;
|
||||||
|
$container
|
||||||
|
->register(DecoratedDecorator::class, DecoratedDecorator::class)
|
||||||
|
->setDecoratedService(Decorated::class)
|
||||||
|
->setAutowired(true)
|
||||||
|
;
|
||||||
|
|
||||||
|
(new DecoratorServicePass())->process($container);
|
||||||
|
(new AutowirePass())->process($container);
|
||||||
|
|
||||||
|
$definition = $container->getDefinition(DecoratedDecorator::class);
|
||||||
|
$this->assertSame(DecoratedDecorator::class.'.inner', (string) $definition->getArgument(0));
|
||||||
|
}
|
||||||
|
|
||||||
public function testAutowireDecoratorRenamedId()
|
public function testAutowireDecoratorRenamedId()
|
||||||
{
|
{
|
||||||
$container = new ContainerBuilder();
|
$container = new ContainerBuilder();
|
||||||
|
@ -585,7 +585,7 @@ class PhpDumperTest extends TestCase
|
|||||||
|
|
||||||
$dumper = new PhpDumper($container);
|
$dumper = new PhpDumper($container);
|
||||||
|
|
||||||
$message = 'Circular reference detected for service "bar", path: "bar -> foo -> bar". Try running "composer require symfony/proxy-manager-bridge".';
|
$message = 'Circular reference detected for service "foo", path: "foo -> bar -> foo". Try running "composer require symfony/proxy-manager-bridge".';
|
||||||
if (method_exists($this, 'expectException')) {
|
if (method_exists($this, 'expectException')) {
|
||||||
$this->expectException(ServiceCircularReferenceException::class);
|
$this->expectException(ServiceCircularReferenceException::class);
|
||||||
$this->expectExceptionMessage($message);
|
$this->expectExceptionMessage($message);
|
||||||
|
@ -373,6 +373,13 @@ class Decorator implements DecoratorInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DecoratedDecorator implements DecoratorInterface
|
||||||
|
{
|
||||||
|
public function __construct(DecoratorInterface $decorator)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class NonAutowirableDecorator implements DecoratorInterface
|
class NonAutowirableDecorator implements DecoratorInterface
|
||||||
{
|
{
|
||||||
public function __construct(LoggerInterface $logger, DecoratorInterface $decorated1, DecoratorInterface $decorated2)
|
public function __construct(LoggerInterface $logger, DecoratorInterface $decorated1, DecoratorInterface $decorated2)
|
||||||
|
Reference in New Issue
Block a user