Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 0876480

Browse filesBrowse files
minor #36193 [DI] dump factory files as classes (nicolas-grekas)
This PR was merged into the 5.1-dev branch. Discussion ---------- [DI] dump factory files as classes | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | no | Deprecations? | no | Tickets | - | License | MIT | Doc PR | - This PR is a performance improvement when using `bin/console` on the command line. Once upon a time, we advised setting `container.dumper.inline_factories` to `false` so that the container could be chunked into many files. More recently, we turned this setting back to `true` in order to optimize for preloading. But this made `bin/console` back to slow: since the CLI cannot have opcache, PHP has to parse this potentially big file all the time. Previous data already showed this can grow big. This PR fixes the issue by generating many files again. But instead of generating the inline code within each file, we now wrap this code inside a class. Then we list this class for preloading. This way, we have the best of both worlds: a `bin/console` that scales no matter the size of the app and top perf when using preloading (I benched a small hello world before/after the patch with preloading enabled, there is no measurable difference.) This should also fix a memory leak that happens when factory files contain closures. Commits ------- cedb5cd [DI] dump factory files as classes
2 parents bb9d522 + cedb5cd commit 0876480
Copy full SHA for 0876480

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Dismiss banner

49 files changed

+806
-516
lines changed

‎src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Kernel/MicroKernelTrait.php
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ public function registerContainerConfiguration(LoaderInterface $loader)
142142
}
143143

144144
$container->setAlias(static::class, 'kernel')->setPublic(true);
145+
146+
if (!$container->hasParameter('container.dumper.inline_factories')) {
147+
$container->setParameter('container.dumper.inline_factories', false);
148+
}
145149
});
146150
}
147151

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php
+71-63Lines changed: 71 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class PhpDumper extends Dumper
6868
private $variableCount;
6969
private $inlinedDefinitions;
7070
private $serviceCalls;
71-
private $reservedVariables = ['instance', 'class', 'this'];
71+
private $reservedVariables = ['instance', 'class', 'this', 'container'];
7272
private $expressionLanguage;
7373
private $targetDirRegex;
7474
private $targetDirMaxMatches;
@@ -246,20 +246,24 @@ public function dump(array $options = [])
246246
if ($this->addGetService) {
247247
$code = preg_replace(
248248
"/(\r?\n\r?\n public function __construct.+?\\{\r?\n)/s",
249-
"\n private \$getService;$1 \$this->getService = \\Closure::fromCallable([\$this, 'getService']);\n",
249+
"\n protected \$getService;$1 \$this->getService = \\Closure::fromCallable([\$this, 'getService']);\n",
250250
$code,
251251
1
252252
);
253253
}
254254

255255
if ($this->asFiles) {
256-
$fileStart = <<<EOF
256+
$fileTemplate = <<<EOF
257257
<?php
258258
259259
use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
260260
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
261261
262-
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.
262+
/*{$this->docStar}
263+
* @internal This class has been auto-generated by the Symfony Dependency Injection Component.
264+
*/
265+
class %s extends {$options['class']}
266+
{%s}
263267
264268
EOF;
265269
$files = [];
@@ -281,7 +285,7 @@ public function dump(array $options = [])
281285

282286
if (!$this->inlineFactories) {
283287
foreach ($this->generateServiceFiles($services) as $file => $c) {
284-
$files[$file] = $fileStart.$c;
288+
$files[$file] = sprintf($fileTemplate, substr($file, 0, -4), $c);
285289
}
286290
foreach ($proxyClasses as $file => $c) {
287291
$files[$file] = "<?php\n".$c;
@@ -301,10 +305,8 @@ public function dump(array $options = [])
301305
$code = [];
302306

303307
foreach ($files as $file => $c) {
304-
$code["Container{$hash}/{$file}"] = $c;
308+
$code["Container{$hash}/{$file}"] = substr_replace($c, "<?php\n\nnamespace Container{$hash};\n", 0, 6);
305309
}
306-
array_pop($code);
307-
$code["Container{$hash}/{$options['class']}.php"] = substr_replace($files[$options['class'].'.php'], "<?php\n\nnamespace Container{$hash};\n", 0, 6);
308310
$namespaceLine = $this->namespace ? "\nnamespace {$this->namespace};\n" : '';
309311
$time = $options['build_time'];
310312
$id = hash('crc32', $hash.$time);
@@ -313,6 +315,9 @@ public function dump(array $options = [])
313315
if ($this->preload && null !== $autoloadFile = $this->getAutoloadFile()) {
314316
$autoloadFile = substr($this->export($autoloadFile), 2, -1);
315317

318+
$factoryFiles = array_reverse(array_keys($code));
319+
$factoryFiles = implode("';\nrequire __DIR__.'/", $factoryFiles);
320+
316321
$code[$options['class'].'.preload.php'] = <<<EOF
317322
<?php
318323
@@ -322,7 +327,7 @@ public function dump(array $options = [])
322327
use Symfony\Component\DependencyInjection\Dumper\Preloader;
323328
324329
require $autoloadFile;
325-
require __DIR__.'/Container{$hash}/{$options['class']}.php';
330+
require __DIR__.'/$factoryFiles';
326331
327332
\$classes = [];
328333
@@ -546,7 +551,13 @@ private function generateProxyClasses(): array
546551
$proxyCode = substr(Kernel::stripComments($proxyCode), 5);
547552
}
548553

549-
$proxyClasses[sprintf('%s.php', explode(' ', $this->inlineRequires ? substr($proxyCode, \strlen($code)) : $proxyCode, 3)[1])] = $proxyCode;
554+
$proxyClass = explode(' ', $this->inlineRequires ? substr($proxyCode, \strlen($code)) : $proxyCode, 3)[1];
555+
556+
if ($this->asFiles || $this->namespace) {
557+
$proxyCode .= "\n\\class_alias(__NAMESPACE__.'\\\\$proxyClass', '$proxyClass', false);\n";
558+
}
559+
560+
$proxyClasses[$proxyClass.'.php'] = $proxyCode;
550561
}
551562

552563
return $proxyClasses;
@@ -784,34 +795,35 @@ private function addService(string $id, Definition $definition): array
784795
$shared = $definition->isShared() ? ' shared' : '';
785796
$public = $definition->isPublic() ? 'public' : 'private';
786797
$autowired = $definition->isAutowired() ? ' autowired' : '';
798+
$asFile = $this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition);
799+
$methodName = $this->generateMethodName($id);
787800

788-
if ($definition->isLazy()) {
801+
if ($asFile || $definition->isLazy()) {
789802
$lazyInitialization = '$lazyLoad = true';
790803
} else {
791804
$lazyInitialization = '';
792805
}
793806

794-
$asFile = $this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition);
795-
$methodName = $this->generateMethodName($id);
796-
if ($asFile) {
797-
$file = $methodName.'.php';
798-
$code = " // Returns the $public '$id'$shared$autowired service.\n\n";
799-
} else {
800-
$file = null;
801-
$code = <<<EOF
807+
$code = <<<EOF
802808
803809
/*{$this->docStar}
804810
* Gets the $public '$id'$shared$autowired service.
805811
*
806812
* $return
807813
EOF;
808-
$code = str_replace('*/', ' ', $code).<<<EOF
814+
$code = str_replace('*/', ' ', $code).<<<EOF
809815
810816
*/
811817
protected function {$methodName}($lazyInitialization)
812818
{
813819
814820
EOF;
821+
822+
if ($asFile) {
823+
$file = $methodName.'.php';
824+
$code = str_replace("protected function {$methodName}(", 'public static function do($container, ', $code);
825+
} else {
826+
$file = null;
815827
}
816828

817829
if ($definition->hasErrors() && $e = $definition->getErrors()) {
@@ -833,20 +845,21 @@ protected function {$methodName}($lazyInitialization)
833845
}
834846

835847
if ($this->getProxyDumper()->isProxyCandidate($definition)) {
836-
$factoryCode = $asFile ? ($definition->isShared() ? "\$this->load('%s.php', false)" : '$this->factories[%2$s](false)') : '$this->%s(false)';
837-
$code .= $this->getProxyDumper()->getProxyFactoryCode($definition, $id, sprintf($factoryCode, $methodName, $this->doExport($id)));
848+
$factoryCode = $asFile ? "\$this->load('%s', false)" : '$this->%s(false)';
849+
$code .= $this->getProxyDumper()->getProxyFactoryCode($definition, $id, sprintf($factoryCode, $methodName));
838850
}
839851

840852
$code .= $this->addServiceInclude($id, $definition);
841853
$code .= $this->addInlineService($id, $definition);
842854
}
843855

844856
if ($asFile) {
845-
$code = implode("\n", array_map(function ($line) { return $line ? substr($line, 8) : $line; }, explode("\n", $code)));
846-
} else {
847-
$code .= " }\n";
857+
$code = str_replace('$this', '$container', $code);
858+
$code = str_replace('function () {', 'function () use ($container) {', $code);
848859
}
849860

861+
$code .= " }\n";
862+
850863
$this->definitionVariables = $this->inlinedDefinitions = null;
851864
$this->referenceVariables = $this->serviceCalls = null;
852865

@@ -1017,21 +1030,6 @@ private function generateServiceFiles(array $services): iterable
10171030
ksort($definitions);
10181031
foreach ($definitions as $id => $definition) {
10191032
if ((list($file, $code) = $services[$id]) && null !== $file && ($definition->isPublic() || !$this->isTrivialInstance($definition) || isset($this->locatedIds[$id]))) {
1020-
if (!$definition->isShared()) {
1021-
$i = strpos($code, "\n\ninclude_once ");
1022-
if (false !== $i && false !== $i = strpos($code, "\n\n", 2 + $i)) {
1023-
$code = [substr($code, 0, 2 + $i), substr($code, 2 + $i)];
1024-
} else {
1025-
$code = ["\n", $code];
1026-
}
1027-
$code[1] = implode("\n", array_map(function ($line) { return $line ? ' '.$line : $line; }, explode("\n", $code[1])));
1028-
$factory = sprintf('$this->factories%s[%s]', $definition->isPublic() ? '' : "['service_container']", $this->doExport($id));
1029-
$lazyloadInitialization = $definition->isLazy() ? '$lazyLoad = true' : '';
1030-
1031-
$code[1] = sprintf("%s = function (%s) {\n%s};\n\nreturn %1\$s();\n", $factory, $lazyloadInitialization, $code[1]);
1032-
$code = $code[0].$code[1];
1033-
}
1034-
10351033
yield $file => $code;
10361034
}
10371035
}
@@ -1112,27 +1110,24 @@ private function startClass(string $class, string $baseClass): string
11121110
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
11131111
11141112
/*{$this->docStar}
1115-
* This class has been auto-generated
1116-
* by the Symfony Dependency Injection Component.
1117-
*
1118-
* @final
1113+
* @internal This class has been auto-generated by the Symfony Dependency Injection Component.
11191114
*/
11201115
class $class extends $baseClass
11211116
{
1122-
private \$parameters = [];
1117+
protected \$parameters = [];
11231118
11241119
public function __construct()
11251120
{
11261121
11271122
EOF;
11281123
if ($this->asFiles) {
1129-
$code = str_replace('$parameters', "\$buildParameters;\n private \$containerDir;\n private \$parameters", $code);
1124+
$code = str_replace('$parameters = []', "\$containerDir;\n protected \$parameters = [];\n private \$buildParameters", $code);
11301125
$code = str_replace('__construct()', '__construct(array $buildParameters = [], $containerDir = __DIR__)', $code);
11311126
$code .= " \$this->buildParameters = \$buildParameters;\n";
11321127
$code .= " \$this->containerDir = \$containerDir;\n";
11331128

11341129
if (null !== $this->targetDirRegex) {
1135-
$code = str_replace('$parameters', "\$targetDir;\n private \$parameters", $code);
1130+
$code = str_replace('$parameters = []', "\$targetDir;\n protected \$parameters = []", $code);
11361131
$code .= ' $this->targetDir = \\dirname($containerDir);'."\n";
11371132
}
11381133
}
@@ -1176,11 +1171,23 @@ public function isCompiled(): bool
11761171
$code .= $this->addRemovedIds();
11771172

11781173
if ($this->asFiles && !$this->inlineFactories) {
1179-
$code .= <<<EOF
1174+
$code .= <<<'EOF'
11801175
1181-
protected function load(\$file, \$lazyLoad = true)
1176+
protected function load($file, $lazyLoad = true)
11821177
{
1183-
return require \$this->containerDir.\\DIRECTORY_SEPARATOR.\$file;
1178+
if (class_exists($class = __NAMESPACE__.'\\'.$file, false)) {
1179+
return $class::do($this, $lazyLoad);
1180+
}
1181+
1182+
if ('.' === $file[-4]) {
1183+
$class = substr($class, 0, -4);
1184+
} else {
1185+
$file .= '.php';
1186+
}
1187+
1188+
$service = require $this->containerDir.\DIRECTORY_SEPARATOR.$file;
1189+
1190+
return class_exists($class, false) ? $class::do($this, $lazyLoad) : $service;
11841191
}
11851192

11861193
EOF;
@@ -1191,16 +1198,13 @@ protected function load(\$file, \$lazyLoad = true)
11911198
if (!$proxyDumper->isProxyCandidate($definition)) {
11921199
continue;
11931200
}
1201+
11941202
if ($this->asFiles && !$this->inlineFactories) {
1195-
$proxyLoader = '$this->load("{$class}.php")';
1196-
} elseif ($this->namespace || $this->inlineFactories) {
1197-
$proxyLoader = 'class_alias(__NAMESPACE__."\\\\$class", $class, false)';
1203+
$proxyLoader = "class_exists(\$class, false) || require __DIR__.'/'.\$class.'.php';\n\n ";
11981204
} else {
11991205
$proxyLoader = '';
12001206
}
1201-
if ($proxyLoader) {
1202-
$proxyLoader = "class_exists(\$class, false) || {$proxyLoader};\n\n ";
1203-
}
1207+
12041208
$code .= <<<EOF
12051209
12061210
protected function createProxy(\$class, \Closure \$factory)
@@ -1295,7 +1299,7 @@ private function addFileMap(): string
12951299
ksort($definitions);
12961300
foreach ($definitions as $id => $definition) {
12971301
if (!$definition->isSynthetic() && $definition->isPublic() && !$this->isHotPath($definition)) {
1298-
$code .= sprintf(" %s => '%s.php',\n", $this->doExport($id), $this->generateMethodName($id));
1302+
$code .= sprintf(" %s => '%s',\n", $this->doExport($id), $this->generateMethodName($id));
12991303
}
13001304
}
13011305

@@ -1709,7 +1713,7 @@ private function dumpValue($value, bool $interpolate = true): string
17091713
$this->export($k),
17101714
$this->export($definition->isShared() ? ($definition->isPublic() ? 'services' : 'privates') : false),
17111715
$this->doExport($id),
1712-
$this->export(ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $v->getInvalidBehavior() && !\is_string($load) ? $this->generateMethodName($id).($load ? '.php' : '') : null),
1716+
$this->export(ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $v->getInvalidBehavior() && !\is_string($load) ? $this->generateMethodName($id) : null),
17131717
$this->export($load)
17141718
);
17151719
$serviceTypes .= sprintf("\n %s => %s,", $this->export($k), $this->export($v instanceof TypedReference ? $v->getType() : '?'));
@@ -1850,11 +1854,7 @@ private function getServiceCall(string $id, Reference $reference = null): string
18501854
}
18511855
$code = "($code)";
18521856
} elseif ($this->asFiles && !$this->inlineFactories && !$this->isHotPath($definition)) {
1853-
$code = sprintf("\$this->load('%s.php')", $this->generateMethodName($id));
1854-
if (!$definition->isShared()) {
1855-
$factory = sprintf('$this->factories%s[%s]', $definition->isPublic() ? '' : "['service_container']", $this->doExport($id));
1856-
$code = sprintf('(isset(%s) ? %1$s() : %s)', $factory, $code);
1857-
}
1857+
$code = sprintf("\$this->load('%s')", $this->generateMethodName($id));
18581858
} else {
18591859
$code = sprintf('$this->%s()', $this->generateMethodName($id));
18601860
}
@@ -2045,6 +2045,14 @@ private function doExport($value, bool $resolveEnv = false)
20452045
} else {
20462046
$export = var_export($value, true);
20472047
}
2048+
if ($this->asFiles) {
2049+
if (false !== strpos($export, '$this')) {
2050+
$export = str_replace('$this', "$'.'this", $export);
2051+
}
2052+
if (false !== strpos($export, 'function () {')) {
2053+
$export = str_replace('function () {', "function ('.') {", $export);
2054+
}
2055+
}
20482056

20492057
if ($resolveEnv && "'" === $export[0] && $export !== $resolvedExport = $this->container->resolveEnvPlaceholders($export, "'.\$this->getEnv('string:%s').'")) {
20502058
$export = $resolvedExport;

‎src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_alias_deprecation.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/container_alias_deprecation.php
+2-5Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,11 @@
1010
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
1111

1212
/**
13-
* This class has been auto-generated
14-
* by the Symfony Dependency Injection Component.
15-
*
16-
* @final
13+
* @internal This class has been auto-generated by the Symfony Dependency Injection Component.
1714
*/
1815
class Symfony_DI_PhpDumper_Test_Aliases_Deprecation extends Container
1916
{
20-
private $parameters = [];
17+
protected $parameters = [];
2118

2219
public function __construct()
2320
{

‎src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_constructor_without_arguments.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_constructor_without_arguments.php
+2-5Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,11 @@
1212
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
1313

1414
/**
15-
* This class has been auto-generated
16-
* by the Symfony Dependency Injection Component.
17-
*
18-
* @final
15+
* @internal This class has been auto-generated by the Symfony Dependency Injection Component.
1916
*/
2017
class ProjectServiceContainer extends \Symfony\Component\DependencyInjection\Tests\Fixtures\Container\ConstructorWithoutArgumentsContainer
2118
{
22-
private $parameters = [];
19+
protected $parameters = [];
2320

2421
public function __construct()
2522
{

‎src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_with_mandatory_constructor_arguments.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_with_mandatory_constructor_arguments.php
+2-5Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,11 @@
1212
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
1313

1414
/**
15-
* This class has been auto-generated
16-
* by the Symfony Dependency Injection Component.
17-
*
18-
* @final
15+
* @internal This class has been auto-generated by the Symfony Dependency Injection Component.
1916
*/
2017
class ProjectServiceContainer extends \Symfony\Component\DependencyInjection\Tests\Fixtures\Container\ConstructorWithMandatoryArgumentsContainer
2118
{
22-
private $parameters = [];
19+
protected $parameters = [];
2320

2421
public function __construct()
2522
{

‎src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_with_optional_constructor_arguments.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/DependencyInjection/Tests/Fixtures/php/custom_container_class_with_optional_constructor_arguments.php
+2-5Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,11 @@
1212
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
1313

1414
/**
15-
* This class has been auto-generated
16-
* by the Symfony Dependency Injection Component.
17-
*
18-
* @final
15+
* @internal This class has been auto-generated by the Symfony Dependency Injection Component.
1916
*/
2017
class ProjectServiceContainer extends \Symfony\Component\DependencyInjection\Tests\Fixtures\Container\ConstructorWithOptionalArgumentsContainer
2118
{
22-
private $parameters = [];
19+
protected $parameters = [];
2320

2421
public function __construct()
2522
{

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.