Skip to content

Commit 70bd523

Browse files
[DependencyInjection][Routing] Define array-shapes to help writing PHP configs using yaml-like arrays
1 parent 9731ad4 commit 70bd523

File tree

18 files changed

+353
-26
lines changed

18 files changed

+353
-26
lines changed

src/Symfony/Component/DependencyInjection/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ CHANGELOG
1212
* Allow multiple `#[AsDecorator]` attributes
1313
* Handle returning arrays and config-builders from config files
1414
* Handle declaring services using PHP arrays that follow the same shape as corresponding yaml files
15+
* Add `ServicesConfig`, `ImportsConfig` and `ParametersConfig` to help writing PHP configs using yaml-like array-shapes
1516
* Deprecate using `$this` or its internal scope from PHP config files; use the `$loader` variable instead
1617

1718
7.3
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Loader\Config;
13+
14+
use Symfony\Component\Config\Builder\ConfigBuilderInterface;
15+
16+
class ImportsConfig implements ConfigBuilderInterface
17+
{
18+
/**
19+
* @param list<string|array{
20+
* resource: string,
21+
* type?: string|null,
22+
* ignore_errors?: bool,
23+
* }> $imports
24+
*/
25+
public function __construct(
26+
private array $imports,
27+
) {
28+
}
29+
30+
public function toArray(): array
31+
{
32+
return $this->imports;
33+
}
34+
35+
public function getExtensionAlias(): string
36+
{
37+
return 'imports';
38+
}
39+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Loader\Config;
13+
14+
use Symfony\Component\Config\Builder\ConfigBuilderInterface;
15+
16+
class ParametersConfig implements ConfigBuilderInterface
17+
{
18+
/**
19+
* @param array<string, scalar|\UnitEnum|array<scalar|\UnitEnum|array|null>|null> $parameters
20+
*/
21+
public function __construct(
22+
private array $parameters,
23+
) {
24+
}
25+
26+
public function toArray(): array
27+
{
28+
return $this->parameters;
29+
}
30+
31+
public function getExtensionAlias(): string
32+
{
33+
return 'parameters';
34+
}
35+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Loader\Config;
13+
14+
use Symfony\Component\Config\Builder\ConfigBuilderInterface;
15+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
16+
use Symfony\Component\DependencyInjection\Loader\Configurator\ReferenceConfigurator;
17+
use Symfony\Component\DependencyInjection\Reference;
18+
use Symfony\Component\ExpressionLanguage\Expression;
19+
20+
/**
21+
* @phpstan-type Definition array{
22+
* class?: string,
23+
* file?: string,
24+
* parent?: string,
25+
* shared?: bool,
26+
* synthetic?: bool,
27+
* lazy?: bool|string,
28+
* public?: bool,
29+
* abstract?: bool,
30+
* deprecated?: Deprecation,
31+
* factory?: Callback,
32+
* configurator?: Callback,
33+
* arguments?: Arguments,
34+
* properties?: array<string, mixed>,
35+
* calls?: list<Call>,
36+
* tags?: Tags,
37+
* resource_tags?: Tags,
38+
* decorates?: string,
39+
* decoration_inner_name?: string,
40+
* decoration_priority?: int,
41+
* decoration_on_invalid?: 'exception'|'ignore'|null,
42+
* autowire?: bool,
43+
* autoconfigure?: bool,
44+
* bind?: array<string, mixed>,
45+
* constructor?: string,
46+
* from_callable?: mixed,
47+
* }
48+
* @phpstan-type Alias string|array{
49+
* alias: string,
50+
* public?: bool,
51+
* deprecated?: Deprecation,
52+
* }
53+
* @phpstan-type Prototype array{
54+
* resource: string,
55+
* namespace?: string,
56+
* exclude?: string|list<string>,
57+
* parent?: string,
58+
* shared?: bool,
59+
* lazy?: bool|string,
60+
* public?: bool,
61+
* abstract?: bool,
62+
* deprecated?: Deprecation,
63+
* factory?: Callback,
64+
* arguments?: Arguments,
65+
* properties?: array<string, mixed>,
66+
* configurator?: Callback,
67+
* calls?: list<Call>,
68+
* tags?: Tags,
69+
* resource_tags?: Tags,
70+
* autowire?: bool,
71+
* autoconfigure?: bool,
72+
* bind?: array<string, mixed>,
73+
* constructor?: string,
74+
* }
75+
* @phpstan-type Stack array{
76+
* stack: list<Definition|Alias|Prototype|array<class-string, Arguments|null>>,
77+
* public?: bool,
78+
* deprecated?: Deprecation,
79+
* }
80+
* @phpstan-type Arguments list<mixed>|array<string, mixed>
81+
* @phpstan-type Callback string|array{0:string|Reference|ReferenceConfigurator,1:string}|\Closure|Reference|ReferenceConfigurator|Expression
82+
* @phpstan-type Tags list<string|array<string, array<string, mixed>>>
83+
* @phpstan-type Deprecation array{package: string, version: string, message?: string}
84+
* @phpstan-type Call array<string, Arguments>|array{0:string, 1?:Arguments, 2?:bool}|array{method:string, arguments?:Arguments, returns_clone?:bool}
85+
*/
86+
class ServicesConfig implements ConfigBuilderInterface
87+
{
88+
/**
89+
* $services:
90+
* @param array<string, Definition|Alias|Prototype|Stack>|array<class-string, Arguments|null> $services
91+
*
92+
* $defaults:
93+
* @param array{
94+
* public?: bool,
95+
* tags?: Tags,
96+
* resource_tags?: Tags,
97+
* autowire?: bool,
98+
* autoconfigure?: bool,
99+
* bind?: array<string, mixed>,
100+
* } $defaults
101+
*
102+
* $instanceof:
103+
* @param array{
104+
* shared?: bool,
105+
* lazy?: bool|string,
106+
* public?: bool,
107+
* properties?: array<string, mixed>,
108+
* configurator?: Callback,
109+
* calls?: list<Call>,
110+
* tags?: Tags,
111+
* resource_tags?: Tags,
112+
* autowire?: bool,
113+
* bind?: array<string, mixed>,
114+
* constructor?: string,
115+
* } $instanceof
116+
*/
117+
public function __construct(
118+
private array $services,
119+
array $defaults = [
120+
'autowire' => true,
121+
'autoconfigure' => true,
122+
],
123+
array $instanceof = [],
124+
) {
125+
if (isset($services['_defaults']) || isset($services['_instanceof'])) {
126+
throw new InvalidArgumentException('The $services argument should not contain "_defaults" or "_instanceof" keys, use the $defaults and $instanceof parameters instead.');
127+
}
128+
$this->services['_defaults'] = $defaults += ['autowire' => true, 'autoconfigure' => true];
129+
$this->services['_instanceof'] = $instanceof;
130+
}
131+
132+
public function toArray(): array
133+
{
134+
return $this->services;
135+
}
136+
137+
public function getExtensionAlias(): string
138+
{
139+
return 'services';
140+
}
141+
}

src/Symfony/Component/DependencyInjection/Loader/Configurator/AbstractConfigurator.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ public static function processValue(mixed $value, bool $allowServices = false):
101101
case $value instanceof \UnitEnum:
102102
return $value;
103103

104+
case $value instanceof \Closure:
105+
return self::processClosure($value);
106+
104107
case $value instanceof ArgumentInterface:
105108
case $value instanceof Definition:
106109
case $value instanceof Expression:
@@ -120,7 +123,7 @@ public static function processValue(mixed $value, bool $allowServices = false):
120123
*
121124
* @throws InvalidArgumentException if the closure is anonymous or references a non-static method
122125
*/
123-
final public static function processClosure(\Closure $closure): callable
126+
private static function processClosure(\Closure $closure): callable
124127
{
125128
$function = new \ReflectionFunction($closure);
126129
if ($function->isAnonymous()) {

src/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,6 @@ function service_closure(string $serviceId): ClosureReferenceConfigurator
194194
*/
195195
function closure(string|array|\Closure|ReferenceConfigurator|Expression $callable): InlineServiceConfigurator
196196
{
197-
if ($callable instanceof \Closure) {
198-
$callable = AbstractConfigurator::processClosure($callable);
199-
}
200-
201197
return (new InlineServiceConfigurator(new Definition('Closure')))
202198
->factory(['Closure', 'fromCallable'])
203199
->args([$callable]);

src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/ConfiguratorTrait.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@ trait ConfiguratorTrait
2222
*/
2323
final public function configurator(string|array|\Closure|ReferenceConfigurator $configurator): static
2424
{
25-
if ($configurator instanceof \Closure) {
26-
$this->definition->setConfigurator(static::processClosure($configurator));
27-
28-
return $this;
29-
}
30-
3125
$this->definition->setConfigurator(static::processValue($configurator, true));
3226

3327
return $this;

src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/FactoryTrait.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,6 @@ trait FactoryTrait
2424
*/
2525
final public function factory(string|array|\Closure|ReferenceConfigurator|Expression $factory): static
2626
{
27-
if ($factory instanceof \Closure) {
28-
$this->definition->setFactory(static::processClosure($factory));
29-
30-
return $this;
31-
}
32-
3327
if (\is_string($factory) && 1 === substr_count($factory, ':')) {
3428
$factoryParts = explode(':', $factory);
3529

src/Symfony/Component/DependencyInjection/Loader/Configurator/Traits/FromCallableTrait.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,6 @@ final public function fromCallable(string|array|\Closure|ReferenceConfigurator|E
4141

4242
$this->definition->setFactory(['Closure', 'fromCallable']);
4343

44-
if ($callable instanceof \Closure) {
45-
$callable = static::processClosure($callable);
46-
}
47-
4844
if (\is_string($callable) && 1 === substr_count($callable, ':')) {
4945
$parts = explode(':', $callable);
5046

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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+
autowire: true
13+
autoconfigure: true
14+
my_service:
15+
class: Symfony\Component\DependencyInjection\Tests\Fixtures\Bar
16+
public: true
17+
autowire: true
18+
autoconfigure: true
19+
arguments: [bar]

0 commit comments

Comments
 (0)