Skip to content

Commit bb6432b

Browse files
HeahDudenicolas-grekas
authored andcommitted
[DependencyInjection] Enable deprecating parameters
1 parent 233b9eb commit bb6432b

18 files changed

+669
-39
lines changed

src/Symfony/Component/DependencyInjection/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ CHANGELOG
77
* Add options `inline_factories` and `inline_class_loader` to `PhpDumper::dump()`
88
* Deprecate `PhpDumper` options `inline_factories_parameter` and `inline_class_loader_parameter`
99
* Add `RemoveBuildParametersPass`, which removes parameters starting with a dot during compilation
10+
* Enable deprecating parameters with `ContainerBuilder::deprecateParameter()`
1011

1112
6.2
1213
---

src/Symfony/Component/DependencyInjection/Container.php

+5-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
2323
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
2424
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
25+
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
2526
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
2627
use Symfony\Contracts\Service\ResetInterface;
2728

@@ -82,7 +83,10 @@ public function compile()
8283
{
8384
$this->parameterBag->resolve();
8485

85-
$this->parameterBag = new FrozenParameterBag($this->parameterBag->all());
86+
$this->parameterBag = new FrozenParameterBag(
87+
$this->parameterBag->all(),
88+
$this->parameterBag instanceof ParameterBag ? $this->parameterBag->allDeprecated() : []
89+
);
8690

8791
$this->compiled = true;
8892
}

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

+27-4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
use Symfony\Component\DependencyInjection\Exception\BadMethodCallException;
3535
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
3636
use Symfony\Component\DependencyInjection\Exception\LogicException;
37+
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
3738
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
3839
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
3940
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
@@ -605,7 +606,15 @@ public function merge(self $container)
605606
}
606607
}
607608
$this->addAliases($container->getAliases());
608-
$this->getParameterBag()->add($container->getParameterBag()->all());
609+
$parameterBag = $this->getParameterBag();
610+
$otherBag = $container->getParameterBag();
611+
$parameterBag->add($otherBag->all());
612+
613+
if ($parameterBag instanceof ParameterBag && $otherBag instanceof ParameterBag) {
614+
foreach ($otherBag->allDeprecated() as $name => $deprecated) {
615+
$parameterBag->deprecate($name, ...$deprecated);
616+
}
617+
}
609618

610619
if ($this->trackResources) {
611620
foreach ($container->getResources() as $resource) {
@@ -621,9 +630,9 @@ public function merge(self $container)
621630
$this->extensionConfigs[$name] = array_merge($this->extensionConfigs[$name], $container->getExtensionConfig($name));
622631
}
623632

624-
if ($this->getParameterBag() instanceof EnvPlaceholderParameterBag && $container->getParameterBag() instanceof EnvPlaceholderParameterBag) {
625-
$envPlaceholders = $container->getParameterBag()->getEnvPlaceholders();
626-
$this->getParameterBag()->mergeEnvPlaceholders($container->getParameterBag());
633+
if ($parameterBag instanceof EnvPlaceholderParameterBag && $otherBag instanceof EnvPlaceholderParameterBag) {
634+
$envPlaceholders = $otherBag->getEnvPlaceholders();
635+
$parameterBag->mergeEnvPlaceholders($otherBag);
627636
} else {
628637
$envPlaceholders = [];
629638
}
@@ -684,6 +693,20 @@ public function prependExtensionConfig(string $name, array $config)
684693
array_unshift($this->extensionConfigs[$name], $config);
685694
}
686695

696+
/**
697+
* Deprecates a service container parameter.
698+
*
699+
* @throws ParameterNotFoundException if the parameter is not defined
700+
*/
701+
public function deprecateParameter(string $name, string $package, string $version, string $message = 'The parameter "%s" is deprecated.'): void
702+
{
703+
if (!$this->parameterBag instanceof ParameterBag) {
704+
throw new BadMethodCallException(sprintf('The parameter bag must be an instance of "%s" to call "%s".', ParameterBag::class, __METHOD__));
705+
}
706+
707+
$this->parameterBag->deprecate($name, $package, $version, $message);
708+
}
709+
687710
/**
688711
* Compiles the container.
689712
*

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

+34-5
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper;
3636
use Symfony\Component\DependencyInjection\Loader\FileLoader;
3737
use Symfony\Component\DependencyInjection\Parameter;
38+
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
3839
use Symfony\Component\DependencyInjection\Reference;
3940
use Symfony\Component\DependencyInjection\ServiceLocator as BaseServiceLocator;
4041
use Symfony\Component\DependencyInjection\TypedReference;
@@ -1232,6 +1233,8 @@ private function startClass(string $class, string $baseClass, bool $hasProxyClas
12321233
*/
12331234
class $class extends $baseClass
12341235
{
1236+
private const DEPRECATED_PARAMETERS = [];
1237+
12351238
protected \$parameters = [];
12361239
protected readonly \WeakReference \$ref;
12371240
@@ -1240,11 +1243,9 @@ public function __construct()
12401243
\$this->ref = \WeakReference::create(\$this);
12411244
12421245
EOF;
1246+
$code = str_replace(" private const DEPRECATED_PARAMETERS = [];\n\n", $this->addDeprecatedParameters(), $code);
12431247
if ($this->asFiles) {
1244-
$code = str_replace('$parameters = []', "\$containerDir;\n protected \$parameters = [];\n private \$buildParameters", $code);
1245-
$code = str_replace('__construct()', '__construct(array $buildParameters = [], $containerDir = __DIR__)', $code);
1246-
$code .= " \$this->buildParameters = \$buildParameters;\n";
1247-
$code .= " \$this->containerDir = \$containerDir;\n";
1248+
$code = str_replace('__construct()', '__construct(private array $buildParameters = [], protected string $containerDir = __DIR__)', $code);
12481249

12491250
if (null !== $this->targetDirRegex) {
12501251
$code = str_replace('$parameters = []', "\$targetDir;\n protected \$parameters = []", $code);
@@ -1389,6 +1390,24 @@ public function getRemovedIds(): array
13891390
EOF;
13901391
}
13911392

1393+
private function addDeprecatedParameters(): string
1394+
{
1395+
if (!($bag = $this->container->getParameterBag()) instanceof ParameterBag) {
1396+
return '';
1397+
}
1398+
1399+
if (!$deprecated = $bag->allDeprecated()) {
1400+
return '';
1401+
}
1402+
$code = '';
1403+
ksort($deprecated);
1404+
foreach ($deprecated as $param => $deprecation) {
1405+
$code .= ' '.$this->doExport($param).' => ['.implode(', ', array_map($this->doExport(...), $deprecation))."],\n";
1406+
}
1407+
1408+
return " private const DEPRECATED_PARAMETERS = [\n{$code} ];\n\n";
1409+
}
1410+
13921411
private function addMethodMap(): string
13931412
{
13941413
$code = '';
@@ -1550,6 +1569,10 @@ private function addDefaultParametersMethod(): string
15501569
15511570
public function getParameter(string $name): array|bool|string|int|float|\UnitEnum|null
15521571
{
1572+
if (isset(self::DEPRECATED_PARAMETERS[$name])) {
1573+
trigger_deprecation(...self::DEPRECATED_PARAMETERS[$name]);
1574+
}
1575+
15531576
if (isset($this->buildParameters[$name])) {
15541577
return $this->buildParameters[$name];
15551578
}
@@ -1588,17 +1611,23 @@ public function getParameterBag(): ParameterBagInterface
15881611
foreach ($this->buildParameters as $name => $value) {
15891612
$parameters[$name] = $value;
15901613
}
1591-
$this->parameterBag = new FrozenParameterBag($parameters);
1614+
$this->parameterBag = new FrozenParameterBag($parameters, self::DEPRECATED_PARAMETERS);
15921615
}
15931616
15941617
return $this->parameterBag;
15951618
}
15961619

15971620
EOF;
1621+
15981622
if (!$this->asFiles) {
15991623
$code = preg_replace('/^.*buildParameters.*\n.*\n.*\n\n?/m', '', $code);
16001624
}
16011625

1626+
if (!($bag = $this->container->getParameterBag()) instanceof ParameterBag || !$bag->allDeprecated()) {
1627+
$code = preg_replace("/\n.*DEPRECATED_PARAMETERS.*\n.*\n.*\n/m", '', $code, 1);
1628+
$code = str_replace(', self::DEPRECATED_PARAMETERS', '', $code);
1629+
}
1630+
16021631
if ($dynamicPhp) {
16031632
$loadedDynamicParameters = $this->exportParameters(array_combine(array_keys($dynamicPhp), array_fill(0, \count($dynamicPhp), false)), '', 8);
16041633
$getDynamicParameter = <<<'EOF'

src/Symfony/Component/DependencyInjection/ParameterBag/FrozenParameterBag.php

+7-3
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,12 @@ class FrozenParameterBag extends ParameterBag
2525
* all keys are already lowercased.
2626
*
2727
* This is always the case when used internally.
28-
*
29-
* @param array $parameters An array of parameters
3028
*/
31-
public function __construct(array $parameters = [])
29+
public function __construct(array $parameters = [], array $deprecatedParameters = [])
3230
{
3331
$this->parameters = $parameters;
3432
$this->resolved = true;
33+
$this->deprecatedParameters = $deprecatedParameters;
3534
}
3635

3736
public function clear()
@@ -49,6 +48,11 @@ public function set(string $name, array|bool|string|int|float|\UnitEnum|null $va
4948
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
5049
}
5150

51+
public function deprecate(string $name, string $package, string $version, string $message = 'The parameter "%s" is deprecated.')
52+
{
53+
throw new LogicException('Impossible to call deprecate() on a frozen ParameterBag.');
54+
}
55+
5256
public function remove(string $name)
5357
{
5458
throw new LogicException('Impossible to call remove() on a frozen ParameterBag.');

src/Symfony/Component/DependencyInjection/ParameterBag/ParameterBag.php

+25-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class ParameterBag implements ParameterBagInterface
2424
{
2525
protected $parameters = [];
2626
protected $resolved = false;
27+
protected array $deprecatedParameters = [];
2728

2829
public function __construct(array $parameters = [])
2930
{
@@ -47,6 +48,11 @@ public function all(): array
4748
return $this->parameters;
4849
}
4950

51+
public function allDeprecated(): array
52+
{
53+
return $this->deprecatedParameters;
54+
}
55+
5056
public function get(string $name): array|bool|string|int|float|\UnitEnum|null
5157
{
5258
if (!\array_key_exists($name, $this->parameters)) {
@@ -81,6 +87,10 @@ public function get(string $name): array|bool|string|int|float|\UnitEnum|null
8187
throw new ParameterNotFoundException($name, null, null, null, $alternatives, $nonNestedAlternative);
8288
}
8389

90+
if (isset($this->deprecatedParameters[$name])) {
91+
trigger_deprecation(...$this->deprecatedParameters[$name]);
92+
}
93+
8494
return $this->parameters[$name];
8595
}
8696

@@ -95,14 +105,28 @@ public function set(string $name, array|bool|string|int|float|\UnitEnum|null $va
95105
$this->parameters[$name] = $value;
96106
}
97107

108+
/**
109+
* Deprecates a service container parameter.
110+
*
111+
* @throws ParameterNotFoundException if the parameter is not defined
112+
*/
113+
public function deprecate(string $name, string $package, string $version, string $message = 'The parameter "%s" is deprecated.')
114+
{
115+
if (!\array_key_exists($name, $this->parameters)) {
116+
throw new ParameterNotFoundException($name);
117+
}
118+
119+
$this->deprecatedParameters[$name] = [$package, $version, $message, $name];
120+
}
121+
98122
public function has(string $name): bool
99123
{
100124
return \array_key_exists($name, $this->parameters);
101125
}
102126

103127
public function remove(string $name)
104128
{
105-
unset($this->parameters[$name]);
129+
unset($this->parameters[$name], $this->deprecatedParameters[$name]);
106130
}
107131

108132
public function resolve()

src/Symfony/Component/DependencyInjection/Tests/ContainerBuilderTest.php

+106
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,11 @@
3131
use Symfony\Component\DependencyInjection\ContainerBuilder;
3232
use Symfony\Component\DependencyInjection\ContainerInterface;
3333
use Symfony\Component\DependencyInjection\Definition;
34+
use Symfony\Component\DependencyInjection\Exception\BadMethodCallException;
3435
use Symfony\Component\DependencyInjection\Exception\EnvNotFoundException;
3536
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
3637
use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
38+
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
3739
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
3840
use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException;
3941
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
@@ -42,6 +44,7 @@
4244
use Symfony\Component\DependencyInjection\Loader\ClosureLoader;
4345
use Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag;
4446
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
47+
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
4548
use Symfony\Component\DependencyInjection\Reference;
4649
use Symfony\Component\DependencyInjection\ServiceLocator;
4750
use Symfony\Component\DependencyInjection\Tests\Compiler\Foo;
@@ -100,6 +103,109 @@ public function testDefinitions()
100103
}
101104
}
102105

106+
/**
107+
* The test should be kept in the group as it always expects a deprecation.
108+
*
109+
* @group legacy
110+
*/
111+
public function testDeprecateParameter()
112+
{
113+
$builder = new ContainerBuilder();
114+
$builder->setParameter('foo', 'bar');
115+
116+
$builder->deprecateParameter('foo', 'symfony/test', '6.3');
117+
118+
$this->expectDeprecation('Since symfony/test 6.3: The parameter "foo" is deprecated.');
119+
120+
$builder->getParameter('foo');
121+
}
122+
123+
/**
124+
* The test should be kept in the group as it always expects a deprecation.
125+
*
126+
* @group legacy
127+
*/
128+
public function testParameterDeprecationIsTrgiggeredWhenCompiled()
129+
{
130+
$builder = new ContainerBuilder();
131+
$builder->setParameter('foo', '%bar%');
132+
$builder->setParameter('bar', 'baz');
133+
134+
$builder->deprecateParameter('bar', 'symfony/test', '6.3');
135+
136+
$this->expectDeprecation('Since symfony/test 6.3: The parameter "bar" is deprecated.');
137+
138+
$builder->compile();
139+
}
140+
141+
public function testDeprecateParameterThrowsWhenParameterIsUndefined()
142+
{
143+
$builder = new ContainerBuilder();
144+
145+
$this->expectException(ParameterNotFoundException::class);
146+
$this->expectExceptionMessage('You have requested a non-existent parameter "foo".');
147+
148+
$builder->deprecateParameter('foo', 'symfony/test', '6.3');
149+
}
150+
151+
public function testDeprecateParameterThrowsWhenParameterBagIsNotInternal()
152+
{
153+
$builder = new ContainerBuilder(new class() implements ParameterBagInterface {
154+
public function clear()
155+
{
156+
}
157+
158+
public function add(array $parameters)
159+
{
160+
}
161+
162+
public function all(): array
163+
{
164+
return [];
165+
}
166+
167+
public function get(string $name): array|bool|string|int|float|\UnitEnum|null
168+
{
169+
return null;
170+
}
171+
172+
public function remove(string $name)
173+
{
174+
}
175+
176+
public function set(string $name, \UnitEnum|float|int|bool|array|string|null $value)
177+
{
178+
}
179+
180+
public function has(string $name): bool
181+
{
182+
return false;
183+
}
184+
185+
public function resolve()
186+
{
187+
}
188+
189+
public function resolveValue(mixed $value)
190+
{
191+
}
192+
193+
public function escapeValue(mixed $value): mixed
194+
{
195+
return null;
196+
}
197+
198+
public function unescapeValue(mixed $value): mixed
199+
{
200+
return null;
201+
}
202+
});
203+
204+
$this->expectException(BadMethodCallException::class);
205+
206+
$builder->deprecateParameter('foo', 'symfony/test', '6.3');
207+
}
208+
103209
public function testRegister()
104210
{
105211
$builder = new ContainerBuilder();

0 commit comments

Comments
 (0)