Skip to content

Commit d4b863e

Browse files
-
1 parent 493b781 commit d4b863e

File tree

12 files changed

+410
-298
lines changed

12 files changed

+410
-298
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/PhpConfigReferenceDumpPass.php

Lines changed: 109 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -16,48 +16,26 @@
1616
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1717
use Symfony\Component\DependencyInjection\ContainerBuilder;
1818
use Symfony\Component\DependencyInjection\Extension\ConfigurationExtensionInterface;
19+
use Symfony\Component\DependencyInjection\Extension\ExtensionInterface;
1920
use Symfony\Component\DependencyInjection\Loader\Configurator\AppReference;
21+
use Symfony\Component\Routing\Loader\Configurator\RoutesReference;
2022

2123
/**
2224
* @internal
2325
*/
2426
class PhpConfigReferenceDumpPass implements CompilerPassInterface
2527
{
26-
private const REFERENCE_TEMPLATE = <<<'EOPHP'
28+
private const REFERENCE_TEMPLATE = <<<'PHP'
2729
<?php
2830
31+
// This file is auto-generated and is for apps only. Bundles SHOULD NOT rely on its content.
32+
2933
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
3034
31-
/**
32-
* This class provides array-shapes for configuring the services and bundles of an application.
33-
*
34-
* Services declared with the config() method are autowired and autoconfigured by default.
35-
*
36-
* All this is auto-generated and is for apps only. Bundles SHOULD NOT rely on it.
37-
*
38-
* Example:
39-
*
40-
* ```php
41-
* // config/services.php
42-
* namespace Symfony\Component\DependencyInjection\Loader\Configurator;
43-
*
44-
* return App::config([
45-
* 'services' => [
46-
* 'App\\' => [
47-
* 'resource' => '../src/',
48-
* ],
49-
* ],
50-
* ]);
51-
* ```
52-
*
53-
* @psalm-import-type ImportsConfig from AppReference
54-
* @psalm-import-type ParametersConfig from AppReference
55-
* @psalm-import-type ServicesConfig from AppReference
56-
*{APP_TYPES}
57-
*/
35+
{APP_TYPES}
5836
final class App extends AppReference
5937
{
60-
{APP_SHAPE}
38+
{APP_PARAM}
6139
public static function config(array $config): array
6240
{
6341
return parent::config($config);
@@ -66,119 +44,121 @@ public static function config(array $config): array
6644
6745
namespace Symfony\Component\Routing\Loader\Configurator;
6846
69-
/**
70-
* This class provides array-shapes for configuring the routes of an application.
71-
*
72-
* All this is auto-generated and is for apps only. Bundles SHOULD NOT rely on it.
73-
*
74-
* Example:
75-
*
76-
* ```php
77-
* // config/routes.php
78-
* namespace Symfony\Component\Routing\Loader\Configurator;
79-
*
80-
* return Routes::config([
81-
* 'controllers' => [
82-
* 'resource' => 'attributes',
83-
* 'type' => 'tagged_services',
84-
* ],
85-
* ]);
86-
* ```
87-
*
88-
* @psalm-import-type RouteConfig from RoutesReference
89-
* @psalm-import-type ImportConfig from RoutesReference
90-
* @psalm-import-type AliasConfig from RoutesReference
91-
*
92-
* @psalm-type RoutesConfig = array{{ROUTES_SHAPE}
93-
* ...<string, ImportConfig|RouteConfig|AliasConfig>
94-
* }
95-
*/
47+
{ROUTES_TYPES}
9648
final class Routes extends RoutesReference
9749
{
98-
/**
99-
* @param RoutesConfig $config
100-
*
101-
* @return RoutesConfig
102-
*/
50+
{ROUTES_PARAM}
10351
public static function config(array $config): array
10452
{
10553
return parent::config($config);
10654
}
10755
}
10856

109-
EOPHP;
57+
PHP;
58+
59+
private const WHEN_ENV_APP_TEMPLATE = <<<'PHPDOC'
60+
61+
* "when@{ENV}"?: array{
62+
* imports?: ImportsConfig,
63+
* parameters?: ParametersConfig,
64+
* services?: ServicesConfig,{SHAPE}
65+
* },
66+
PHPDOC;
67+
68+
private const ROUTES_TYPES_TEMPLATE = <<<'PHPDOC'
69+
70+
* @psalm-type RoutesConfig = array{{SHAPE}
71+
* ...<string, RouteConfig|ImportConfig|AliasConfig>
72+
* }
73+
*/
74+
PHPDOC;
75+
76+
private const WHEN_ENV_ROUTES_TEMPLATE = <<<'PHPDOC'
77+
78+
* "when@{ENV}"?: array<string, RouteConfig|ImportConfig|AliasConfig>,
79+
PHPDOC;
80+
81+
public function __construct(
82+
private string $referenceFile,
83+
private array $bundlesDefinition,
84+
) {
85+
}
11086

11187
public function process(ContainerBuilder $container): void
11288
{
113-
if (!$container->hasParameter('.kernel.config_dir')) {
114-
return;
115-
}
116-
117-
$knownEnvs = [];
118-
if ($container->hasParameter('.container.known_envs')) {
119-
$knownEnvs = array_flip($container->getParameter('.container.known_envs'));
120-
}
121-
if ($container->hasParameter('.kernel.known_envs')) {
122-
$knownEnvs += array_flip($container->getParameter('.kernel.known_envs'));
123-
}
124-
$knownEnvs = array_keys($knownEnvs);
89+
$knownEnvs = $container->hasParameter('.container.known_envs') ? $container->getParameter('.container.known_envs') : [$container->getParameter('kernel.environment')];
90+
$knownEnvs = array_unique($knownEnvs);
12591
sort($knownEnvs);
92+
$extensionsPerEnv = [];
93+
$appTypes = '';
12694

127-
if ($container->hasParameter('.kernel.all_bundles')) {
128-
$allBundles = $container->getParameter('.kernel.all_bundles');
129-
foreach ($allBundles as $bundle => $envs) {
130-
if (!$extension = (new $bundle())->getContainerExtension()) {
131-
continue;
132-
}
133-
$extensions[$bundle] = $extension;
95+
$anyEnvExtensions = [];
96+
foreach ($this->bundlesDefinition as $bundle => $envs) {
97+
if (!$extension = (new $bundle())->getContainerExtension()) {
98+
continue;
13499
}
135-
} else {
136-
$extensions = $container->getExtensions();
137-
}
100+
if (!$configuration = $this->getConfiguration($extension, $container)) {
101+
continue;
102+
}
103+
$anyEnvExtensions[$bundle] = $extension;
104+
$type = $this->camelCase($extension->getAlias()).'Config';
105+
$appTypes .= \sprintf("\n * @psalm-type %s = %s", $type, ArrayShapeGenerator::generate($configuration->getConfigTreeBuilder()->buildTree()));
138106

139-
$types = '';
140-
$genericShape = '';
141-
foreach ($this->getConfigurations($extensions, $container) as $name => $configuration) {
142-
$type = $this->camelCase($name).'Config';
143-
$types .= \sprintf("\n * @psalm-type %s = %s", $type, ArrayShapeGenerator::generate($configuration->getConfigTreeBuilder()->buildTree()));
144-
$genericShape .= \sprintf("\n %s?: %s,", $name, $type);
107+
foreach ($knownEnvs as $env) {
108+
if ($envs[$env] ?? $envs['all'] ?? false) {
109+
$extensionsPerEnv[$env][] = $extension;
110+
} else {
111+
unset($anyEnvExtensions[$bundle]);
112+
}
113+
}
145114
}
115+
krsort($extensionsPerEnv);
146116

147-
$shape = str_replace("\n", "\n * ", $genericShape);
148-
$phpdoc = (new \ReflectionClass(AppReference::class))->getMethod('config')->getDocComment();
117+
$r = new \ReflectionClass(AppReference::class);
149118

150-
if ($phpdoc === $shape = str_replace("\n * ...<string, ExtensionConfig>,", $shape, $phpdoc)) {
151-
throw new \LogicException(\sprintf('Cannot find insertion point for config shape in "%s".', AppReference::class));
119+
if (false === $i = strpos($phpdoc = $r->getDocComment(), "\n */")) {
120+
throw new \LogicException(\sprintf('Cannot insert config shape in "%s".', AppReference::class));
152121
}
153-
$phpdoc = $shape;
154-
155-
$shape = str_replace("\n", "\n * ", $genericShape);
122+
$appTypes = substr_replace($phpdoc, $appTypes, $i, 0);
156123

157-
if ($phpdoc === $shape = str_replace("\n * ...<string, ExtensionConfig>,", $shape, $phpdoc)) {
158-
throw new \LogicException(\sprintf('Cannot find insertion point for config shape in "%s".', AppReference::class));
124+
if (false === $i = strpos($phpdoc = $r->getMethod('config')->getDocComment(), "\n * ...<string, ExtensionType|array{")) {
125+
throw new \LogicException(\sprintf('Cannot insert config shape in "%s".', AppReference::class));
126+
}
127+
$appParam = substr_replace($phpdoc, $this->getShapeForExtensions($anyEnvExtensions, $container), $i, 0);
128+
$i += strlen($appParam) - strlen($phpdoc);
129+
130+
foreach ($extensionsPerEnv as $env => $extensions) {
131+
$appParam = substr_replace($appParam, strtr(self::WHEN_ENV_APP_TEMPLATE, [
132+
'{ENV}' => $env,
133+
'{SHAPE}' => $this->getShapeForExtensions($extensions, $container, ' '),
134+
]), $i, 0);
159135
}
160-
$phpdoc = $shape;
161-
$routesShape = '';
162136

163-
if ($knownEnvs) {
164-
if ($phpdoc === $shape = preg_replace('{\.\.\.<string, array\{ .* when@%env% .*}', \sprintf("...<'when@%s', array{", implode("'|'when@", $knownEnvs)), $phpdoc)) {
165-
throw new \LogicException(\sprintf('Cannot find insertion point for config shape in "%s".', AppReference::class));
166-
}
167-
$phpdoc = $shape;
137+
$r = new \ReflectionClass(RoutesReference::class);
168138

169-
foreach ($knownEnvs as $env) {
170-
$routesShape .= "\n * 'when@{$env}'?: array<string, RouteConfig|ImportConfig|AliasConfig>,";
171-
}
139+
if (false === $i = strpos($phpdoc = $r->getDocComment(), "\n * @psalm-type RoutesConfig = ")) {
140+
throw new \LogicException(\sprintf('Cannot insert config shape in "%s".', RoutesReference::class));
141+
}
142+
$routesTypes = '';
143+
foreach ($knownEnvs as $env) {
144+
$routesTypes .= strtr(self::WHEN_ENV_ROUTES_TEMPLATE, ['{ENV}' => $env]);
145+
}
146+
if ('' !== $routesTypes) {
147+
$routesTypes = strtr(self::ROUTES_TYPES_TEMPLATE, ['{SHAPE}' => $routesTypes]);
148+
$routesTypes = substr_replace($phpdoc, $routesTypes, $i);
172149
}
173150

174151
$configReference = strtr(self::REFERENCE_TEMPLATE, [
175-
'{APP_TYPES}' => $types,
176-
'{APP_SHAPE}' => $shape,
177-
'{ROUTES_SHAPE}' => $routesShape,
152+
'{APP_TYPES}' => $appTypes,
153+
'{APP_PARAM}' => $appParam,
154+
'{ROUTES_TYPES}' => $routesTypes,
155+
'{ROUTES_PARAM}' => $r->getMethod('config')->getDocComment(),
178156
]);
179157

180-
// Ignore errors when writing to the file
181-
@file_put_contents($container->getParameter('.kernel.config_dir').'/reference.php', $configReference);
158+
$dir = \dirname($this->referenceFile);
159+
if (is_dir($dir) && is_writable($dir) && (!is_file($this->referenceFile) || file_get_contents($this->referenceFile) !== $configReference)) {
160+
file_put_contents($this->referenceFile, $configReference);
161+
}
182162
}
183163

184164
private function camelCase(string $input): string
@@ -188,19 +168,25 @@ private function camelCase(string $input): string
188168
return preg_replace('#\W#', '', $output);
189169
}
190170

191-
private function getConfigurations(array $extensions, ContainerBuilder $container): array
171+
private function getConfiguration(ExtensionInterface $extension, ContainerBuilder $container): ?ConfigurationInterface
172+
{
173+
return match (true) {
174+
$extension instanceof ConfigurationInterface => $extension,
175+
$extension instanceof ConfigurationExtensionInterface => $extension->getConfiguration([], $container),
176+
default => null,
177+
};
178+
}
179+
180+
private function getShapeForExtensions(array $extensions, ContainerBuilder $container, string $indent = ''): string
192181
{
193-
$configurations = [];
182+
$shape = '';
194183
foreach ($extensions as $extension) {
195-
if (null !== $configuration = match (true) {
196-
$extension instanceof ConfigurationInterface => $extension,
197-
$extension instanceof ConfigurationExtensionInterface => $extension->getConfiguration([], $container),
198-
default => null,
199-
}) {
200-
$configurations[$extension->getAlias()] = $configuration;
184+
if ($this->getConfiguration($extension, $container)) {
185+
$type = $this->camelCase($extension->getAlias()).'Config';
186+
$shape .= \sprintf("\n * %s%s?: %s,", $indent, $extension->getAlias(), $type);
201187
}
202188
}
203189

204-
return $configurations;
190+
return $shape;
205191
}
206192
}

src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,9 @@ public function build(ContainerBuilder $container): void
149149
]);
150150
}
151151

152-
$container->addCompilerPass(new PhpConfigReferenceDumpPass());
152+
if ($container->hasParameter('.kernel.config_dir') && $container->hasParameter('.kernel.bundles_definition')) {
153+
$container->addCompilerPass(new PhpConfigReferenceDumpPass($container->getParameter('.kernel.config_dir').'/reference.php', $container->getParameter('.kernel.bundles_definition')));
154+
}
153155
$container->addCompilerPass(new AssetsContextPass());
154156
$container->addCompilerPass(new LoggerPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -32);
155157
$container->addCompilerPass(new RegisterControllerArgumentLocatorsPass());

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -240,16 +240,16 @@ protected function getKernelParameters(): array
240240
{
241241
$parameters = parent::getKernelParameters();
242242
$bundlesPath = $this->getBundlesPath();
243-
$allBundles = !is_file($bundlesPath) ? [FrameworkBundle::class => ['all' => true]] : require $bundlesPath;
243+
$bundlesDefinition = !is_file($bundlesPath) ? [FrameworkBundle::class => ['all' => true]] : require $bundlesPath;
244244
$knownEnvs = [$this->environment => true];
245245

246-
foreach ($allBundles as $envs) {
246+
foreach ($bundlesDefinition as $envs) {
247247
$knownEnvs += $envs;
248248
}
249249
unset($knownEnvs['all']);
250+
$parameters['.container.known_envs'] = array_keys($knownEnvs);
250251
$parameters['.kernel.config_dir'] = $this->getConfigDir();
251-
$parameters['.kernel.known_envs'] = array_keys($knownEnvs);
252-
$parameters['.kernel.all_bundles'] = $allBundles;
252+
$parameters['.kernel.bundles_definition'] = $bundlesDefinition;
253253

254254
return $parameters;
255255
}

0 commit comments

Comments
 (0)