[DI] configure inlined services before injecting them when dumping the container
This commit is contained in:
parent
5632dc7c7a
commit
e5c54053c4
@ -55,7 +55,9 @@ class PhpDumper extends Dumper
|
|||||||
private $definitionVariables;
|
private $definitionVariables;
|
||||||
private $referenceVariables;
|
private $referenceVariables;
|
||||||
private $variableCount;
|
private $variableCount;
|
||||||
private $reservedVariables = array('instance', 'class');
|
private $inlinedDefinitions;
|
||||||
|
private $serviceCalls;
|
||||||
|
private $reservedVariables = array('instance', 'class', 'this');
|
||||||
private $expressionLanguage;
|
private $expressionLanguage;
|
||||||
private $targetDirRegex;
|
private $targetDirRegex;
|
||||||
private $targetDirMaxMatches;
|
private $targetDirMaxMatches;
|
||||||
@ -295,63 +297,6 @@ EOF;
|
|||||||
return $this->proxyDumper;
|
return $this->proxyDumper;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates Service local temp variables.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function addServiceLocalTempVariables($cId, Definition $definition, \SplObjectStorage $inlinedDefinitions, array $serviceCalls, $preInstance = false)
|
|
||||||
{
|
|
||||||
$calls = array();
|
|
||||||
|
|
||||||
foreach ($inlinedDefinitions as $def) {
|
|
||||||
if ($preInstance && !$inlinedDefinitions[$def][1]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$this->getServiceCallsFromArguments(array($def->getArguments(), $def->getFactory()), $calls, $preInstance, $cId);
|
|
||||||
if ($def !== $definition) {
|
|
||||||
$arguments = array($def->getProperties(), $def->getMethodCalls(), $def->getConfigurator());
|
|
||||||
$this->getServiceCallsFromArguments($arguments, $calls, $preInstance && !$this->hasReference($cId, $arguments, true), $cId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!isset($inlinedDefinitions[$definition])) {
|
|
||||||
$arguments = array($definition->getProperties(), $definition->getMethodCalls(), $definition->getConfigurator());
|
|
||||||
$this->getServiceCallsFromArguments($arguments, $calls, false, $cId);
|
|
||||||
}
|
|
||||||
|
|
||||||
$code = '';
|
|
||||||
foreach ($calls as $id => list($callCount)) {
|
|
||||||
if ('service_container' === $id || $id === $cId || isset($this->referenceVariables[$id])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($callCount <= 1 && $serviceCalls[$id][0] <= 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$name = $this->getNextVariableName();
|
|
||||||
$this->referenceVariables[$id] = new Variable($name);
|
|
||||||
|
|
||||||
$reference = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $serviceCalls[$id][1] ? new Reference($id, $serviceCalls[$id][1]) : null;
|
|
||||||
$code .= sprintf(" \$%s = %s;\n", $name, $this->getServiceCall($id, $reference));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('' !== $code) {
|
|
||||||
if ($preInstance) {
|
|
||||||
$code .= <<<EOTXT
|
|
||||||
|
|
||||||
if (isset(\$this->services['$cId'])) {
|
|
||||||
return \$this->services['$cId'];
|
|
||||||
}
|
|
||||||
|
|
||||||
EOTXT;
|
|
||||||
}
|
|
||||||
|
|
||||||
$code .= "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $code;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function analyzeCircularReferences(array $edges, &$checkedNodes, &$currentPath)
|
private function analyzeCircularReferences(array $edges, &$checkedNodes, &$currentPath)
|
||||||
{
|
{
|
||||||
foreach ($edges as $edge) {
|
foreach ($edges as $edge) {
|
||||||
@ -436,19 +381,19 @@ EOTXT;
|
|||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function addServiceInclude($cId, Definition $definition, \SplObjectStorage $inlinedDefinitions, array $serviceCalls)
|
private function addServiceInclude($cId, Definition $definition)
|
||||||
{
|
{
|
||||||
$code = '';
|
$code = '';
|
||||||
|
|
||||||
if ($this->inlineRequires && !$this->isHotPath($definition)) {
|
if ($this->inlineRequires && !$this->isHotPath($definition)) {
|
||||||
$lineage = array();
|
$lineage = array();
|
||||||
foreach ($inlinedDefinitions as $def) {
|
foreach ($this->inlinedDefinitions as $def) {
|
||||||
if (!$def->isDeprecated() && \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) {
|
if (!$def->isDeprecated() && \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) {
|
||||||
$this->collectLineage($class, $lineage);
|
$this->collectLineage($class, $lineage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($serviceCalls as $id => list($callCount, $behavior)) {
|
foreach ($this->serviceCalls as $id => list($callCount, $behavior)) {
|
||||||
if ('service_container' !== $id && $id !== $cId
|
if ('service_container' !== $id && $id !== $cId
|
||||||
&& ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior
|
&& ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior
|
||||||
&& $this->container->has($id)
|
&& $this->container->has($id)
|
||||||
@ -464,7 +409,7 @@ EOTXT;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($inlinedDefinitions as $def) {
|
foreach ($this->inlinedDefinitions as $def) {
|
||||||
if ($file = $def->getFile()) {
|
if ($file = $def->getFile()) {
|
||||||
$code .= sprintf(" include_once %s;\n", $this->dumpValue($file));
|
$code .= sprintf(" include_once %s;\n", $this->dumpValue($file));
|
||||||
}
|
}
|
||||||
@ -477,59 +422,6 @@ EOTXT;
|
|||||||
return $code;
|
return $code;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the inline definition of a service.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*
|
|
||||||
* @throws RuntimeException When the factory definition is incomplete
|
|
||||||
* @throws ServiceCircularReferenceException When a circular reference is detected
|
|
||||||
*/
|
|
||||||
private function addServiceInlinedDefinitions($id, Definition $definition, \SplObjectStorage $inlinedDefinitions, &$isSimpleInstance, $preInstance = false)
|
|
||||||
{
|
|
||||||
$code = '';
|
|
||||||
|
|
||||||
foreach ($inlinedDefinitions as $def) {
|
|
||||||
if ($definition === $def || isset($this->definitionVariables[$def])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($inlinedDefinitions[$def][0] <= 1 && !$def->getMethodCalls() && !$def->getProperties() && !$def->getConfigurator() && false === strpos($this->dumpValue($def->getClass()), '$')) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ($preInstance && !$inlinedDefinitions[$def][1]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$name = $this->getNextVariableName();
|
|
||||||
$this->definitionVariables[$def] = new Variable($name);
|
|
||||||
|
|
||||||
// a construct like:
|
|
||||||
// $a = new ServiceA(ServiceB $b); $b = new ServiceB(ServiceA $a);
|
|
||||||
// this is an indication for a wrong implementation, you can circumvent this problem
|
|
||||||
// by setting up your service structure like this:
|
|
||||||
// $b = new ServiceB();
|
|
||||||
// $a = new ServiceA(ServiceB $b);
|
|
||||||
// $b->setServiceA(ServiceA $a);
|
|
||||||
if (isset($inlinedDefinitions[$definition]) && $this->hasReference($id, array($def->getArguments(), $def->getFactory()))) {
|
|
||||||
throw new ServiceCircularReferenceException($id, array($id, '...', $id));
|
|
||||||
}
|
|
||||||
|
|
||||||
$code .= $this->addNewInstance($def, '$'.$name, ' = ', $id);
|
|
||||||
|
|
||||||
if (!$this->hasReference($id, array($def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()), true, $inlinedDefinitions)) {
|
|
||||||
$code .= $this->addServiceProperties($def, $name);
|
|
||||||
$code .= $this->addServiceMethodCalls($def, $name);
|
|
||||||
$code .= $this->addServiceConfigurator($def, $name);
|
|
||||||
} else {
|
|
||||||
$isSimpleInstance = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$code .= "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $code;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates the service instance.
|
* Generates the service instance.
|
||||||
*
|
*
|
||||||
@ -566,14 +458,7 @@ EOTXT;
|
|||||||
$instantiation .= ' = ';
|
$instantiation .= ' = ';
|
||||||
}
|
}
|
||||||
|
|
||||||
$code = $this->addNewInstance($definition, $return, $instantiation, $id);
|
return $this->addNewInstance($definition, $return, $instantiation, $id);
|
||||||
$this->referenceVariables[$id] = new Variable('instance');
|
|
||||||
|
|
||||||
if (!$isSimpleInstance) {
|
|
||||||
$code .= "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $code;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -658,40 +543,6 @@ EOTXT;
|
|||||||
return $code;
|
return $code;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the inline definition setup.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*
|
|
||||||
* @throws ServiceCircularReferenceException when the container contains a circular reference
|
|
||||||
*/
|
|
||||||
private function addServiceInlinedDefinitionsSetup($id, Definition $definition, \SplObjectStorage $inlinedDefinitions, $isSimpleInstance)
|
|
||||||
{
|
|
||||||
$code = '';
|
|
||||||
foreach ($inlinedDefinitions as $def) {
|
|
||||||
if ($definition === $def || !$this->hasReference($id, array($def->getProperties(), $def->getMethodCalls(), $def->getConfigurator()), true, $inlinedDefinitions)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the instance is simple, the return statement has already been generated
|
|
||||||
// so, the only possible way to get there is because of a circular reference
|
|
||||||
if ($isSimpleInstance) {
|
|
||||||
throw new ServiceCircularReferenceException($id, array($id, '...', $id));
|
|
||||||
}
|
|
||||||
|
|
||||||
$name = (string) $this->definitionVariables[$def];
|
|
||||||
$code .= $this->addServiceProperties($def, $name);
|
|
||||||
$code .= $this->addServiceMethodCalls($def, $name);
|
|
||||||
$code .= $this->addServiceConfigurator($def, $name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('' !== $code && ($definition->getProperties() || $definition->getMethodCalls() || $definition->getConfigurator())) {
|
|
||||||
$code .= "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return $code;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds configurator definition.
|
* Adds configurator definition.
|
||||||
*
|
*
|
||||||
@ -742,6 +593,7 @@ EOTXT;
|
|||||||
$this->definitionVariables = new \SplObjectStorage();
|
$this->definitionVariables = new \SplObjectStorage();
|
||||||
$this->referenceVariables = array();
|
$this->referenceVariables = array();
|
||||||
$this->variableCount = 0;
|
$this->variableCount = 0;
|
||||||
|
$this->definitionVariables[$definition] = $this->referenceVariables[$id] = new Variable('instance');
|
||||||
|
|
||||||
$return = array();
|
$return = array();
|
||||||
|
|
||||||
@ -801,6 +653,11 @@ EOTXT;
|
|||||||
EOF;
|
EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->serviceCalls = array();
|
||||||
|
$this->inlinedDefinitions = $this->getDefinitionsFromArguments(array($definition), null, $this->serviceCalls);
|
||||||
|
|
||||||
|
$code .= $this->addServiceInclude($id, $definition);
|
||||||
|
|
||||||
if ($this->getProxyDumper()->isProxyCandidate($definition)) {
|
if ($this->getProxyDumper()->isProxyCandidate($definition)) {
|
||||||
$factoryCode = $asFile ? "\$this->load('%s.php', false)" : '$this->%s(false)';
|
$factoryCode = $asFile ? "\$this->load('%s.php', false)" : '$this->%s(false)';
|
||||||
$code .= $this->getProxyDumper()->getProxyFactoryCode($definition, $id, sprintf($factoryCode, $methodName));
|
$code .= $this->getProxyDumper()->getProxyFactoryCode($definition, $id, sprintf($factoryCode, $methodName));
|
||||||
@ -810,40 +667,22 @@ EOF;
|
|||||||
$code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id)));
|
$code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
$inlinedDefinitions = $this->getDefinitionsFromArguments(array($definition));
|
$head = $tail = '';
|
||||||
$constructorDefinitions = $this->getDefinitionsFromArguments(array($definition->getArguments(), $definition->getFactory()));
|
$arguments = array($definition->getArguments(), $definition->getFactory());
|
||||||
unset($constructorDefinitions[$definition]); // ensures $definition will be last
|
$this->addInlineVariables($head, $tail, $id, $arguments, true);
|
||||||
$otherDefinitions = new \SplObjectStorage();
|
$code .= '' !== $head ? $head."\n" : '';
|
||||||
$serviceCalls = array();
|
|
||||||
|
|
||||||
foreach ($inlinedDefinitions as $def) {
|
if ($arguments = array_filter(array($definition->getProperties(), $definition->getMethodCalls(), $definition->getConfigurator()))) {
|
||||||
if ($def === $definition || isset($constructorDefinitions[$def])) {
|
$this->addInlineVariables($tail, $tail, $id, $arguments, false);
|
||||||
$constructorDefinitions[$def] = $inlinedDefinitions[$def];
|
|
||||||
} else {
|
$tail .= '' !== $tail ? "\n" : '';
|
||||||
$otherDefinitions[$def] = $inlinedDefinitions[$def];
|
$tail .= $this->addServiceProperties($definition);
|
||||||
}
|
$tail .= $this->addServiceMethodCalls($definition);
|
||||||
$arguments = array($def->getArguments(), $def->getFactory(), $def->getProperties(), $def->getMethodCalls(), $def->getConfigurator());
|
$tail .= $this->addServiceConfigurator($definition);
|
||||||
$this->getServiceCallsFromArguments($arguments, $serviceCalls, false, $id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$isSimpleInstance = !$definition->getProperties() && !$definition->getMethodCalls() && !$definition->getConfigurator();
|
$code .= $this->addServiceInstance($id, $definition, '' === $tail)
|
||||||
$preInstance = isset($this->circularReferences[$id]) && !$this->getProxyDumper()->isProxyCandidate($definition) && $definition->isShared();
|
.('' !== $tail ? "\n".$tail."\n return \$instance;\n" : '');
|
||||||
|
|
||||||
$code .=
|
|
||||||
$this->addServiceInclude($id, $definition, $inlinedDefinitions, $serviceCalls).
|
|
||||||
$this->addServiceLocalTempVariables($id, $definition, $constructorDefinitions, $serviceCalls, $preInstance).
|
|
||||||
$this->addServiceInlinedDefinitions($id, $definition, $constructorDefinitions, $isSimpleInstance, $preInstance).
|
|
||||||
$this->addServiceInstance($id, $definition, $isSimpleInstance).
|
|
||||||
$this->addServiceLocalTempVariables($id, $definition, $constructorDefinitions->offsetUnset($definition) ?: $constructorDefinitions, $serviceCalls).
|
|
||||||
$this->addServiceLocalTempVariables($id, $definition, $otherDefinitions, $serviceCalls).
|
|
||||||
$this->addServiceInlinedDefinitions($id, $definition, $constructorDefinitions, $isSimpleInstance).
|
|
||||||
$this->addServiceInlinedDefinitions($id, $definition, $otherDefinitions, $isSimpleInstance).
|
|
||||||
$this->addServiceInlinedDefinitionsSetup($id, $definition, $inlinedDefinitions, $isSimpleInstance).
|
|
||||||
$this->addServiceProperties($definition).
|
|
||||||
$this->addServiceMethodCalls($definition).
|
|
||||||
$this->addServiceConfigurator($definition).
|
|
||||||
(!$isSimpleInstance ? "\n return \$instance;\n" : '')
|
|
||||||
;
|
|
||||||
|
|
||||||
if ($asFile) {
|
if ($asFile) {
|
||||||
$code = implode("\n", array_map(function ($line) { return $line ? substr($line, 8) : $line; }, explode("\n", $code)));
|
$code = implode("\n", array_map(function ($line) { return $line ? substr($line, 8) : $line; }, explode("\n", $code)));
|
||||||
@ -851,12 +690,108 @@ EOF;
|
|||||||
$code .= " }\n";
|
$code .= " }\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->definitionVariables = null;
|
$this->definitionVariables = $this->inlinedDefinitions = null;
|
||||||
$this->referenceVariables = null;
|
$this->referenceVariables = $this->serviceCalls = null;
|
||||||
|
|
||||||
return $code;
|
return $code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function addInlineVariables(&$head, &$tail, $id, array $arguments, $forConstructor)
|
||||||
|
{
|
||||||
|
$hasSelfRef = false;
|
||||||
|
|
||||||
|
foreach ($arguments as $argument) {
|
||||||
|
if (\is_array($argument)) {
|
||||||
|
$hasSelfRef = $this->addInlineVariables($head, $tail, $id, $argument, $forConstructor) || $hasSelfRef;
|
||||||
|
} elseif ($argument instanceof Reference) {
|
||||||
|
$hasSelfRef = $this->addInlineReference($head, $tail, $id, $this->container->normalizeId($argument), $forConstructor) || $hasSelfRef;
|
||||||
|
} elseif ($argument instanceof Definition) {
|
||||||
|
$hasSelfRef = $this->addInlineService($head, $tail, $id, $argument, $forConstructor) || $hasSelfRef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $hasSelfRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addInlineReference(&$head, &$tail, $id, $targetId, $forConstructor)
|
||||||
|
{
|
||||||
|
if ('service_container' === $targetId || isset($this->referenceVariables[$targetId])) {
|
||||||
|
return isset($this->circularReferences[$id][$targetId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
list($callCount, $behavior) = $this->serviceCalls[$targetId];
|
||||||
|
|
||||||
|
if (2 > $callCount && (!$forConstructor || !isset($this->circularReferences[$id][$targetId]))) {
|
||||||
|
return isset($this->circularReferences[$id][$targetId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = $this->getNextVariableName();
|
||||||
|
$this->referenceVariables[$targetId] = new Variable($name);
|
||||||
|
|
||||||
|
$reference = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE >= $behavior ? new Reference($targetId, $behavior) : null;
|
||||||
|
$code = sprintf(" \$%s = %s;\n", $name, $this->getServiceCall($targetId, $reference));
|
||||||
|
|
||||||
|
if (!isset($this->circularReferences[$id][$targetId])) {
|
||||||
|
$head .= $code;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$forConstructor) {
|
||||||
|
$tail .= $code;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$head .= $code.sprintf(<<<'EOTXT'
|
||||||
|
|
||||||
|
if (isset($this->%s['%s'])) {
|
||||||
|
return $this->%1$s['%2$s'];
|
||||||
|
}
|
||||||
|
|
||||||
|
EOTXT
|
||||||
|
,
|
||||||
|
$this->container->getDefinition($id)->isPublic() ? 'services' : 'privates',
|
||||||
|
$id
|
||||||
|
);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addInlineService(&$head, &$tail, $id, Definition $definition, $forConstructor)
|
||||||
|
{
|
||||||
|
if (isset($this->definitionVariables[$definition])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$arguments = array($definition->getArguments(), $definition->getFactory());
|
||||||
|
|
||||||
|
if (2 > $this->inlinedDefinitions[$definition] && !$definition->getMethodCalls() && !$definition->getProperties() && !$definition->getConfigurator() && false === strpos($this->dumpValue($definition->getClass()), '$')) {
|
||||||
|
return $this->addInlineVariables($head, $tail, $id, $arguments, $forConstructor);
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = $this->getNextVariableName();
|
||||||
|
$this->definitionVariables[$definition] = new Variable($name);
|
||||||
|
|
||||||
|
$code = '';
|
||||||
|
$hasSelfRef = $this->addInlineVariables($code, $tail, $id, $arguments, $forConstructor);
|
||||||
|
$code .= $this->addNewInstance($definition, '$'.$name, ' = ', $id);
|
||||||
|
$hasSelfRef ? $tail .= ('' !== $tail ? "\n" : '').$code : $head .= ('' !== $head ? "\n" : '').$code;
|
||||||
|
|
||||||
|
$code = '';
|
||||||
|
$arguments = array($definition->getProperties(), $definition->getMethodCalls(), $definition->getConfigurator());
|
||||||
|
$hasSelfRef = $this->addInlineVariables($code, $tail, $id, $arguments, false) || $hasSelfRef;
|
||||||
|
|
||||||
|
$code .= $this->addServiceProperties($definition, $name);
|
||||||
|
$code .= $this->addServiceMethodCalls($definition, $name);
|
||||||
|
$code .= $this->addServiceConfigurator($definition, $name);
|
||||||
|
if ('' !== $code) {
|
||||||
|
$hasSelfRef ? $tail .= ('' !== $tail ? "\n" : '').$code : $head .= $code;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $hasSelfRef;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds multiple services.
|
* Adds multiple services.
|
||||||
*
|
*
|
||||||
@ -1574,31 +1509,7 @@ EOF;
|
|||||||
return implode(' && ', $conditions);
|
return implode(' && ', $conditions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private function getDefinitionsFromArguments(array $arguments, \SplObjectStorage $definitions = null, array &$calls = array())
|
||||||
* Builds service calls from arguments.
|
|
||||||
*
|
|
||||||
* Populates $calls with "referenced id" => ["reference count", "invalid behavior"] pairs.
|
|
||||||
*/
|
|
||||||
private function getServiceCallsFromArguments(array $arguments, array &$calls, $preInstance, $callerId)
|
|
||||||
{
|
|
||||||
foreach ($arguments as $argument) {
|
|
||||||
if (\is_array($argument)) {
|
|
||||||
$this->getServiceCallsFromArguments($argument, $calls, $preInstance, $callerId);
|
|
||||||
} elseif ($argument instanceof Reference) {
|
|
||||||
$id = $this->container->normalizeId($argument);
|
|
||||||
|
|
||||||
if (!isset($calls[$id])) {
|
|
||||||
$calls[$id] = array((int) ($preInstance && isset($this->circularReferences[$callerId][$id])), $argument->getInvalidBehavior());
|
|
||||||
} else {
|
|
||||||
$calls[$id][1] = min($calls[$id][1], $argument->getInvalidBehavior());
|
|
||||||
}
|
|
||||||
|
|
||||||
++$calls[$id][0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getDefinitionsFromArguments(array $arguments, $isConstructorArgument = true, \SplObjectStorage $definitions = null)
|
|
||||||
{
|
{
|
||||||
if (null === $definitions) {
|
if (null === $definitions) {
|
||||||
$definitions = new \SplObjectStorage();
|
$definitions = new \SplObjectStorage();
|
||||||
@ -1606,88 +1517,31 @@ EOF;
|
|||||||
|
|
||||||
foreach ($arguments as $argument) {
|
foreach ($arguments as $argument) {
|
||||||
if (\is_array($argument)) {
|
if (\is_array($argument)) {
|
||||||
$this->getDefinitionsFromArguments($argument, $isConstructorArgument, $definitions);
|
$this->getDefinitionsFromArguments($argument, $definitions, $calls);
|
||||||
|
} elseif ($argument instanceof Reference) {
|
||||||
|
$id = $this->container->normalizeId($argument);
|
||||||
|
|
||||||
|
if (!isset($calls[$id])) {
|
||||||
|
$calls[$id] = array(0, $argument->getInvalidBehavior());
|
||||||
|
} else {
|
||||||
|
$calls[$id][1] = min($calls[$id][1], $argument->getInvalidBehavior());
|
||||||
|
}
|
||||||
|
|
||||||
|
++$calls[$id][0];
|
||||||
} elseif (!$argument instanceof Definition) {
|
} elseif (!$argument instanceof Definition) {
|
||||||
// no-op
|
// no-op
|
||||||
} elseif (isset($definitions[$argument])) {
|
} elseif (isset($definitions[$argument])) {
|
||||||
$def = $definitions[$argument];
|
$definitions[$argument] = 1 + $definitions[$argument];
|
||||||
$definitions[$argument] = array(1 + $def[0], $isConstructorArgument || $def[1]);
|
|
||||||
} else {
|
} else {
|
||||||
$definitions[$argument] = array(1, $isConstructorArgument);
|
$definitions[$argument] = 1;
|
||||||
$this->getDefinitionsFromArguments($argument->getArguments(), $isConstructorArgument, $definitions);
|
$arguments = array($argument->getArguments(), $argument->getFactory(), $argument->getProperties(), $argument->getMethodCalls(), $argument->getConfigurator());
|
||||||
$this->getDefinitionsFromArguments(array($argument->getFactory()), $isConstructorArgument, $definitions);
|
$this->getDefinitionsFromArguments($arguments, $definitions, $calls);
|
||||||
$this->getDefinitionsFromArguments($argument->getProperties(), false, $definitions);
|
|
||||||
$this->getDefinitionsFromArguments($argument->getMethodCalls(), false, $definitions);
|
|
||||||
$this->getDefinitionsFromArguments(array($argument->getConfigurator()), false, $definitions);
|
|
||||||
// move current definition last in the list
|
|
||||||
$def = $definitions[$argument];
|
|
||||||
unset($definitions[$argument]);
|
|
||||||
$definitions[$argument] = $def;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $definitions;
|
return $definitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a service id has a reference.
|
|
||||||
*
|
|
||||||
* @param string $id
|
|
||||||
* @param array $arguments
|
|
||||||
* @param bool $deep
|
|
||||||
* @param array $visited
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function hasReference($id, array $arguments, $deep = false, \SplObjectStorage $inlinedDefinitions = null, array &$visited = array())
|
|
||||||
{
|
|
||||||
if (!isset($this->circularReferences[$id])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($arguments as $argument) {
|
|
||||||
if (\is_array($argument)) {
|
|
||||||
if ($this->hasReference($id, $argument, $deep, $inlinedDefinitions, $visited)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
} elseif ($argument instanceof Reference) {
|
|
||||||
$argumentId = $this->container->normalizeId($argument);
|
|
||||||
if ($id === $argumentId) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$deep || isset($visited[$argumentId]) || !isset($this->circularReferences[$argumentId])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$visited[$argumentId] = true;
|
|
||||||
|
|
||||||
$service = $this->container->getDefinition($argumentId);
|
|
||||||
} elseif ($argument instanceof Definition) {
|
|
||||||
if (isset($inlinedDefinitions[$argument])) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
$service = $argument;
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the proxy manager is enabled, disable searching for references in lazy services,
|
|
||||||
// as these services will be instantiated lazily and don't have direct related references.
|
|
||||||
if ($service->isLazy() && !$this->getProxyDumper() instanceof NullDumper) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->hasReference($id, array($service->getArguments(), $service->getFactory(), $service->getProperties(), $service->getMethodCalls(), $service->getConfigurator()), $deep, $inlinedDefinitions, $visited)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dumps values.
|
* Dumps values.
|
||||||
*
|
*
|
||||||
@ -1711,7 +1565,7 @@ EOF;
|
|||||||
|
|
||||||
return sprintf('array(%s)', implode(', ', $code));
|
return sprintf('array(%s)', implode(', ', $code));
|
||||||
} elseif ($value instanceof ArgumentInterface) {
|
} elseif ($value instanceof ArgumentInterface) {
|
||||||
$scope = array($this->definitionVariables, $this->referenceVariables, $this->variableCount);
|
$scope = array($this->definitionVariables, $this->referenceVariables);
|
||||||
$this->definitionVariables = $this->referenceVariables = null;
|
$this->definitionVariables = $this->referenceVariables = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -1758,7 +1612,7 @@ EOF;
|
|||||||
return implode("\n", $code);
|
return implode("\n", $code);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
list($this->definitionVariables, $this->referenceVariables, $this->variableCount) = $scope;
|
list($this->definitionVariables, $this->referenceVariables) = $scope;
|
||||||
}
|
}
|
||||||
} elseif ($value instanceof Definition) {
|
} elseif ($value instanceof Definition) {
|
||||||
if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) {
|
if (null !== $this->definitionVariables && $this->definitionVariables->contains($value)) {
|
||||||
|
@ -850,7 +850,14 @@ class PhpDumperTest extends TestCase
|
|||||||
$dumper = new PhpDumper($container);
|
$dumper = new PhpDumper($container);
|
||||||
$dumper->dump();
|
$dumper->dump();
|
||||||
|
|
||||||
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services_deep_graph.php', $dumper->dump());
|
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services_deep_graph.php', $dumper->dump(array('class' => 'Symfony_DI_PhpDumper_Test_Deep_Graph')));
|
||||||
|
|
||||||
|
require self::$fixturesPath.'/php/services_deep_graph.php';
|
||||||
|
|
||||||
|
$container = new \Symfony_DI_PhpDumper_Test_Deep_Graph();
|
||||||
|
|
||||||
|
$this->assertInstanceOf(FooForDeepGraph::class, $container->get('foo'));
|
||||||
|
$this->assertEquals((object) array('p2' => (object) array('p3' => (object) array())), $container->get('foo')->bClone);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testHotPathOptimizations()
|
public function testHotPathOptimizations()
|
||||||
@ -1077,3 +1084,14 @@ class Rot13EnvVarProcessor implements EnvVarProcessorInterface
|
|||||||
return array('rot13' => 'string');
|
return array('rot13' => 'string');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FooForDeepGraph
|
||||||
|
{
|
||||||
|
public $bClone;
|
||||||
|
|
||||||
|
public function __construct(\stdClass $a, \stdClass $b)
|
||||||
|
{
|
||||||
|
// clone to verify that $b has been fully initialized before
|
||||||
|
$this->bClone = clone $b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -121,6 +121,7 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Private extends Container
|
|||||||
$this->services['connection'] = $instance = new \stdClass($a, $b);
|
$this->services['connection'] = $instance = new \stdClass($a, $b);
|
||||||
|
|
||||||
$a->subscriber = ${($_ = isset($this->services['subscriber']) ? $this->services['subscriber'] : $this->getSubscriberService()) && false ?: '_'};
|
$a->subscriber = ${($_ = isset($this->services['subscriber']) ? $this->services['subscriber'] : $this->getSubscriberService()) && false ?: '_'};
|
||||||
|
|
||||||
$b->logger = ${($_ = isset($this->services['logger']) ? $this->services['logger'] : $this->getLoggerService()) && false ?: '_'};
|
$b->logger = ${($_ = isset($this->services['logger']) ? $this->services['logger'] : $this->getLoggerService()) && false ?: '_'};
|
||||||
|
|
||||||
return $instance;
|
return $instance;
|
||||||
@ -135,17 +136,19 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Private extends Container
|
|||||||
{
|
{
|
||||||
$a = new \stdClass();
|
$a = new \stdClass();
|
||||||
|
|
||||||
$b = new \stdClass();
|
$c = new \stdClass();
|
||||||
|
|
||||||
$this->services['connection2'] = $instance = new \stdClass($a, $b);
|
$this->services['connection2'] = $instance = new \stdClass($a, $c);
|
||||||
|
|
||||||
$c = ${($_ = isset($this->services['manager2']) ? $this->services['manager2'] : $this->getManager2Service()) && false ?: '_'};
|
$b = ${($_ = isset($this->services['manager2']) ? $this->services['manager2'] : $this->getManager2Service()) && false ?: '_'};
|
||||||
|
|
||||||
|
$a->subscriber2 = new \stdClass($b);
|
||||||
|
|
||||||
$d = new \stdClass($instance);
|
$d = new \stdClass($instance);
|
||||||
|
|
||||||
$a->subscriber2 = new \stdClass($c);
|
$d->handler2 = new \stdClass($b);
|
||||||
$d->handler2 = new \stdClass($c);
|
|
||||||
$b->logger2 = $d;
|
$c->logger2 = $d;
|
||||||
|
|
||||||
return $instance;
|
return $instance;
|
||||||
}
|
}
|
||||||
|
@ -169,6 +169,7 @@ class Symfony_DI_PhpDumper_Test_Almost_Circular_Public extends Container
|
|||||||
$c = new \stdClass($instance);
|
$c = new \stdClass($instance);
|
||||||
|
|
||||||
$c->handler2 = new \stdClass(${($_ = isset($this->services['manager2']) ? $this->services['manager2'] : $this->getManager2Service()) && false ?: '_'});
|
$c->handler2 = new \stdClass(${($_ = isset($this->services['manager2']) ? $this->services['manager2'] : $this->getManager2Service()) && false ?: '_'});
|
||||||
|
|
||||||
$b->logger2 = $c;
|
$b->logger2 = $c;
|
||||||
|
|
||||||
return $instance;
|
return $instance;
|
||||||
|
@ -14,7 +14,7 @@ use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
|
|||||||
*
|
*
|
||||||
* @final since Symfony 3.3
|
* @final since Symfony 3.3
|
||||||
*/
|
*/
|
||||||
class ProjectServiceContainer extends Container
|
class Symfony_DI_PhpDumper_Test_Deep_Graph extends Container
|
||||||
{
|
{
|
||||||
private $parameters;
|
private $parameters;
|
||||||
private $targetDirs = array();
|
private $targetDirs = array();
|
||||||
@ -58,13 +58,13 @@ class ProjectServiceContainer extends Container
|
|||||||
/**
|
/**
|
||||||
* Gets the public 'bar' shared service.
|
* Gets the public 'bar' shared service.
|
||||||
*
|
*
|
||||||
* @return \c5
|
* @return \stdClass
|
||||||
*/
|
*/
|
||||||
protected function getBarService()
|
protected function getBarService()
|
||||||
{
|
{
|
||||||
$this->services['bar'] = $instance = new \c5();
|
$this->services['bar'] = $instance = new \stdClass();
|
||||||
|
|
||||||
$instance->p5 = new \c6(${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->getFooService()) && false ?: '_'});
|
$instance->p5 = new \stdClass(${($_ = isset($this->services['foo']) ? $this->services['foo'] : $this->getFooService()) && false ?: '_'});
|
||||||
|
|
||||||
return $instance;
|
return $instance;
|
||||||
}
|
}
|
||||||
@ -72,7 +72,7 @@ class ProjectServiceContainer extends Container
|
|||||||
/**
|
/**
|
||||||
* Gets the public 'foo' shared service.
|
* Gets the public 'foo' shared service.
|
||||||
*
|
*
|
||||||
* @return \c1
|
* @return \Symfony\Component\DependencyInjection\Tests\Dumper\FooForDeepGraph
|
||||||
*/
|
*/
|
||||||
protected function getFooService()
|
protected function getFooService()
|
||||||
{
|
{
|
||||||
@ -82,15 +82,11 @@ class ProjectServiceContainer extends Container
|
|||||||
return $this->services['foo'];
|
return $this->services['foo'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$b = new \c2();
|
$b = new \stdClass();
|
||||||
|
$c = new \stdClass();
|
||||||
$this->services['foo'] = $instance = new \c1($a, $b);
|
$c->p3 = new \stdClass();
|
||||||
|
|
||||||
$c = new \c3();
|
|
||||||
|
|
||||||
$c->p3 = new \c4();
|
|
||||||
$b->p2 = $c;
|
$b->p2 = $c;
|
||||||
|
|
||||||
return $instance;
|
return $this->services['foo'] = new \Symfony\Component\DependencyInjection\Tests\Dumper\FooForDeepGraph($a, $b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,24 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
foo:
|
foo:
|
||||||
class: c1
|
class: Symfony\Component\DependencyInjection\Tests\Dumper\FooForDeepGraph
|
||||||
public: true
|
public: true
|
||||||
arguments:
|
arguments:
|
||||||
- '@bar'
|
- '@bar'
|
||||||
- !service
|
- !service
|
||||||
class: c2
|
class: stdClass
|
||||||
properties:
|
properties:
|
||||||
p2: !service
|
p2: !service
|
||||||
class: c3
|
class: stdClass
|
||||||
properties:
|
properties:
|
||||||
p3: !service
|
p3: !service
|
||||||
class: c4
|
class: stdClass
|
||||||
|
|
||||||
bar:
|
bar:
|
||||||
class: c5
|
class: stdClass
|
||||||
public: true
|
public: true
|
||||||
properties:
|
properties:
|
||||||
p5: !service
|
p5: !service
|
||||||
class: c6
|
class: stdClass
|
||||||
arguments: ['@foo']
|
arguments: ['@foo']
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user