Skip to content

Commit

Permalink
[DI] Allow dumping the container in one file instead of many files
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas-grekas committed Jul 17, 2019
1 parent 52e9fb9 commit 256e765
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 23 deletions.
1 change: 1 addition & 0 deletions src/Symfony/Component/DependencyInjection/CHANGELOG.md
Expand Up @@ -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`
Expand Down
87 changes: 64 additions & 23 deletions src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
Expand Up @@ -72,6 +72,7 @@ class PhpDumper extends Dumper
private $namespace;
private $asFiles;
private $hotPathTag;
private $inlineFactories;
private $inlineRequires;
private $inlinedRequires = [];
private $circularReferences = [];
Expand Down Expand Up @@ -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(),
Expand All @@ -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'];

Expand Down Expand Up @@ -215,6 +218,8 @@ public function dump(array $options = [])
}
}

$proxyClasses = $this->generateProxyClasses();

$code =
$this->startClass($options['class'], $baseClass, $baseClassWithNamespace).
$this->addServices($services).
Expand Down Expand Up @@ -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] = "<?php\n".$c;
}
}
foreach ($this->generateProxyClasses() as $file => $c) {
$files[$file] = "<?php\n".$c;

$code .= $this->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 = [];

Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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');
Expand All @@ -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 = "<?php\n".$proxyCode;
$proxyCode = substr(Kernel::stripComments($proxyCode), 5);
}
yield sprintf('%s.php', explode(' ', $proxyCode, 3)[1]) => $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())) {
Expand Down Expand Up @@ -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';
Expand All @@ -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) {
Expand Down Expand Up @@ -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 .= <<<EOF
Expand All @@ -1043,7 +1079,7 @@ public function isCompiled()
EOF;
$code .= $this->addRemovedIds();

if ($this->asFiles) {
if ($this->asFiles && !$this->inlineFactories) {
$code .= <<<EOF
protected function load(\$file, \$lazyLoad = true)
Expand All @@ -1059,10 +1095,10 @@ protected function load(\$file, \$lazyLoad = true)
if (!$proxyDumper->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 = '';
}
Expand Down Expand Up @@ -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";
}
}
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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));
Expand Down

0 comments on commit 256e765

Please sign in to comment.