Skip to content

Commit 2a3923f

Browse files
committed
Introducing container non-empty parameters
1 parent 2aacb6c commit 2a3923f

38 files changed

+649
-79
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

+1
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ public function load(array $configs, ContainerBuilder $container): void
308308
if (isset($config['secret'])) {
309309
$container->setParameter('kernel.secret', $config['secret']);
310310
}
311+
$container->nonEmptyParameter('kernel.secret', 'A non-empty value for the parameter "kernel.secret" is required. Did you forget to configure the "framework.secret" option?');
311312

312313
$container->setParameter('kernel.http_method_override', $config['http_method_override']);
313314
$container->setParameter('kernel.trust_x_sendfile_type_header', $config['trust_x_sendfile_type_header']);

src/Symfony/Bundle/FrameworkBundle/composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"ext-xml": "*",
2222
"symfony/cache": "^6.4|^7.0",
2323
"symfony/config": "^6.4|^7.0",
24-
"symfony/dependency-injection": "^7.1",
24+
"symfony/dependency-injection": "^7.2",
2525
"symfony/deprecation-contracts": "^2.5|^3",
2626
"symfony/error-handler": "^6.4|^7.0",
2727
"symfony/event-dispatcher": "^6.4|^7.0",

src/Symfony/Component/DependencyInjection/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CHANGELOG
66

77
* Deprecate `!tagged` tag, use `!tagged_iterator` instead
88
* Add a `ContainerBuilder::registerChild()` shortcut method for registering child definitions
9+
* Enable non-empty parameters with `ParameterBag::nonEmpty()` and `ContainerBuilder::nonEmptyParameter()` methods
910

1011
7.1
1112
---

src/Symfony/Component/DependencyInjection/Container.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ public function compile(): void
8686

8787
$this->parameterBag = new FrozenParameterBag(
8888
$this->parameterBag->all(),
89-
$this->parameterBag instanceof ParameterBag ? $this->parameterBag->allDeprecated() : []
89+
$this->parameterBag instanceof ParameterBag ? $this->parameterBag->allDeprecated() : [],
90+
$this->parameterBag instanceof ParameterBag ? $this->parameterBag->allNonEmpty() : [],
9091
);
9192

9293
$this->compiled = true;

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

+13
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,10 @@ public function merge(self $container): void
671671
foreach ($otherBag->allDeprecated() as $name => $deprecated) {
672672
$parameterBag->deprecate($name, ...$deprecated);
673673
}
674+
675+
foreach ($otherBag->allNonEmpty() as $name => $message) {
676+
$parameterBag->nonEmpty($name, $message);
677+
}
674678
}
675679

676680
if ($this->trackResources) {
@@ -764,6 +768,15 @@ public function deprecateParameter(string $name, string $package, string $versio
764768
$this->parameterBag->deprecate($name, $package, $version, $message);
765769
}
766770

771+
public function nonEmptyParameter(string $name, string $message): void
772+
{
773+
if (!$this->parameterBag instanceof ParameterBag) {
774+
throw new BadMethodCallException(\sprintf('The parameter bag must be an instance of "%s" to call "%s()".', ParameterBag::class, __METHOD__));
775+
}
776+
777+
$this->parameterBag->nonEmpty($name, $message);
778+
}
779+
767780
/**
768781
* Compiles the container.
769782
*

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

+45-8
Original file line numberDiff line numberDiff line change
@@ -1258,13 +1258,17 @@ class $class extends $baseClass
12581258
{
12591259
private const DEPRECATED_PARAMETERS = [];
12601260
1261+
private const NONEMPTY_PARAMETERS = [];
1262+
12611263
protected \$parameters = [];
12621264
12631265
public function __construct()
12641266
{
12651267
12661268
EOF;
12671269
$code = str_replace(" private const DEPRECATED_PARAMETERS = [];\n\n", $this->addDeprecatedParameters(), $code);
1270+
$code = str_replace(" private const NONEMPTY_PARAMETERS = [];\n\n", $this->addNonEmptyParameters(), $code);
1271+
12681272
if ($this->asFiles) {
12691273
$code = str_replace('__construct()', '__construct(private array $buildParameters = [], protected string $containerDir = __DIR__)', $code);
12701274

@@ -1429,6 +1433,24 @@ private function addDeprecatedParameters(): string
14291433
return " private const DEPRECATED_PARAMETERS = [\n{$code} ];\n\n";
14301434
}
14311435

1436+
private function addNonEmptyParameters(): string
1437+
{
1438+
if (!($bag = $this->container->getParameterBag()) instanceof ParameterBag) {
1439+
return '';
1440+
}
1441+
1442+
if (!$nonEmpty = $bag->allNonEmpty()) {
1443+
return '';
1444+
}
1445+
$code = '';
1446+
ksort($nonEmpty);
1447+
foreach ($nonEmpty as $param => $message) {
1448+
$code .= ' '.$this->doExport($param).' => '.$this->doExport($message).",\n";
1449+
}
1450+
1451+
return " private const NONEMPTY_PARAMETERS = [\n{$code} ];\n\n";
1452+
}
1453+
14321454
private function addMethodMap(): string
14331455
{
14341456
$code = '';
@@ -1563,14 +1585,16 @@ private function addInlineRequires(bool $hasProxyClasses): string
15631585

15641586
private function addDefaultParametersMethod(): string
15651587
{
1566-
if (!$this->container->getParameterBag()->all()) {
1588+
$bag = $this->container->getParameterBag();
1589+
1590+
if (!$bag->all() && (!$bag instanceof ParameterBag || !$bag->allNonEmpty())) {
15671591
return '';
15681592
}
15691593

15701594
$php = [];
15711595
$dynamicPhp = [];
15721596

1573-
foreach ($this->container->getParameterBag()->all() as $key => $value) {
1597+
foreach ($bag->all() as $key => $value) {
15741598
if ($key !== $resolvedKey = $this->container->resolveEnvPlaceholders($key)) {
15751599
throw new InvalidArgumentException(\sprintf('Parameter name cannot use env parameters: "%s".', $resolvedKey));
15761600
}
@@ -1600,13 +1624,20 @@ public function getParameter(string $name): array|bool|string|int|float|\UnitEnu
16001624
}
16011625
16021626
if (!(isset($this->parameters[$name]) || isset($this->loadedDynamicParameters[$name]) || \array_key_exists($name, $this->parameters))) {
1603-
throw new ParameterNotFoundException($name);
1627+
throw new ParameterNotFoundException($name, extraMessage: self::NONEMPTY_PARAMETERS[$name] ?? null);
16041628
}
1629+
16051630
if (isset($this->loadedDynamicParameters[$name])) {
1606-
return $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name);
1631+
$value = $this->loadedDynamicParameters[$name] ? $this->dynamicParameters[$name] : $this->getDynamicParameter($name);
1632+
} else {
1633+
$value = $this->parameters[$name];
1634+
}
1635+
1636+
if (isset(self::NONEMPTY_PARAMETERS[$name]) && empty($value)) {
1637+
throw new \Symfony\Component\DependencyInjection\Exception\EmptyParameterValueException(self::NONEMPTY_PARAMETERS[$name]);
16071638
}
16081639
1609-
return $this->parameters[$name];
1640+
return $value;
16101641
}
16111642
16121643
public function hasParameter(string $name): bool
@@ -1633,7 +1664,7 @@ public function getParameterBag(): ParameterBagInterface
16331664
foreach ($this->buildParameters as $name => $value) {
16341665
$parameters[$name] = $value;
16351666
}
1636-
$this->parameterBag = new FrozenParameterBag($parameters, self::DEPRECATED_PARAMETERS);
1667+
$this->parameterBag = new FrozenParameterBag($parameters, self::DEPRECATED_PARAMETERS, self::NONEMPTY_PARAMETERS);
16371668
}
16381669
16391670
return $this->parameterBag;
@@ -1645,9 +1676,15 @@ public function getParameterBag(): ParameterBagInterface
16451676
$code = preg_replace('/^.*buildParameters.*\n.*\n.*\n\n?/m', '', $code);
16461677
}
16471678

1648-
if (!($bag = $this->container->getParameterBag()) instanceof ParameterBag || !$bag->allDeprecated()) {
1679+
if (!$bag instanceof ParameterBag || !$bag->allDeprecated()) {
16491680
$code = preg_replace("/\n.*DEPRECATED_PARAMETERS.*\n.*\n.*\n/m", '', $code, 1);
1650-
$code = str_replace(', self::DEPRECATED_PARAMETERS', '', $code);
1681+
$code = str_replace(', self::DEPRECATED_PARAMETERS', ', []', $code);
1682+
}
1683+
1684+
if (!$bag instanceof ParameterBag || !$bag->allNonEmpty()) {
1685+
$code = str_replace(', extraMessage: self::NONEMPTY_PARAMETERS[$name] ?? null', '', $code);
1686+
$code = str_replace(', self::NONEMPTY_PARAMETERS', '', $code);
1687+
$code = preg_replace("/\n.*NONEMPTY_PARAMETERS.*\n.*\n.*\n/m", '', $code, 1);
16511688
}
16521689

16531690
if ($dynamicPhp) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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\Exception;
13+
14+
use Psr\Container\NotFoundExceptionInterface;
15+
16+
/**
17+
* This exception is thrown when an existent parameter with an empty value is used.
18+
*
19+
* @author Yonel Ceruto <open@yceruto.dev>
20+
*/
21+
class EmptyParameterValueException extends InvalidArgumentException implements NotFoundExceptionInterface
22+
{
23+
}

src/Symfony/Component/DependencyInjection/Exception/ParameterNotFoundException.php

+18-1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public function __construct(
3636
private array $alternatives = [],
3737
private ?string $nonNestedAlternative = null,
3838
private ?string $sourceExtensionName = null,
39+
private ?string $extraMessage = null,
3940
) {
4041
parent::__construct('', 0, $previous);
4142

@@ -57,7 +58,7 @@ public function updateRepr(): void
5758
}
5859

5960
if ($this->alternatives) {
60-
if (1 == \count($this->alternatives)) {
61+
if (1 === \count($this->alternatives)) {
6162
$this->message .= ' Did you mean this: "';
6263
} else {
6364
$this->message .= ' Did you mean one of these: "';
@@ -66,6 +67,10 @@ public function updateRepr(): void
6667
} elseif (null !== $this->nonNestedAlternative) {
6768
$this->message .= ' You cannot access nested array items, do you want to inject "'.$this->nonNestedAlternative.'" instead?';
6869
}
70+
71+
if ($this->extraMessage) {
72+
$this->message .= ' '.$this->extraMessage;
73+
}
6974
}
7075

7176
public function getKey(): string
@@ -103,4 +108,16 @@ public function setSourceExtensionName(?string $sourceExtensionName): void
103108

104109
$this->updateRepr();
105110
}
111+
112+
public function getExtraMessage(): ?string
113+
{
114+
return $this->extraMessage;
115+
}
116+
117+
public function setExtraMessage(?string $extraMessage): void
118+
{
119+
$this->extraMessage = $extraMessage;
120+
121+
$this->updateRepr();
122+
}
106123
}

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

+6
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class FrozenParameterBag extends ParameterBag
2929
public function __construct(
3030
array $parameters = [],
3131
protected array $deprecatedParameters = [],
32+
protected array $nonEmptyParameters = [],
3233
) {
3334
$this->parameters = $parameters;
3435
$this->resolved = true;
@@ -54,6 +55,11 @@ public function deprecate(string $name, string $package, string $version, string
5455
throw new LogicException('Impossible to call deprecate() on a frozen ParameterBag.');
5556
}
5657

58+
public function nonEmpty(string $name, string $message = 'A non-empty parameter "%s" is required.'): never
59+
{
60+
throw new LogicException('Impossible to call nonEmpty() on a frozen ParameterBag.');
61+
}
62+
5763
public function remove(string $name): never
5864
{
5965
throw new LogicException('Impossible to call remove() on a frozen ParameterBag.');

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

+21-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection\ParameterBag;
1313

14+
use Symfony\Component\DependencyInjection\Exception\EmptyParameterValueException;
1415
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
1516
use Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException;
1617
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
@@ -26,6 +27,7 @@ class ParameterBag implements ParameterBagInterface
2627
protected array $parameters = [];
2728
protected bool $resolved = false;
2829
protected array $deprecatedParameters = [];
30+
protected array $nonEmptyParameters = [];
2931

3032
public function __construct(array $parameters = [])
3133
{
@@ -54,13 +56,22 @@ public function allDeprecated(): array
5456
return $this->deprecatedParameters;
5557
}
5658

59+
public function allNonEmpty(): array
60+
{
61+
return $this->nonEmptyParameters;
62+
}
63+
5764
public function get(string $name): array|bool|string|int|float|\UnitEnum|null
5865
{
5966
if (!\array_key_exists($name, $this->parameters)) {
6067
if (!$name) {
6168
throw new ParameterNotFoundException($name);
6269
}
6370

71+
if (\array_key_exists($name, $this->nonEmptyParameters)) {
72+
throw new ParameterNotFoundException($name, extraMessage: $this->nonEmptyParameters[$name]);
73+
}
74+
6475
$alternatives = [];
6576
foreach ($this->parameters as $key => $parameterValue) {
6677
$lev = levenshtein($name, $key);
@@ -92,6 +103,10 @@ public function get(string $name): array|bool|string|int|float|\UnitEnum|null
92103
trigger_deprecation(...$this->deprecatedParameters[$name]);
93104
}
94105

106+
if (\array_key_exists($name, $this->nonEmptyParameters) && empty($this->parameters[$name])) {
107+
throw new EmptyParameterValueException($this->nonEmptyParameters[$name]);
108+
}
109+
95110
return $this->parameters[$name];
96111
}
97112

@@ -118,14 +133,19 @@ public function deprecate(string $name, string $package, string $version, string
118133
$this->deprecatedParameters[$name] = [$package, $version, $message, $name];
119134
}
120135

136+
public function nonEmpty(string $name, string $message): void
137+
{
138+
$this->nonEmptyParameters[$name] = $message;
139+
}
140+
121141
public function has(string $name): bool
122142
{
123143
return \array_key_exists($name, $this->parameters);
124144
}
125145

126146
public function remove(string $name): void
127147
{
128-
unset($this->parameters[$name], $this->deprecatedParameters[$name]);
148+
unset($this->parameters[$name], $this->deprecatedParameters[$name], $this->nonEmptyParameters[$name]);
129149
}
130150

131151
public function resolve(): void

src/Symfony/Component/DependencyInjection/Tests/Dumper/PhpDumperTest.php

+21
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,27 @@ public function testDeprecatedParametersAsFiles()
511511
$this->assertStringMatchesFormatFile(self::$fixturesPath.'/php/services_deprecated_parameters_as_files.txt', $dump);
512512
}
513513

514+
public function testNonEmptyParameters()
515+
{
516+
$container = include self::$fixturesPath.'/containers/container_nonempty_parameters.php';
517+
$container->compile();
518+
519+
$dumper = new PhpDumper($container);
520+
521+
$this->assertStringEqualsFile(self::$fixturesPath.'/php/services_nonempty_parameters.php', $dumper->dump());
522+
}
523+
524+
public function testNonEmptyParametersAsFiles()
525+
{
526+
$container = include self::$fixturesPath.'/containers/container_nonempty_parameters.php';
527+
$container->compile();
528+
529+
$dumper = new PhpDumper($container);
530+
$dump = print_r($dumper->dump(['as_files' => true, 'file' => __DIR__, 'inline_factories_parameter' => false, 'inline_class_loader_parameter' => false]), true);
531+
532+
$this->assertStringMatchesFormatFile(self::$fixturesPath.'/php/services_nonempty_parameters_as_files.txt', $dump);
533+
}
534+
514535
public function testEnvInId()
515536
{
516537
$container = include self::$fixturesPath.'/containers/container_env_in_id.php';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
use Symfony\Component\DependencyInjection\ContainerBuilder;
4+
use Symfony\Component\DependencyInjection\Parameter;
5+
6+
$container = new ContainerBuilder();
7+
$container->nonEmptyParameter('bar', 'Did you forget to configure the "foo.bar" option?');
8+
$container->register('foo', 'stdClass')
9+
->setArguments([new Parameter('bar')])
10+
->setPublic(true)
11+
;
12+
13+
return $container;

0 commit comments

Comments
 (0)