diff --git a/src/Symfony/Component/DependencyInjection/CHANGELOG.md b/src/Symfony/Component/DependencyInjection/CHANGELOG.md index 9ca5dc07ee5bb..bbe164bacf575 100644 --- a/src/Symfony/Component/DependencyInjection/CHANGELOG.md +++ b/src/Symfony/Component/DependencyInjection/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG 4.4.0 ----- + * added support for dumping the container in one file instead of many files * deprecated support for short factories and short configurators in Yaml * deprecated `tagged` in favor of `tagged_iterator` * deprecated passing an instance of `Symfony\Component\DependencyInjection\Parameter` as class name to `Symfony\Component\DependencyInjection\Definition` diff --git a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php index 2228bdc00f598..e126526ff6eab 100644 --- a/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php +++ b/src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php @@ -72,6 +72,7 @@ class PhpDumper extends Dumper private $namespace; private $asFiles; private $hotPathTag; + private $inlineFactories; private $inlineRequires; private $inlinedRequires = []; private $circularReferences = []; @@ -134,6 +135,7 @@ public function dump(array $options = []) 'as_files' => false, 'debug' => true, 'hot_path_tag' => 'container.hot_path', + 'inline_factories_parameter' => 'container.dumper.inline_factories', 'inline_class_loader_parameter' => 'container.dumper.inline_class_loader', 'service_locator_tag' => 'container.service_locator', 'build_time' => time(), @@ -143,6 +145,7 @@ public function dump(array $options = []) $this->namespace = $options['namespace']; $this->asFiles = $options['as_files']; $this->hotPathTag = $options['hot_path_tag']; + $this->inlineFactories = $this->asFiles && $options['inline_factories_parameter'] && $this->container->hasParameter($options['inline_factories_parameter']) && $this->container->getParameter($options['inline_factories_parameter']); $this->inlineRequires = $options['inline_class_loader_parameter'] && $this->container->hasParameter($options['inline_class_loader_parameter']) && $this->container->getParameter($options['inline_class_loader_parameter']); $this->serviceLocatorTag = $options['service_locator_tag']; @@ -215,6 +218,8 @@ public function dump(array $options = []) } } + $proxyClasses = $this->generateProxyClasses(); + $code = $this->startClass($options['class'], $baseClass, $baseClassWithNamespace). $this->addServices($services). @@ -258,13 +263,24 @@ public function dump(array $options = []) $files['removed-ids.php'] = $c .= "];\n"; } - foreach ($this->generateServiceFiles($services) as $file => $c) { - $files[$file] = $fileStart.$c; + if (!$this->inlineFactories) { + foreach ($this->generateServiceFiles($services) as $file => $c) { + $files[$file] = $fileStart.$c; + } + foreach ($proxyClasses as $file => $c) { + $files[$file] = "generateProxyClasses() as $file => $c) { - $files[$file] = "endClass(); + + if ($this->inlineFactories) { + foreach ($proxyClasses as $c) { + $code .= $c; + } } - $files[$options['class'].'.php'] = $code.$this->endClass(); + + $files[$options['class'].'.php'] = $code; $hash = ucfirst(strtr(ContainerBuilder::hash($files), '._', 'xx')); $code = []; @@ -303,7 +319,7 @@ public function dump(array $options = []) EOF; } else { $code .= $this->endClass(); - foreach ($this->generateProxyClasses() as $c) { + foreach ($proxyClasses as $c) { $code .= $c; } } @@ -422,8 +438,9 @@ private function collectLineage($class, array &$lineage) $lineage[$class] = substr($exportedFile, 1, -1); } - private function generateProxyClasses() + private function generateProxyClasses(): array { + $proxyClasses = []; $alreadyGenerated = []; $definitions = $this->container->getDefinitions(); $strip = '' === $this->docStar && method_exists('Symfony\Component\HttpKernel\Kernel', 'stripComments'); @@ -442,19 +459,39 @@ private function generateProxyClasses() if ("\n" === $proxyCode = "\n".$proxyDumper->getProxyCode($definition)) { continue; } + + if ($this->inlineRequires) { + $lineage = []; + $this->collectLineage($class, $lineage); + + $code = ''; + foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) { + if ($this->inlineFactories) { + $this->inlinedRequires[$file] = true; + } + $file = preg_replace('#^\\$this->targetDirs\[(\d++)\]#', sprintf('\dirname(__DIR__, %d + $1)', $this->asFiles), $file); + $code .= sprintf("include_once %s;\n", $file); + } + + $proxyCode = $code.$proxyCode; + } + if ($strip) { $proxyCode = " $proxyCode; + + $proxyClasses[sprintf('%s.php', explode(' ', $proxyCode, 3)[1])] = $proxyCode; } + + return $proxyClasses; } private function addServiceInclude(string $cId, Definition $definition): string { $code = ''; - if ($this->inlineRequires && !$this->isHotPath($definition)) { + if ($this->inlineRequires && (!$this->isHotPath($definition) || $this->getProxyDumper()->isProxyCandidate($definition))) { $lineage = []; foreach ($this->inlinedDefinitions as $def) { if (!$def->isDeprecated() && \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) { @@ -685,7 +722,7 @@ private function addService(string $id, Definition $definition): array $lazyInitialization = ''; } - $asFile = $this->asFiles && !$this->isHotPath($definition); + $asFile = $this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition); $methodName = $this->generateMethodName($id); if ($asFile) { $file = $methodName.'.php'; @@ -711,17 +748,16 @@ protected function {$methodName}($lazyInitialization) $this->serviceCalls = []; $this->inlinedDefinitions = $this->getDefinitionsFromArguments([$definition], null, $this->serviceCalls); - $code .= $this->addServiceInclude($id, $definition); + if ($definition->isDeprecated()) { + $code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id))); + } if ($this->getProxyDumper()->isProxyCandidate($definition)) { $factoryCode = $asFile ? ($definition->isShared() ? "\$this->load('%s.php', false)" : '$this->factories[%2$s](false)') : '$this->%s(false)'; $code .= $this->getProxyDumper()->getProxyFactoryCode($definition, $id, sprintf($factoryCode, $methodName, $this->doExport($id))); } - if ($definition->isDeprecated()) { - $code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id))); - } - + $code .= $this->addServiceInclude($id, $definition); $code .= $this->addInlineService($id, $definition); if ($asFile) { @@ -1024,7 +1060,7 @@ public function __construct() $code .= $this->addSyntheticIds(); $code .= $this->addMethodMap(); - $code .= $this->asFiles ? $this->addFileMap() : ''; + $code .= $this->asFiles && !$this->inlineFactories ? $this->addFileMap() : ''; $code .= $this->addAliases(); $code .= $this->addInlineRequires(); $code .= <<addRemovedIds(); - if ($this->asFiles) { + if ($this->asFiles && !$this->inlineFactories) { $code .= <<isProxyCandidate($definition)) { continue; } - if ($this->asFiles) { + if ($this->asFiles && !$this->inlineFactories) { $proxyLoader = '$this->load("{$class}.php")'; - } elseif ($this->namespace) { - $proxyLoader = 'class_alias("'.$this->namespace.'\\\\{$class}", $class, false)'; + } elseif ($this->namespace || $this->inlineFactories) { + $proxyLoader = 'class_alias(__NAMESPACE__."\\\\$class", $class, false)'; } else { $proxyLoader = ''; } @@ -1140,7 +1176,7 @@ private function addMethodMap(): string $definitions = $this->container->getDefinitions(); ksort($definitions); foreach ($definitions as $id => $definition) { - if (!$definition->isSynthetic() && $definition->isPublic() && (!$this->asFiles || $this->isHotPath($definition))) { + if (!$definition->isSynthetic() && $definition->isPublic() && (!$this->asFiles || $this->inlineFactories || $this->isHotPath($definition))) { $code .= ' '.$this->doExport($id).' => '.$this->doExport($this->generateMethodName($id)).",\n"; } } @@ -1237,6 +1273,11 @@ private function addInlineRequires(): string foreach ($this->container->findTaggedServiceIds($this->hotPathTag) as $id => $tags) { $definition = $this->container->getDefinition($id); + + if ($this->getProxyDumper()->isProxyCandidate($definition)) { + continue; + } + $inlinedDefinitions = $this->getDefinitionsFromArguments([$definition]); foreach ($inlinedDefinitions as $def) { @@ -1578,7 +1619,7 @@ private function dumpValue($value, bool $interpolate = true): string continue; } $definition = $this->container->findDefinition($id = (string) $v); - $load = !($definition->hasErrors() && $e = $definition->getErrors()) ? $this->asFiles && !$this->isHotPath($definition) : reset($e); + $load = !($definition->hasErrors() && $e = $definition->getErrors()) ? $this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition) : reset($e); $serviceMap .= sprintf("\n %s => [%s, %s, %s, %s],", $this->export($k), $this->export($definition->isShared() ? ($definition->isPublic() ? 'services' : 'privates') : false), @@ -1716,7 +1757,7 @@ private function getServiceCall(string $id, Reference $reference = null): string $code = sprintf('$this->%s[%s] = %s', $definition->isPublic() ? 'services' : 'privates', $this->doExport($id), $code); } $code = "($code)"; - } elseif ($this->asFiles && !$this->isHotPath($definition)) { + } elseif ($this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition)) { $code = sprintf("\$this->load('%s.php')", $this->generateMethodName($id)); if (!$definition->isShared()) { $factory = sprintf('$this->factories%s[%s]', $definition->isPublic() ? '' : "['service_container']", $this->doExport($id));