Skip to content

Commit b494600

Browse files
[DependencyInjection][Routing] Handle declaring services and routes using PHP arrays that follow the same shape as corresponding yaml files
1 parent 08f859f commit b494600

File tree

6 files changed

+63
-8
lines changed

6 files changed

+63
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ CHANGELOG
1111
* Deprecate registering a service without a class when its id is a non-existing FQCN
1212
* Allow multiple `#[AsDecorator]` attributes
1313
* Handle returning arrays and config-builders from config files
14+
* Handle declaring services using PHP arrays that follow the same shape as corresponding yaml files
1415
* Deprecate using `$this` or its internal scope from PHP config files; use the `$loader` variable instead
1516

1617
7.3

Loader/FileLoader.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,25 @@ public function registerAliasesForSinglyImplementedInterfaces(): void
259259
$this->interfaces = $this->singlyImplemented = $this->aliases = [];
260260
}
261261

262-
final protected function loadExtensionConfig(string $namespace, array $config): void
262+
final protected function loadExtensionConfig(string $namespace, array $config, string $file = '?'): void
263263
{
264+
if (\in_array($namespace, ['imports', 'services', 'parameters'], true)) {
265+
$yamlLoader = new YamlFileLoader($this->container, $this->locator, $this->env, $this->prepend);
266+
$loadContent = new \ReflectionMethod(YamlFileLoader::class, 'loadContent');
267+
$loadContent->invoke($yamlLoader, [$namespace => $config], $file);
268+
269+
if ($this->env && isset($config['when@'.$this->env])) {
270+
if (!\is_array($config['when@'.$this->env])) {
271+
throw new InvalidArgumentException(\sprintf('The "when@%s" key should contain an array in "%s".', $this->env, $file));
272+
}
273+
274+
$yamlLoader->env = null;
275+
$loadContent->invoke($yamlLoader, [$namespace => $config['when@'.$this->env]], $file);
276+
}
277+
278+
return;
279+
}
280+
264281
if (!$this->prepend) {
265282
$this->container->loadFromExtension($namespace, $config);
266283

Loader/PhpFileLoader.php

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -83,25 +83,25 @@ class_exists(ContainerConfigurator::class);
8383
}
8484

8585
if (\is_object($result) && \is_callable($result)) {
86-
$result = $this->executeCallback($result, new ContainerConfigurator($this->container, $this, $this->instanceof, $path, $resource, $this->env), $path);
86+
$result = $this->callConfigurator($result, new ContainerConfigurator($this->container, $this, $this->instanceof, $path, $resource, $this->env), $path);
8787
}
8888
if ($result instanceof ConfigBuilderInterface) {
89-
$this->loadExtensionConfig($result->getExtensionAlias(), ContainerConfigurator::processValue($result->toArray()));
89+
$this->loadExtensionConfig($result->getExtensionAlias(), ContainerConfigurator::processValue($result->toArray()), $path);
9090
} elseif (is_iterable($result)) {
9191
foreach ($result as $key => $config) {
9292
if ($config instanceof ConfigBuilderInterface) {
9393
if (\is_string($key) && $config->getExtensionAlias() !== $key) {
9494
throw new InvalidArgumentException(\sprintf('The extension alias "%s" of the "%s" config builder does not match the key "%s" in file "%s".', $config->getExtensionAlias(), get_debug_type($config), $key, $path));
9595
}
96-
$this->loadExtensionConfig($config->getExtensionAlias(), ContainerConfigurator::processValue($config->toArray()));
96+
$this->loadExtensionConfig($config->getExtensionAlias(), ContainerConfigurator::processValue($config->toArray()), $path);
9797
} elseif (!\is_string($key) || !\is_array($config)) {
9898
throw new InvalidArgumentException(\sprintf('The configuration returned in file "%s" must yield only string-keyed arrays or ConfigBuilderInterface values.', $path));
9999
} else {
100-
$this->loadExtensionConfig($key, ContainerConfigurator::processValue($config));
100+
$this->loadExtensionConfig($key, ContainerConfigurator::processValue($config), $path);
101101
}
102102
}
103103
} elseif (null !== $result) {
104-
throw new InvalidArgumentException(\sprintf('The return value in config file "%s" is invalid.', $path));
104+
throw new InvalidArgumentException(\sprintf('The return value in config file "%s" is invalid: "%s" given.', $path, get_debug_type($result)));
105105
}
106106

107107
$this->loadExtensionConfigs();
@@ -129,7 +129,7 @@ public function supports(mixed $resource, ?string $type = null): bool
129129
/**
130130
* Resolve the parameters to the $callback and execute it.
131131
*/
132-
private function executeCallback(callable $callback, ContainerConfigurator $containerConfigurator, string $path): mixed
132+
private function callConfigurator(callable $callback, ContainerConfigurator $containerConfigurator, string $path): mixed
133133
{
134134
$callback = $callback(...);
135135
$arguments = [];
@@ -202,7 +202,9 @@ private function executeCallback(callable $callback, ContainerConfigurator $cont
202202

203203
++$this->importing;
204204
try {
205-
return $callback(...$arguments);
205+
$result = $callback(...$arguments);
206+
207+
return \in_array($result, $configBuilders, true) ? null : $result;
206208
} catch (\Throwable $e) {
207209
$configBuilders = [];
208210
throw $e;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
parameters:
2+
foo: bar
3+
4+
services:
5+
service_container:
6+
class: Symfony\Component\DependencyInjection\ContainerInterface
7+
public: true
8+
synthetic: true
9+
Symfony\Component\DependencyInjection\Tests\Fixtures\Bar:
10+
class: Symfony\Component\DependencyInjection\Tests\Fixtures\Bar
11+
public: true
12+
my_service:
13+
class: Symfony\Component\DependencyInjection\Tests\Fixtures\Bar
14+
public: true
15+
arguments: [bar]
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
use Symfony\Component\DependencyInjection\Tests\Fixtures\Bar;
4+
5+
return [
6+
'parameters' => [
7+
'foo' => 'bar',
8+
],
9+
'services' => [
10+
'_defaults' => [
11+
'public' => true,
12+
],
13+
Bar::class => null,
14+
'my_service' => [
15+
'class' => Bar::class,
16+
'arguments' => ['%foo%'],
17+
],
18+
],
19+
];

Tests/Loader/PhpFileLoaderTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ public static function provideConfig()
144144
yield ['closure'];
145145
yield ['from_callable'];
146146
yield ['env_param'];
147+
yield ['array_config'];
147148
}
148149

149150
public function testResourceTags()

0 commit comments

Comments
 (0)