[DI] Fix dumping Doctrine-like service graphs (bis)
This commit is contained in:
parent
ee491444f4
commit
a37f3e0807
@ -122,7 +122,7 @@ class AnalyzeServiceReferencesPass extends AbstractRecursivePass implements Repe
|
|||||||
$this->lazy = false;
|
$this->lazy = false;
|
||||||
|
|
||||||
$byConstructor = $this->byConstructor;
|
$byConstructor = $this->byConstructor;
|
||||||
$this->byConstructor = true;
|
$this->byConstructor = $isRoot || $byConstructor;
|
||||||
$this->processValue($value->getFactory());
|
$this->processValue($value->getFactory());
|
||||||
$this->processValue($value->getArguments());
|
$this->processValue($value->getArguments());
|
||||||
$this->byConstructor = $byConstructor;
|
$this->byConstructor = $byConstructor;
|
||||||
|
@ -302,10 +302,10 @@ EOF;
|
|||||||
return $this->proxyDumper;
|
return $this->proxyDumper;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function analyzeCircularReferences($sourceId, array $edges, &$checkedNodes, &$currentPath = [])
|
private function analyzeCircularReferences($sourceId, array $edges, &$checkedNodes, &$currentPath = [], $byConstructor = true)
|
||||||
{
|
{
|
||||||
$checkedNodes[$sourceId] = true;
|
$checkedNodes[$sourceId] = true;
|
||||||
$currentPath[$sourceId] = $sourceId;
|
$currentPath[$sourceId] = $byConstructor;
|
||||||
|
|
||||||
foreach ($edges as $edge) {
|
foreach ($edges as $edge) {
|
||||||
$node = $edge->getDestNode();
|
$node = $edge->getDestNode();
|
||||||
@ -314,44 +314,52 @@ EOF;
|
|||||||
if (!$node->getValue() instanceof Definition || $sourceId === $id || $edge->isLazy() || $edge->isWeak()) {
|
if (!$node->getValue() instanceof Definition || $sourceId === $id || $edge->isLazy() || $edge->isWeak()) {
|
||||||
// no-op
|
// no-op
|
||||||
} elseif (isset($currentPath[$id])) {
|
} elseif (isset($currentPath[$id])) {
|
||||||
$currentId = $id;
|
$this->addCircularReferences($id, $currentPath, $edge->isReferencedByConstructor());
|
||||||
foreach (array_reverse($currentPath) as $parentId) {
|
|
||||||
$this->circularReferences[$parentId][$currentId] = $currentId;
|
|
||||||
if ($parentId === $id) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$currentId = $parentId;
|
|
||||||
}
|
|
||||||
} elseif (!isset($checkedNodes[$id])) {
|
} elseif (!isset($checkedNodes[$id])) {
|
||||||
$this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes, $currentPath);
|
$this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes, $currentPath, $edge->isReferencedByConstructor());
|
||||||
} elseif (isset($this->circularReferences[$id])) {
|
} elseif (isset($this->circularReferences[$id])) {
|
||||||
$this->connectCircularReferences($id, $currentPath);
|
$this->connectCircularReferences($id, $currentPath, $edge->isReferencedByConstructor());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unset($currentPath[$sourceId]);
|
unset($currentPath[$sourceId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function connectCircularReferences($sourceId, &$currentPath, &$subPath = [])
|
private function connectCircularReferences($sourceId, &$currentPath, $byConstructor, &$subPath = [])
|
||||||
{
|
{
|
||||||
$subPath[$sourceId] = $sourceId;
|
$currentPath[$sourceId] = $subPath[$sourceId] = $byConstructor;
|
||||||
$currentPath[$sourceId] = $sourceId;
|
|
||||||
|
|
||||||
foreach ($this->circularReferences[$sourceId] as $id) {
|
foreach ($this->circularReferences[$sourceId] as $id => $byConstructor) {
|
||||||
if (isset($currentPath[$id])) {
|
if (isset($currentPath[$id])) {
|
||||||
$currentId = $id;
|
$this->addCircularReferences($id, $currentPath, $byConstructor);
|
||||||
foreach (array_reverse($currentPath) as $parentId) {
|
|
||||||
$this->circularReferences[$parentId][$currentId] = $currentId;
|
|
||||||
if ($parentId === $id) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$currentId = $parentId;
|
|
||||||
}
|
|
||||||
} elseif (!isset($subPath[$id]) && isset($this->circularReferences[$id])) {
|
} elseif (!isset($subPath[$id]) && isset($this->circularReferences[$id])) {
|
||||||
$this->connectCircularReferences($id, $currentPath, $subPath);
|
$this->connectCircularReferences($id, $currentPath, $byConstructor, $subPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unset($currentPath[$sourceId]);
|
unset($currentPath[$sourceId], $subPath[$sourceId]);
|
||||||
unset($subPath[$sourceId]);
|
}
|
||||||
|
|
||||||
|
private function addCircularReferences($id, $currentPath, $byConstructor)
|
||||||
|
{
|
||||||
|
$currentPath[$id] = $byConstructor;
|
||||||
|
$circularRefs = [];
|
||||||
|
|
||||||
|
foreach (array_reverse($currentPath) as $parentId => $v) {
|
||||||
|
$byConstructor = $byConstructor && $v;
|
||||||
|
$circularRefs[] = $parentId;
|
||||||
|
|
||||||
|
if ($parentId === $id) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentId = $id;
|
||||||
|
foreach ($circularRefs as $parentId) {
|
||||||
|
if (empty($this->circularReferences[$parentId][$currentId])) {
|
||||||
|
$this->circularReferences[$parentId][$currentId] = $byConstructor;
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentId = $parentId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function collectLineage($class, array &$lineage)
|
private function collectLineage($class, array &$lineage)
|
||||||
@ -661,7 +669,6 @@ EOF;
|
|||||||
$autowired = $definition->isAutowired() ? ' autowired' : '';
|
$autowired = $definition->isAutowired() ? ' autowired' : '';
|
||||||
|
|
||||||
if ($definition->isLazy()) {
|
if ($definition->isLazy()) {
|
||||||
unset($this->circularReferences[$id]);
|
|
||||||
$lazyInitialization = '$lazyLoad = true';
|
$lazyInitialization = '$lazyLoad = true';
|
||||||
} else {
|
} else {
|
||||||
$lazyInitialization = '';
|
$lazyInitialization = '';
|
||||||
@ -736,12 +743,12 @@ EOF;
|
|||||||
|
|
||||||
private function addInlineReference($id, Definition $definition, $targetId, $forConstructor)
|
private function addInlineReference($id, Definition $definition, $targetId, $forConstructor)
|
||||||
{
|
{
|
||||||
list($callCount, $behavior) = $this->serviceCalls[$targetId];
|
|
||||||
|
|
||||||
while ($this->container->hasAlias($targetId)) {
|
while ($this->container->hasAlias($targetId)) {
|
||||||
$targetId = (string) $this->container->getAlias($targetId);
|
$targetId = (string) $this->container->getAlias($targetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
list($callCount, $behavior) = $this->serviceCalls[$targetId];
|
||||||
|
|
||||||
if ($id === $targetId) {
|
if ($id === $targetId) {
|
||||||
return $this->addInlineService($id, $definition, $definition);
|
return $this->addInlineService($id, $definition, $definition);
|
||||||
}
|
}
|
||||||
@ -750,9 +757,13 @@ EOF;
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
$hasSelfRef = isset($this->circularReferences[$id][$targetId]);
|
$hasSelfRef = isset($this->circularReferences[$id][$targetId]) && !isset($this->definitionVariables[$definition]);
|
||||||
$forConstructor = $forConstructor && !isset($this->definitionVariables[$definition]);
|
|
||||||
$code = $hasSelfRef && !$forConstructor ? $this->addInlineService($id, $definition, $definition) : '';
|
if ($hasSelfRef && !$forConstructor && !$forConstructor = !$this->circularReferences[$id][$targetId]) {
|
||||||
|
$code = $this->addInlineService($id, $definition, $definition);
|
||||||
|
} else {
|
||||||
|
$code = '';
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($this->referenceVariables[$targetId]) || (2 > $callCount && (!$hasSelfRef || !$forConstructor))) {
|
if (isset($this->referenceVariables[$targetId]) || (2 > $callCount && (!$hasSelfRef || !$forConstructor))) {
|
||||||
return $code;
|
return $code;
|
||||||
@ -785,15 +796,23 @@ EOTXT
|
|||||||
|
|
||||||
private function addInlineService($id, Definition $definition, Definition $inlineDef = null, $forConstructor = true)
|
private function addInlineService($id, Definition $definition, Definition $inlineDef = null, $forConstructor = true)
|
||||||
{
|
{
|
||||||
$isSimpleInstance = $isRootInstance = null === $inlineDef;
|
$code = '';
|
||||||
|
|
||||||
|
if ($isSimpleInstance = $isRootInstance = null === $inlineDef) {
|
||||||
|
foreach ($this->serviceCalls as $targetId => list($callCount, $behavior, $byConstructor)) {
|
||||||
|
if ($byConstructor && isset($this->circularReferences[$id][$targetId]) && !$this->circularReferences[$id][$targetId]) {
|
||||||
|
$code .= $this->addInlineReference($id, $definition, $targetId, $forConstructor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($this->definitionVariables[$inlineDef = $inlineDef ?: $definition])) {
|
if (isset($this->definitionVariables[$inlineDef = $inlineDef ?: $definition])) {
|
||||||
return '';
|
return $code;
|
||||||
}
|
}
|
||||||
|
|
||||||
$arguments = [$inlineDef->getArguments(), $inlineDef->getFactory()];
|
$arguments = [$inlineDef->getArguments(), $inlineDef->getFactory()];
|
||||||
|
|
||||||
$code = $this->addInlineVariables($id, $definition, $arguments, $forConstructor);
|
$code .= $this->addInlineVariables($id, $definition, $arguments, $forConstructor);
|
||||||
|
|
||||||
if ($arguments = array_filter([$inlineDef->getProperties(), $inlineDef->getMethodCalls(), $inlineDef->getConfigurator()])) {
|
if ($arguments = array_filter([$inlineDef->getProperties(), $inlineDef->getMethodCalls(), $inlineDef->getConfigurator()])) {
|
||||||
$isSimpleInstance = false;
|
$isSimpleInstance = false;
|
||||||
@ -1550,7 +1569,7 @@ EOF;
|
|||||||
return implode(' && ', $conditions);
|
return implode(' && ', $conditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage $definitions = null, array &$calls = [])
|
private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage $definitions = null, array &$calls = [], $byConstructor = null)
|
||||||
{
|
{
|
||||||
if (null === $definitions) {
|
if (null === $definitions) {
|
||||||
$definitions = new \SplObjectStorage();
|
$definitions = new \SplObjectStorage();
|
||||||
@ -1558,12 +1577,16 @@ EOF;
|
|||||||
|
|
||||||
foreach ($arguments as $argument) {
|
foreach ($arguments as $argument) {
|
||||||
if (\is_array($argument)) {
|
if (\is_array($argument)) {
|
||||||
$this->getDefinitionsFromArguments($argument, $definitions, $calls);
|
$this->getDefinitionsFromArguments($argument, $definitions, $calls, $byConstructor);
|
||||||
} elseif ($argument instanceof Reference) {
|
} elseif ($argument instanceof Reference) {
|
||||||
$id = $this->container->normalizeId($argument);
|
$id = $this->container->normalizeId($argument);
|
||||||
|
|
||||||
|
while ($this->container->hasAlias($id)) {
|
||||||
|
$id = (string) $this->container->getAlias($id);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isset($calls[$id])) {
|
if (!isset($calls[$id])) {
|
||||||
$calls[$id] = [0, $argument->getInvalidBehavior()];
|
$calls[$id] = [0, $argument->getInvalidBehavior(), $byConstructor];
|
||||||
} else {
|
} else {
|
||||||
$calls[$id][1] = min($calls[$id][1], $argument->getInvalidBehavior());
|
$calls[$id][1] = min($calls[$id][1], $argument->getInvalidBehavior());
|
||||||
}
|
}
|
||||||
@ -1575,8 +1598,10 @@ EOF;
|
|||||||
$definitions[$argument] = 1 + $definitions[$argument];
|
$definitions[$argument] = 1 + $definitions[$argument];
|
||||||
} else {
|
} else {
|
||||||
$definitions[$argument] = 1;
|
$definitions[$argument] = 1;
|
||||||
$arguments = [$argument->getArguments(), $argument->getFactory(), $argument->getProperties(), $argument->getMethodCalls(), $argument->getConfigurator()];
|
$arguments = [$argument->getArguments(), $argument->getFactory()];
|
||||||
$this->getDefinitionsFromArguments($arguments, $definitions, $calls);
|
$this->getDefinitionsFromArguments($arguments, $definitions, $calls, null === $byConstructor || $byConstructor);
|
||||||
|
$arguments = [$argument->getProperties(), $argument->getMethodCalls(), $argument->getConfigurator()];
|
||||||
|
$this->getDefinitionsFromArguments($arguments, $definitions, $calls, null !== $byConstructor && $byConstructor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1717,6 +1742,11 @@ EOF;
|
|||||||
return '$'.$value;
|
return '$'.$value;
|
||||||
} elseif ($value instanceof Reference) {
|
} elseif ($value instanceof Reference) {
|
||||||
$id = $this->container->normalizeId($value);
|
$id = $this->container->normalizeId($value);
|
||||||
|
|
||||||
|
while ($this->container->hasAlias($id)) {
|
||||||
|
$id = (string) $this->container->getAlias($id);
|
||||||
|
}
|
||||||
|
|
||||||
if (null !== $this->referenceVariables && isset($this->referenceVariables[$id])) {
|
if (null !== $this->referenceVariables && isset($this->referenceVariables[$id])) {
|
||||||
return $this->dumpValue($this->referenceVariables[$id], $interpolate);
|
return $this->dumpValue($this->referenceVariables[$id], $interpolate);
|
||||||
}
|
}
|
||||||
|
@ -369,10 +369,15 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Private extends Container
|
|||||||
*/
|
*/
|
||||||
protected function getManager3Service($lazyLoad = true)
|
protected function getManager3Service($lazyLoad = true)
|
||||||
{
|
{
|
||||||
$a = new \stdClass();
|
$a = ${($_ = isset($this->services['listener3']) ? $this->services['listener3'] : $this->getListener3Service()) && false ?: '_'};
|
||||||
$a->listener = [0 => ${($_ = isset($this->services['listener3']) ? $this->services['listener3'] : $this->getListener3Service()) && false ?: '_'}];
|
|
||||||
|
|
||||||
return $this->services['manager3'] = new \stdClass($a);
|
if (isset($this->services['manager3'])) {
|
||||||
|
return $this->services['manager3'];
|
||||||
|
}
|
||||||
|
$b = new \stdClass();
|
||||||
|
$b->listener = [0 => $a];
|
||||||
|
|
||||||
|
return $this->services['manager3'] = new \stdClass($b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -489,9 +494,12 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Private extends Container
|
|||||||
protected function getManager4Service($lazyLoad = true)
|
protected function getManager4Service($lazyLoad = true)
|
||||||
{
|
{
|
||||||
$a = new \stdClass();
|
$a = new \stdClass();
|
||||||
|
|
||||||
|
$this->services['manager4'] = $instance = new \stdClass($a);
|
||||||
|
|
||||||
$a->listener = [0 => ${($_ = isset($this->services['listener4']) ? $this->services['listener4'] : $this->getListener4Service()) && false ?: '_'}];
|
$a->listener = [0 => ${($_ = isset($this->services['listener4']) ? $this->services['listener4'] : $this->getListener4Service()) && false ?: '_'}];
|
||||||
|
|
||||||
return $this->services['manager4'] = new \stdClass($a);
|
return $instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -497,7 +497,13 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Public extends Container
|
|||||||
*/
|
*/
|
||||||
protected function getManager3Service($lazyLoad = true)
|
protected function getManager3Service($lazyLoad = true)
|
||||||
{
|
{
|
||||||
return $this->services['manager3'] = new \stdClass(${($_ = isset($this->services['connection3']) ? $this->services['connection3'] : $this->getConnection3Service()) && false ?: '_'});
|
$a = ${($_ = isset($this->services['connection3']) ? $this->services['connection3'] : $this->getConnection3Service()) && false ?: '_'};
|
||||||
|
|
||||||
|
if (isset($this->services['manager3'])) {
|
||||||
|
return $this->services['manager3'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->services['manager3'] = new \stdClass($a);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -613,7 +619,13 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Public extends Container
|
|||||||
*/
|
*/
|
||||||
protected function getManager4Service($lazyLoad = true)
|
protected function getManager4Service($lazyLoad = true)
|
||||||
{
|
{
|
||||||
return $this->services['manager4'] = new \stdClass(${($_ = isset($this->services['connection4']) ? $this->services['connection4'] : $this->getConnection4Service()) && false ?: '_'});
|
$a = ${($_ = isset($this->services['connection4']) ? $this->services['connection4'] : $this->getConnection4Service()) && false ?: '_'};
|
||||||
|
|
||||||
|
if (isset($this->services['manager4'])) {
|
||||||
|
return $this->services['manager4'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->services['manager4'] = new \stdClass($a);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user