Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fffad85

Browse files
committed
prepend extension configs with file loaders
1 parent 39e4f04 commit fffad85

16 files changed

+173
-25
lines changed

src/Symfony/Component/DependencyInjection/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ CHANGELOG
88
* Add argument `$prepend` to `ContainerConfigurator::extension()` to prepend the configuration instead of appending it
99
* Have `ServiceLocator` implement `ServiceCollectionInterface`
1010
* Add `#[Lazy]` attribute as shortcut for `#[Autowire(lazy: [bool|string])]` and `#[Autoconfigure(lazy: [bool|string])]`
11+
* Add argument `$prepend` to `FileLoader::construct()` to prepend loaded configuration instead of appending it
12+
* [BC BREAK] When used in the `prependExtension()` methods, the `ContainerConfigurator::import()` method now prepends the configuration instead of appending it
1113

1214
7.0
1315
---

src/Symfony/Component/DependencyInjection/Extension/AbstractExtension.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ final public function prepend(ContainerBuilder $container): void
4949
$this->prependExtension($configurator, $container);
5050
};
5151

52-
$this->executeConfiguratorCallback($container, $callback, $this);
52+
$this->executeConfiguratorCallback($container, $callback, $this, true);
5353
}
5454

5555
final public function load(array $configs, ContainerBuilder $container): void

src/Symfony/Component/DependencyInjection/Extension/ExtensionTrait.php

+6-6
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@
3030
*/
3131
trait ExtensionTrait
3232
{
33-
private function executeConfiguratorCallback(ContainerBuilder $container, \Closure $callback, ConfigurableExtensionInterface $subject): void
33+
private function executeConfiguratorCallback(ContainerBuilder $container, \Closure $callback, ConfigurableExtensionInterface $subject, bool $prepend = false): void
3434
{
3535
$env = $container->getParameter('kernel.environment');
36-
$loader = $this->createContainerLoader($container, $env);
36+
$loader = $this->createContainerLoader($container, $env, $prepend);
3737
$file = (new \ReflectionObject($subject))->getFileName();
3838
$bundleLoader = $loader->getResolver()->resolve($file);
3939
if (!$bundleLoader instanceof PhpFileLoader) {
@@ -50,15 +50,15 @@ private function executeConfiguratorCallback(ContainerBuilder $container, \Closu
5050
}
5151
}
5252

53-
private function createContainerLoader(ContainerBuilder $container, string $env): DelegatingLoader
53+
private function createContainerLoader(ContainerBuilder $container, string $env, bool $prepend): DelegatingLoader
5454
{
5555
$buildDir = $container->getParameter('kernel.build_dir');
5656
$locator = new FileLocator();
5757
$resolver = new LoaderResolver([
58-
new XmlFileLoader($container, $locator, $env),
59-
new YamlFileLoader($container, $locator, $env),
58+
new XmlFileLoader($container, $locator, $env, $prepend),
59+
new YamlFileLoader($container, $locator, $env, $prepend),
6060
new IniFileLoader($container, $locator, $env),
61-
new PhpFileLoader($container, $locator, $env, new ConfigBuilderGenerator($buildDir)),
61+
new PhpFileLoader($container, $locator, $env, new ConfigBuilderGenerator($buildDir), $prepend),
6262
new GlobFileLoader($container, $locator, $env),
6363
new DirectoryLoader($container, $locator, $env),
6464
new ClosureLoader($container, $env),

src/Symfony/Component/DependencyInjection/Loader/FileLoader.php

+46-1
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,17 @@ abstract class FileLoader extends BaseFileLoader
4545
/** @var array<string, Alias> */
4646
protected array $aliases = [];
4747
protected bool $autoRegisterAliasesForSinglyImplementedInterfaces = true;
48+
protected bool $prepend = false;
49+
protected array $extensionConfigs = [];
50+
protected int $importing = 0;
4851

49-
public function __construct(ContainerBuilder $container, FileLocatorInterface $locator, ?string $env = null)
52+
/**
53+
* @param bool $prepend Whether to prepend extension config instead of appending them
54+
*/
55+
public function __construct(ContainerBuilder $container, FileLocatorInterface $locator, ?string $env = null, bool $prepend = false)
5056
{
5157
$this->container = $container;
58+
$this->prepend = $prepend;
5259

5360
parent::__construct($locator, $env);
5461
}
@@ -66,6 +73,7 @@ public function import(mixed $resource, ?string $type = null, bool|string $ignor
6673
throw new \TypeError(sprintf('Invalid argument $ignoreErrors provided to "%s::import()": boolean or "not_found" expected, "%s" given.', static::class, get_debug_type($ignoreErrors)));
6774
}
6875

76+
++$this->importing;
6977
try {
7078
return parent::import(...$args);
7179
} catch (LoaderLoadException $e) {
@@ -82,6 +90,8 @@ public function import(mixed $resource, ?string $type = null, bool|string $ignor
8290
if (__FILE__ !== $frame['file']) {
8391
throw $e;
8492
}
93+
} finally {
94+
--$this->importing;
8595
}
8696

8797
return null;
@@ -217,6 +227,41 @@ public function registerAliasesForSinglyImplementedInterfaces(): void
217227
$this->interfaces = $this->singlyImplemented = $this->aliases = [];
218228
}
219229

230+
protected function loadExtensionConfig(string $namespace, array $config): void
231+
{
232+
if (!$this->prepend) {
233+
$this->container->loadFromExtension($namespace, $config);
234+
235+
return;
236+
}
237+
238+
if ($this->importing) {
239+
if (!isset($this->extensionConfigs[$namespace])) {
240+
$this->extensionConfigs[$namespace] = [];
241+
}
242+
array_unshift($this->extensionConfigs[$namespace], $config);
243+
244+
return;
245+
}
246+
247+
$this->container->prependExtensionConfig($namespace, $config);
248+
}
249+
250+
protected function loadExtensionConfigs(): void
251+
{
252+
if ($this->importing || [] === $this->extensionConfigs) {
253+
return;
254+
}
255+
256+
foreach ($this->extensionConfigs as $namespace => $configs) {
257+
foreach ($configs as $config) {
258+
$this->container->prependExtensionConfig($namespace, $config);
259+
}
260+
}
261+
262+
$this->extensionConfigs = [];
263+
}
264+
220265
/**
221266
* Registers a definition in the container with its instanceof-conditionals.
222267
*/

src/Symfony/Component/DependencyInjection/Loader/PhpFileLoader.php

+13-4
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,9 @@ class PhpFileLoader extends FileLoader
3636
protected bool $autoRegisterAliasesForSinglyImplementedInterfaces = false;
3737
private ?ConfigBuilderGeneratorInterface $generator;
3838

39-
public function __construct(ContainerBuilder $container, FileLocatorInterface $locator, ?string $env = null, ?ConfigBuilderGeneratorInterface $generator = null)
39+
public function __construct(ContainerBuilder $container, FileLocatorInterface $locator, ?string $env = null, ?ConfigBuilderGeneratorInterface $generator = null, bool $prepend = false)
4040
{
41-
parent::__construct($container, $locator, $env);
41+
parent::__construct($container, $locator, $env, $prepend);
4242
$this->generator = $generator;
4343
}
4444

@@ -145,10 +145,19 @@ class_exists(ContainerConfigurator::class);
145145

146146
$callback(...$arguments);
147147

148-
/** @var ConfigBuilderInterface $configBuilder */
148+
$this->loadFromExtensions($configBuilders);
149+
}
150+
151+
/**
152+
* @param iterable<ConfigBuilderInterface> $configBuilders
153+
*/
154+
private function loadFromExtensions(iterable $configBuilders): void
155+
{
149156
foreach ($configBuilders as $configBuilder) {
150-
$containerConfigurator->extension($configBuilder->getExtensionAlias(), $configBuilder->toArray());
157+
$this->loadExtensionConfig($configBuilder->getExtensionAlias(), $configBuilder->toArray());
151158
}
159+
160+
$this->loadExtensionConfigs();
152161
}
153162

154163
/**

src/Symfony/Component/DependencyInjection/Loader/XmlFileLoader.php

+4-2
Original file line numberDiff line numberDiff line change
@@ -808,7 +808,7 @@ private function validateExtensions(\DOMDocument $dom, string $file): void
808808
}
809809

810810
// can it be handled by an extension?
811-
if (!$this->container->hasExtension($node->namespaceURI)) {
811+
if (!$this->prepend && !$this->container->hasExtension($node->namespaceURI)) {
812812
$extensionNamespaces = array_filter(array_map(fn (ExtensionInterface $ext) => $ext->getNamespace(), $this->container->getExtensions()));
813813
throw new InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in "%s"). Looked for namespace "%s", found "%s".', $node->tagName, $file, $node->namespaceURI, $extensionNamespaces ? implode('", "', $extensionNamespaces) : 'none'));
814814
}
@@ -830,8 +830,10 @@ private function loadFromExtensions(\DOMDocument $xml): void
830830
$values = [];
831831
}
832832

833-
$this->container->loadFromExtension($node->namespaceURI, $values);
833+
$this->loadExtensionConfig($node->namespaceURI, $values);
834834
}
835+
836+
$this->loadExtensionConfigs();
835837
}
836838

837839
/**

src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php

+5-3
Original file line numberDiff line numberDiff line change
@@ -802,7 +802,7 @@ private function validate(mixed $content, string $file): ?array
802802
continue;
803803
}
804804

805-
if (!$this->container->hasExtension($namespace)) {
805+
if (!$this->prepend && !$this->container->hasExtension($namespace)) {
806806
$extensionNamespaces = array_filter(array_map(fn (ExtensionInterface $ext) => $ext->getAlias(), $this->container->getExtensions()));
807807
throw new InvalidArgumentException(sprintf('There is no extension able to load the configuration for "%s" (in "%s"). Looked for namespace "%s", found "%s".', $namespace, $file, $namespace, $extensionNamespaces ? sprintf('"%s"', implode('", "', $extensionNamespaces)) : 'none'));
808808
}
@@ -941,12 +941,14 @@ private function loadFromExtensions(array $content): void
941941
continue;
942942
}
943943

944-
if (!\is_array($values) && null !== $values) {
944+
if (!\is_array($values)) {
945945
$values = [];
946946
}
947947

948-
$this->container->loadFromExtension($namespace, $values);
948+
$this->loadExtensionConfig($namespace, $values);
949949
}
950+
951+
$this->loadExtensionConfigs();
950952
}
951953

952954
private function checkDefinition(string $id, array $definition, string $file): void

src/Symfony/Component/DependencyInjection/Tests/Extension/AbstractExtensionTest.php

+33-7
Original file line numberDiff line numberDiff line change
@@ -54,28 +54,54 @@ public function configure(DefinitionConfigurator $definition): void
5454
self::assertSame($expected, $this->processConfiguration($extension));
5555
}
5656

57-
public function testPrependAppendExtensionConfig()
57+
public function testPrependExtensionConfig()
5858
{
5959
$extension = new class() extends AbstractExtension {
60+
public function configure(DefinitionConfigurator $definition): void
61+
{
62+
$definition->rootNode()
63+
->children()
64+
->scalarNode('foo')->end()
65+
->end();
66+
}
67+
6068
public function prependExtension(ContainerConfigurator $container, ContainerBuilder $builder): void
6169
{
62-
// append config
63-
$container->extension('third', ['foo' => 'append']);
70+
// prepend config from plain array
71+
$container->extension('third', ['foo' => 'pong'], true);
72+
73+
// prepend config from external file
74+
$container->import('../Fixtures/config/packages/ping.yaml');
75+
}
76+
77+
public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
78+
{
79+
$container->parameters()->set('foo_param', $config['foo']);
80+
}
6481

65-
// prepend config
66-
$container->extension('third', ['foo' => 'prepend'], true);
82+
public function getAlias(): string
83+
{
84+
return 'third';
6785
}
6886
};
6987

7088
$container = $this->processPrependExtension($extension);
7189

7290
$expected = [
73-
['foo' => 'prepend'],
91+
['foo' => 'a'],
92+
['foo' => 'c1'],
93+
['foo' => 'c2'],
94+
['foo' => 'b'],
95+
['foo' => 'ping'],
96+
['foo' => 'pong'],
7497
['foo' => 'bar'],
75-
['foo' => 'append'],
7698
];
7799

78100
self::assertSame($expected, $container->getExtensionConfig('third'));
101+
102+
$container = $this->processLoadExtension($extension, $expected);
103+
104+
self::assertSame('bar', $container->getParameter('foo_param'));
79105
}
80106

81107
public function testLoadExtension()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
imports:
2+
- { resource: './third_a.yaml' }
3+
- { resource: './third_b.yaml' }
4+
5+
third:
6+
foo: ping
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
third:
2+
foo: a
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
imports:
2+
- { resource: './third_c.yaml' }
3+
4+
third:
5+
foo: b
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
third:
2+
foo: c1
3+
4+
when@test:
5+
third:
6+
foo: c2

src/Symfony/Component/DependencyInjection/Tests/Loader/PhpFileLoaderTest.php

+15
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,21 @@ public function testLoad()
4747
$this->assertEquals('foo', $container->getParameter('foo'), '->load() loads a PHP file resource');
4848
}
4949

50+
public function testPrependExtensionConfig()
51+
{
52+
$container = new ContainerBuilder();
53+
$container->registerExtension(new \AcmeExtension());
54+
$container->prependExtensionConfig('acme', ['foo' => 'bar']);
55+
$loader = new PhpFileLoader($container, new FileLocator(\dirname(__DIR__).'/Fixtures'), 'prod', new ConfigBuilderGenerator(sys_get_temp_dir()), true);
56+
$loader->load('config/config_builder.php');
57+
58+
$expected = [
59+
['color' => 'blue'],
60+
['foo' => 'bar'],
61+
];
62+
$this->assertSame($expected, $container->getExtensionConfig('acme'));
63+
}
64+
5065
public function testConfigServices()
5166
{
5267
$fixtures = realpath(__DIR__.'/../Fixtures');

src/Symfony/Component/DependencyInjection/Tests/Loader/XmlFileLoaderTest.php

+14
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,20 @@ public function testExtensions()
604604
}
605605
}
606606

607+
public function testPrependExtensionConfig()
608+
{
609+
$container = new ContainerBuilder();
610+
$container->prependExtensionConfig('http://www.example.com/schema/project', ['foo' => 'bar']);
611+
$loader = new XmlFileLoader($container, new FileLocator(self::$fixturesPath.'/xml'), prepend: true);
612+
$loader->load('extensions/services1.xml');
613+
614+
$expected = [
615+
['babar' => 'babar'],
616+
['foo' => 'bar'],
617+
];
618+
$this->assertSame($expected, $container->getExtensionConfig('http://www.example.com/schema/project'));
619+
}
620+
607621
public function testExtensionInPhar()
608622
{
609623
if (\extension_loaded('suhosin') && !str_contains(\ini_get('suhosin.executor.include.whitelist'), 'phar')) {

src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php

+14
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,20 @@ public function testExtensions()
346346
}
347347
}
348348

349+
public function testPrependExtensionConfig()
350+
{
351+
$container = new ContainerBuilder();
352+
$container->prependExtensionConfig('project', ['foo' => 'bar']);
353+
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'), prepend: true);
354+
$loader->load('services10.yml');
355+
356+
$expected = [
357+
['test' => '%project.parameter.foo%'],
358+
['foo' => 'bar'],
359+
];
360+
$this->assertSame($expected, $container->getExtensionConfig('project'));
361+
}
362+
349363
public function testExtensionWithNullConfig()
350364
{
351365
$container = new ContainerBuilder();

src/Symfony/Component/HttpKernel/Bundle/BundleExtension.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public function prepend(ContainerBuilder $container): void
5151
$this->subject->prependExtension($configurator, $container);
5252
};
5353

54-
$this->executeConfiguratorCallback($container, $callback, $this->subject);
54+
$this->executeConfiguratorCallback($container, $callback, $this->subject, true);
5555
}
5656

5757
public function load(array $configs, ContainerBuilder $container): void

0 commit comments

Comments
 (0)