Skip to content

Commit 685ff7f

Browse files
[DI] Allow dumping the container in one file instead of many files
1 parent 52e9fb9 commit 685ff7f

File tree

5 files changed

+900
-23
lines changed

5 files changed

+900
-23
lines changed

src/Symfony/Component/DependencyInjection/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ CHANGELOG
44
4.4.0
55
-----
66

7+
* added support for dumping the container in one file instead of many files
78
* deprecated support for short factories and short configurators in Yaml
89
* deprecated `tagged` in favor of `tagged_iterator`
910
* deprecated passing an instance of `Symfony\Component\DependencyInjection\Parameter` as class name to `Symfony\Component\DependencyInjection\Definition`

src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ class PhpDumper extends Dumper
7272
private $namespace;
7373
private $asFiles;
7474
private $hotPathTag;
75+
private $inlineFactories;
7576
private $inlineRequires;
7677
private $inlinedRequires = [];
7778
private $circularReferences = [];
@@ -134,6 +135,7 @@ public function dump(array $options = [])
134135
'as_files' => false,
135136
'debug' => true,
136137
'hot_path_tag' => 'container.hot_path',
138+
'inline_factories_parameter' => 'container.dumper.inline_factories',
137139
'inline_class_loader_parameter' => 'container.dumper.inline_class_loader',
138140
'service_locator_tag' => 'container.service_locator',
139141
'build_time' => time(),
@@ -143,6 +145,7 @@ public function dump(array $options = [])
143145
$this->namespace = $options['namespace'];
144146
$this->asFiles = $options['as_files'];
145147
$this->hotPathTag = $options['hot_path_tag'];
148+
$this->inlineFactories = $this->asFiles && $options['inline_factories_parameter'] && $this->container->hasParameter($options['inline_factories_parameter']) && $this->container->getParameter($options['inline_factories_parameter']);
146149
$this->inlineRequires = $options['inline_class_loader_parameter'] && $this->container->hasParameter($options['inline_class_loader_parameter']) && $this->container->getParameter($options['inline_class_loader_parameter']);
147150
$this->serviceLocatorTag = $options['service_locator_tag'];
148151

@@ -215,13 +218,17 @@ public function dump(array $options = [])
215218
}
216219
}
217220

221+
$proxyClasses = $this->inlineFactories ? $this->generateProxyClasses() : null;
222+
218223
$code =
219224
$this->startClass($options['class'], $baseClass, $baseClassWithNamespace).
220225
$this->addServices($services).
221226
$this->addDeprecatedAliases().
222227
$this->addDefaultParametersMethod()
223228
;
224229

230+
$proxyClasses = $proxyClasses ?? $this->generateProxyClasses();
231+
225232
if ($this->addGetService) {
226233
$code = preg_replace(
227234
"/(\r?\n\r?\n public function __construct.+?\\{\r?\n)/s",
@@ -258,13 +265,24 @@ public function dump(array $options = [])
258265
$files['removed-ids.php'] = $c .= "];\n";
259266
}
260267

261-
foreach ($this->generateServiceFiles($services) as $file => $c) {
262-
$files[$file] = $fileStart.$c;
268+
if (!$this->inlineFactories) {
269+
foreach ($this->generateServiceFiles($services) as $file => $c) {
270+
$files[$file] = $fileStart.$c;
271+
}
272+
foreach ($proxyClasses as $file => $c) {
273+
$files[$file] = "<?php\n".$c;
274+
}
263275
}
264-
foreach ($this->generateProxyClasses() as $file => $c) {
265-
$files[$file] = "<?php\n".$c;
276+
277+
$code .= $this->endClass();
278+
279+
if ($this->inlineFactories) {
280+
foreach ($proxyClasses as $c) {
281+
$code .= $c;
282+
}
266283
}
267-
$files[$options['class'].'.php'] = $code.$this->endClass();
284+
285+
$files[$options['class'].'.php'] = $code;
268286
$hash = ucfirst(strtr(ContainerBuilder::hash($files), '._', 'xx'));
269287
$code = [];
270288

@@ -303,7 +321,7 @@ public function dump(array $options = [])
303321
EOF;
304322
} else {
305323
$code .= $this->endClass();
306-
foreach ($this->generateProxyClasses() as $c) {
324+
foreach ($proxyClasses as $c) {
307325
$code .= $c;
308326
}
309327
}
@@ -422,8 +440,9 @@ private function collectLineage($class, array &$lineage)
422440
$lineage[$class] = substr($exportedFile, 1, -1);
423441
}
424442

425-
private function generateProxyClasses()
443+
private function generateProxyClasses(): array
426444
{
445+
$proxyClasses = [];
427446
$alreadyGenerated = [];
428447
$definitions = $this->container->getDefinitions();
429448
$strip = '' === $this->docStar && method_exists('Symfony\Component\HttpKernel\Kernel', 'stripComments');
@@ -442,19 +461,39 @@ private function generateProxyClasses()
442461
if ("\n" === $proxyCode = "\n".$proxyDumper->getProxyCode($definition)) {
443462
continue;
444463
}
464+
465+
if ($this->inlineRequires) {
466+
$lineage = [];
467+
$this->collectLineage($class, $lineage);
468+
469+
$code = '';
470+
foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) {
471+
if ($this->inlineFactories) {
472+
$this->inlinedRequires[$file] = true;
473+
}
474+
$file = preg_replace('#^\\$this->targetDirs\[(\d++)\]#', sprintf('\dirname(__DIR__, %d + $1)', $this->asFiles), $file);
475+
$code .= sprintf("include_once %s;\n", $file);
476+
}
477+
478+
$proxyCode = $code.$proxyCode;
479+
}
480+
445481
if ($strip) {
446482
$proxyCode = "<?php\n".$proxyCode;
447483
$proxyCode = substr(Kernel::stripComments($proxyCode), 5);
448484
}
449-
yield sprintf('%s.php', explode(' ', $proxyCode, 3)[1]) => $proxyCode;
485+
486+
$proxyClasses[sprintf('%s.php', explode(' ', $proxyCode, 3)[1])] = $proxyCode;
450487
}
488+
489+
return $proxyClasses;
451490
}
452491

453492
private function addServiceInclude(string $cId, Definition $definition): string
454493
{
455494
$code = '';
456495

457-
if ($this->inlineRequires && !$this->isHotPath($definition)) {
496+
if ($this->inlineRequires && (!$this->isHotPath($definition) || $this->getProxyDumper()->isProxyCandidate($definition))) {
458497
$lineage = [];
459498
foreach ($this->inlinedDefinitions as $def) {
460499
if (!$def->isDeprecated() && \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) {
@@ -685,7 +724,7 @@ private function addService(string $id, Definition $definition): array
685724
$lazyInitialization = '';
686725
}
687726

688-
$asFile = $this->asFiles && !$this->isHotPath($definition);
727+
$asFile = $this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition);
689728
$methodName = $this->generateMethodName($id);
690729
if ($asFile) {
691730
$file = $methodName.'.php';
@@ -711,17 +750,16 @@ protected function {$methodName}($lazyInitialization)
711750
$this->serviceCalls = [];
712751
$this->inlinedDefinitions = $this->getDefinitionsFromArguments([$definition], null, $this->serviceCalls);
713752

714-
$code .= $this->addServiceInclude($id, $definition);
753+
if ($definition->isDeprecated()) {
754+
$code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id)));
755+
}
715756

716757
if ($this->getProxyDumper()->isProxyCandidate($definition)) {
717758
$factoryCode = $asFile ? ($definition->isShared() ? "\$this->load('%s.php', false)" : '$this->factories[%2$s](false)') : '$this->%s(false)';
718759
$code .= $this->getProxyDumper()->getProxyFactoryCode($definition, $id, sprintf($factoryCode, $methodName, $this->doExport($id)));
719760
}
720761

721-
if ($definition->isDeprecated()) {
722-
$code .= sprintf(" @trigger_error(%s, E_USER_DEPRECATED);\n\n", $this->export($definition->getDeprecationMessage($id)));
723-
}
724-
762+
$code .= $this->addServiceInclude($id, $definition);
725763
$code .= $this->addInlineService($id, $definition);
726764

727765
if ($asFile) {
@@ -1024,7 +1062,7 @@ public function __construct()
10241062

10251063
$code .= $this->addSyntheticIds();
10261064
$code .= $this->addMethodMap();
1027-
$code .= $this->asFiles ? $this->addFileMap() : '';
1065+
$code .= $this->asFiles && !$this->inlineFactories ? $this->addFileMap() : '';
10281066
$code .= $this->addAliases();
10291067
$code .= $this->addInlineRequires();
10301068
$code .= <<<EOF
@@ -1043,7 +1081,7 @@ public function isCompiled()
10431081
EOF;
10441082
$code .= $this->addRemovedIds();
10451083

1046-
if ($this->asFiles) {
1084+
if ($this->asFiles && !$this->inlineFactories) {
10471085
$code .= <<<EOF
10481086
10491087
protected function load(\$file, \$lazyLoad = true)
@@ -1059,10 +1097,10 @@ protected function load(\$file, \$lazyLoad = true)
10591097
if (!$proxyDumper->isProxyCandidate($definition)) {
10601098
continue;
10611099
}
1062-
if ($this->asFiles) {
1100+
if ($this->asFiles && !$this->inlineFactories) {
10631101
$proxyLoader = '$this->load("{$class}.php")';
1064-
} elseif ($this->namespace) {
1065-
$proxyLoader = 'class_alias("'.$this->namespace.'\\\\{$class}", $class, false)';
1102+
} elseif ($this->namespace || $this->inlineFactories) {
1103+
$proxyLoader = 'class_alias(__NAMESPACE__."\\\\$class", $class, false)';
10661104
} else {
10671105
$proxyLoader = '';
10681106
}
@@ -1140,7 +1178,7 @@ private function addMethodMap(): string
11401178
$definitions = $this->container->getDefinitions();
11411179
ksort($definitions);
11421180
foreach ($definitions as $id => $definition) {
1143-
if (!$definition->isSynthetic() && $definition->isPublic() && (!$this->asFiles || $this->isHotPath($definition))) {
1181+
if (!$definition->isSynthetic() && $definition->isPublic() && (!$this->asFiles || $this->inlineFactories || $this->isHotPath($definition))) {
11441182
$code .= ' '.$this->doExport($id).' => '.$this->doExport($this->generateMethodName($id)).",\n";
11451183
}
11461184
}
@@ -1237,6 +1275,11 @@ private function addInlineRequires(): string
12371275

12381276
foreach ($this->container->findTaggedServiceIds($this->hotPathTag) as $id => $tags) {
12391277
$definition = $this->container->getDefinition($id);
1278+
1279+
if ($this->getProxyDumper()->isProxyCandidate($definition)) {
1280+
continue;
1281+
}
1282+
12401283
$inlinedDefinitions = $this->getDefinitionsFromArguments([$definition]);
12411284

12421285
foreach ($inlinedDefinitions as $def) {
@@ -1578,7 +1621,7 @@ private function dumpValue($value, bool $interpolate = true): string
15781621
continue;
15791622
}
15801623
$definition = $this->container->findDefinition($id = (string) $v);
1581-
$load = !($definition->hasErrors() && $e = $definition->getErrors()) ? $this->asFiles && !$this->isHotPath($definition) : reset($e);
1624+
$load = !($definition->hasErrors() && $e = $definition->getErrors()) ? $this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition) : reset($e);
15821625
$serviceMap .= sprintf("\n %s => [%s, %s, %s, %s],",
15831626
$this->export($k),
15841627
$this->export($definition->isShared() ? ($definition->isPublic() ? 'services' : 'privates') : false),
@@ -1716,7 +1759,7 @@ private function getServiceCall(string $id, Reference $reference = null): string
17161759
$code = sprintf('$this->%s[%s] = %s', $definition->isPublic() ? 'services' : 'privates', $this->doExport($id), $code);
17171760
}
17181761
$code = "($code)";
1719-
} elseif ($this->asFiles && !$this->isHotPath($definition)) {
1762+
} elseif ($this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition)) {
17201763
$code = sprintf("\$this->load('%s.php')", $this->generateMethodName($id));
17211764
if (!$definition->isShared()) {
17221765
$factory = sprintf('$this->factories%s[%s]', $definition->isPublic() ? '' : "['service_container']", $this->doExport($id));

src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Psr\Container\ContainerInterface;
16+
use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper;
1617
use Symfony\Component\Config\FileLocator;
1718
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
1819
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
@@ -42,6 +43,7 @@
4243

4344
require_once __DIR__.'/../Fixtures/includes/autowiring_classes.php';
4445
require_once __DIR__.'/../Fixtures/includes/classes.php';
46+
require_once __DIR__.'/../Fixtures/includes/foo_lazy.php';
4547

4648
class PhpDumperTest extends TestCase
4749
{
@@ -234,6 +236,59 @@ public function testDumpAsFiles()
234236
$this->assertStringMatchesFormatFile(self::$fixturesPath.'/php/services9_as_files.txt', $dump);
235237
}
236238

239+
public function testDumpAsFilesWithFactoriesInlined()
240+
{
241+
$container = include self::$fixturesPath.'/containers/container9.php';
242+
$container->setParameter('container.dumper.inline_factories', true);
243+
$container->setParameter('container.dumper.inline_class_loader', true);
244+
245+
$container->getDefinition('bar')->addTag('hot');
246+
$container->register('non_shared_foo', \Bar\FooClass::class)
247+
->setFile(realpath(self::$fixturesPath.'/includes/foo.php'))
248+
->setShared(false)
249+
->setPublic(true);
250+
$container->register('throwing_one', \Bar\FooClass::class)
251+
->addArgument(new Reference('errored_one', ContainerBuilder::RUNTIME_EXCEPTION_ON_INVALID_REFERENCE))
252+
->setPublic(true);
253+
$container->register('errored_one', 'stdClass')
254+
->addError('No-no-no-no');
255+
$container->compile();
256+
257+
$dumper = new PhpDumper($container);
258+
$dump = print_r($dumper->dump(['as_files' => true, 'file' => __DIR__, 'hot_path_tag' => 'hot', 'build_time' => 1563381341]), true);
259+
260+
if ('\\' === \DIRECTORY_SEPARATOR) {
261+
$dump = str_replace('\\\\Fixtures\\\\includes\\\\', '/Fixtures/includes/', $dump);
262+
}
263+
$this->assertStringMatchesFormatFile(self::$fixturesPath.'/php/services9_inlined_factories.txt', $dump);
264+
}
265+
266+
/**
267+
* @requires function \Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper::getProxyCode
268+
*/
269+
public function testDumpAsFilesWithLazyFactoriesInlined()
270+
{
271+
$container = new ContainerBuilder();
272+
$container->setParameter('container.dumper.inline_factories', true);
273+
$container->setParameter('container.dumper.inline_class_loader', true);
274+
275+
$container->register('lazy_foo', \Bar\FooClass::class)
276+
->addArgument(new Definition(\Bar\FooLazyClass::class))
277+
->setPublic(true)
278+
->setLazy(true);
279+
280+
$container->compile();
281+
282+
$dumper = new PhpDumper($container);
283+
$dumper->setProxyDumper(new ProxyDumper());
284+
$dump = print_r($dumper->dump(['as_files' => true, 'file' => __DIR__, 'hot_path_tag' => 'hot', 'build_time' => 1563381341]), true);
285+
286+
if ('\\' === \DIRECTORY_SEPARATOR) {
287+
$dump = str_replace('\\\\Fixtures\\\\includes\\\\', '/Fixtures/includes/', $dump);
288+
}
289+
$this->assertStringMatchesFormatFile(self::$fixturesPath.'/php/services9_lazy_inlined_factories.txt', $dump);
290+
}
291+
237292
public function testNonSharedLazyDumpAsFiles()
238293
{
239294
$container = include self::$fixturesPath.'/containers/container_non_shared_lazy.php';

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy