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

Skip to content

Commit 59b79d3

Browse files
committed
[Config] Builder: Remove typehints and allow for EnvConfigurator
1 parent 84a514c commit 59b79d3

File tree

11 files changed

+149
-30
lines changed

11 files changed

+149
-30
lines changed

src/Symfony/Component/Config/Builder/ClassBuilder.php

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class ClassBuilder
3232
/** @var Method[] */
3333
private $methods = [];
3434
private $require = [];
35+
private $use = [];
3536
private $implements = [];
3637

3738
public function __construct(string $namespace, string $name)
@@ -66,6 +67,10 @@ public function build(): string
6667
}
6768
$require .= sprintf('require_once __DIR__.\DIRECTORY_SEPARATOR.\'%s\';', implode('\'.\DIRECTORY_SEPARATOR.\'', $path))."\n";
6869
}
70+
$use = '';
71+
foreach (array_keys($this->use) as $statement) {
72+
$use .= sprintf('use %s;', $statement)."\n";
73+
}
6974

7075
$implements = [] === $this->implements ? '' : 'implements '.implode(', ', $this->implements);
7176
$body = '';
@@ -84,6 +89,7 @@ public function build(): string
8489
namespace NAMESPACE;
8590
8691
REQUIRE
92+
USE
8793
8894
/**
8995
* This class is automatically generated to help creating config.
@@ -94,17 +100,22 @@ class CLASS IMPLEMENTS
94100
{
95101
BODY
96102
}
97-
', ['NAMESPACE' => $this->namespace, 'REQUIRE' => $require, 'CLASS' => $this->getName(), 'IMPLEMENTS' => $implements, 'BODY' => $body]);
103+
', ['NAMESPACE' => $this->namespace, 'REQUIRE' => $require, 'USE' => $use, 'CLASS' => $this->getName(), 'IMPLEMENTS' => $implements, 'BODY' => $body]);
98104

99105
return $content;
100106
}
101107

102-
public function addRequire(self $class)
108+
public function addRequire(self $class): void
103109
{
104110
$this->require[] = $class;
105111
}
106112

107-
public function addImplements(string $interface)
113+
public function addUse(string $class): void
114+
{
115+
$this->use[$class] = true;
116+
}
117+
118+
public function addImplements(string $interface): void
108119
{
109120
$this->implements[] = '\\'.ltrim($interface, '\\');
110121
}
@@ -148,7 +159,7 @@ public function getNamespace(): string
148159
return $this->namespace;
149160
}
150161

151-
public function getFqcn()
162+
public function getFqcn(): string
152163
{
153164
return '\\'.$this->namespace.'\\'.$this->name;
154165
}

src/Symfony/Component/Config/Builder/ConfigBuilderGenerator.php

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515
use Symfony\Component\Config\Definition\BooleanNode;
1616
use Symfony\Component\Config\Definition\ConfigurationInterface;
1717
use Symfony\Component\Config\Definition\EnumNode;
18+
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
1819
use Symfony\Component\Config\Definition\FloatNode;
1920
use Symfony\Component\Config\Definition\IntegerNode;
2021
use Symfony\Component\Config\Definition\NodeInterface;
2122
use Symfony\Component\Config\Definition\PrototypedArrayNode;
2223
use Symfony\Component\Config\Definition\ScalarNode;
2324
use Symfony\Component\Config\Definition\VariableNode;
25+
use Symfony\Component\Config\Loader\ParamConfigurator;
2426

2527
/**
2628
* Generate ConfigBuilders to help create valid config.
@@ -83,7 +85,7 @@ private function getFullPath(ClassBuilder $class): string
8385
return $directory.\DIRECTORY_SEPARATOR.$class->getFilename();
8486
}
8587

86-
private function writeClasses()
88+
private function writeClasses(): void
8789
{
8890
foreach ($this->classes as $class) {
8991
$this->buildConstructor($class);
@@ -95,7 +97,7 @@ private function writeClasses()
9597
$this->classes = [];
9698
}
9799

98-
private function buildNode(NodeInterface $node, ClassBuilder $class, string $namespace)
100+
private function buildNode(NodeInterface $node, ClassBuilder $class, string $namespace): void
99101
{
100102
if (!$node instanceof ArrayNode) {
101103
throw new \LogicException('The node was expected to be an ArrayNode. This Configuration includes an edge case not supported yet.');
@@ -121,7 +123,7 @@ private function buildNode(NodeInterface $node, ClassBuilder $class, string $nam
121123
}
122124
}
123125

124-
private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $namespace)
126+
private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $namespace): void
125127
{
126128
$childClass = new ClassBuilder($namespace, $node->getName());
127129
$class->addRequire($childClass);
@@ -134,20 +136,22 @@ public function NAME(array $value = []): CLASS
134136
if (null === $this->PROPERTY) {
135137
$this->PROPERTY = new CLASS($value);
136138
} elseif ([] !== $value) {
137-
throw new \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException(sprintf(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'));
139+
throw new InvalidConfigurationException(sprintf(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'));
138140
}
139141
140142
return $this->PROPERTY;
141143
}';
144+
$class->addUse(InvalidConfigurationException::class);
142145
$class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn()]);
143146

144147
$this->buildNode($node, $childClass, $this->getSubNamespace($childClass));
145148
}
146149

147-
private function handleVariableNode(VariableNode $node, ClassBuilder $class)
150+
private function handleVariableNode(VariableNode $node, ClassBuilder $class): void
148151
{
149152
$comment = $this->getComment($node);
150153
$property = $class->addProperty($node->getName());
154+
$class->addUse(ParamConfigurator::class);
151155

152156
$body = '
153157
/**
@@ -162,23 +166,24 @@ public function NAME($valueDEFAULT): self
162166
$class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'COMMENT' => $comment, 'DEFAULT' => $node->hasDefaultValue() ? ' = '.var_export($node->getDefaultValue(), true) : '']);
163167
}
164168

165-
private function handlePrototypedArrayNode(PrototypedArrayNode $node, ClassBuilder $class, string $namespace)
169+
private function handlePrototypedArrayNode(PrototypedArrayNode $node, ClassBuilder $class, string $namespace): void
166170
{
167171
$name = $this->getSingularName($node);
168172
$prototype = $node->getPrototype();
169173
$methodName = $name;
170174

171175
$parameterType = $this->getParameterType($prototype);
172176
if (null !== $parameterType || $prototype instanceof ScalarNode) {
177+
$class->addUse(ParamConfigurator::class);
173178
$property = $class->addProperty($node->getName());
174179
if (null === $key = $node->getKeyAttribute()) {
175180
// This is an array of values; don't use singular name
176181
$body = '
177182
/**
178-
* @param list<TYPE> $value
183+
* @param ParamConfigurator|list<TYPE|ParamConfigurator> $value
179184
* @return $this
180185
*/
181-
public function NAME(array $value): self
186+
public function NAME($value): self
182187
{
183188
$this->PROPERTY = $value;
184189
@@ -189,16 +194,17 @@ public function NAME(array $value): self
189194
} else {
190195
$body = '
191196
/**
197+
* @param ParamConfigurator|TYPE $value
192198
* @return $this
193199
*/
194-
public function NAME(string $VAR, TYPE$VALUE): self
200+
public function NAME(string $VAR, $VALUE): self
195201
{
196202
$this->PROPERTY[$VAR] = $VALUE;
197203
198204
return $this;
199205
}';
200206

201-
$class->addMethod($methodName, $body, ['PROPERTY' => $property->getName(), 'TYPE' => '' === $parameterType ? '' : $parameterType.' ', 'VAR' => '' === $key ? 'key' : $key, 'VALUE' => 'value' === $key ? 'data' : 'value']);
207+
$class->addMethod($methodName, $body, ['PROPERTY' => $property->getName(), 'TYPE' => '' === $parameterType ? 'mixed' : $parameterType, 'VAR' => '' === $key ? 'key' : $key, 'VALUE' => 'value' === $key ? 'data' : 'value']);
202208
}
203209

204210
return;
@@ -227,31 +233,33 @@ public function NAME(string $VAR, array $VALUE = []): CLASS
227233
return $this->PROPERTY[$VAR];
228234
}
229235
230-
throw new \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException(sprintf(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'));
236+
throw new InvalidConfigurationException(sprintf(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\'));
231237
}';
238+
$class->addUse(InvalidConfigurationException::class);
232239
$class->addMethod($methodName, $body, ['PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn(), 'VAR' => '' === $key ? 'key' : $key, 'VALUE' => 'value' === $key ? 'data' : 'value']);
233240
}
234241

235242
$this->buildNode($prototype, $childClass, $namespace.'\\'.$childClass->getName());
236243
}
237244

238-
private function handleScalarNode(ScalarNode $node, ClassBuilder $class)
245+
private function handleScalarNode(ScalarNode $node, ClassBuilder $class): void
239246
{
240247
$comment = $this->getComment($node);
241248
$property = $class->addProperty($node->getName());
249+
$class->addUse(ParamConfigurator::class);
242250

243251
$body = '
244252
/**
245253
COMMENT * @return $this
246254
*/
247-
public function NAME(TYPE$value): self
255+
public function NAME($value): self
248256
{
249257
$this->PROPERTY = $value;
250258
251259
return $this;
252260
}';
253-
$parameterType = $this->getParameterType($node) ?? '';
254-
$class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'TYPE' => '' === $parameterType ? '' : $parameterType.' ', 'COMMENT' => $comment]);
261+
262+
$class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'COMMENT' => $comment]);
255263
}
256264

257265
private function getParameterType(NodeInterface $node): ?string
@@ -301,9 +309,15 @@ private function getComment(VariableNode $node): string
301309
}
302310

303311
if ($node instanceof EnumNode) {
304-
$comment .= sprintf(' * @param %s $value', implode('|', array_map(function ($a) {
312+
$comment .= sprintf(' * @param ParamConfigurator|%s $value', implode('|', array_map(function ($a) {
305313
return var_export($a, true);
306314
}, $node->getValues()))).\PHP_EOL;
315+
} else {
316+
$parameterType = $this->getParameterType($node);
317+
if (null === $parameterType || '' === $parameterType) {
318+
$parameterType = 'mixed';
319+
}
320+
$comment .= ' * @param ParamConfigurator|'.$parameterType.' $value'.\PHP_EOL;
307321
}
308322

309323
if ($node->isDeprecated()) {
@@ -387,9 +401,10 @@ private function buildConstructor(ClassBuilder $class): void
387401

388402
$body .= '
389403
if ($value !== []) {
390-
throw new \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException(sprintf(\'The following keys are not supported by "%s": \', __CLASS__) . implode(\', \', array_keys($value)));
404+
throw new InvalidConfigurationException(sprintf(\'The following keys are not supported by "%s": \', __CLASS__) . implode(\', \', array_keys($value)));
391405
}';
392406

407+
$class->addUse(InvalidConfigurationException::class);
393408
$class->addMethod('__construct', '
394409
public function __construct(array $value = [])
395410
{
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Config\Loader;
13+
14+
/**
15+
* Placeholder for a parameter.
16+
*
17+
* @author Tobias Nyholm <[email protected]>
18+
*/
19+
class ParamConfigurator
20+
{
21+
private $name;
22+
23+
public function __construct(string $name)
24+
{
25+
$this->name = $name;
26+
}
27+
28+
public function __toString(): string
29+
{
30+
return '%'.$this->name.'%';
31+
}
32+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
4+
use Symfony\Config\PlaceholdersConfig;
5+
6+
return static function (PlaceholdersConfig $config) {
7+
$config->enabled(env('FOO_ENABLED')->bool());
8+
$config->favoriteFloat(param('eulers_number'));
9+
$config->goodIntegers(env('MY_INTEGERS')->json());
10+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
return [
4+
'enabled' => '%env(bool:FOO_ENABLED)%',
5+
'favorite_float' => '%eulers_number%',
6+
'good_integers' => '%env(json:MY_INTEGERS)%',
7+
];
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Symfony\Component\Config\Tests\Builder\Fixtures;
4+
5+
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
6+
use Symfony\Component\Config\Definition\ConfigurationInterface;
7+
8+
class Placeholders implements ConfigurationInterface
9+
{
10+
public function getConfigTreeBuilder(): TreeBuilder
11+
{
12+
$tb = new TreeBuilder('placeholders');
13+
$rootNode = $tb->getRootNode();
14+
$rootNode
15+
->children()
16+
->booleanNode('enabled')->defaultFalse()->end()
17+
->floatNode('favorite_float')->end()
18+
->arrayNode('good_integers')
19+
->integerPrototype()->end()
20+
->end()
21+
->end()
22+
;
23+
24+
return $tb;
25+
}
26+
}

src/Symfony/Component/Config/Tests/Builder/GeneratedConfigTest.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
1010
use Symfony\Component\Config\Tests\Builder\Fixtures\AddToList;
1111
use Symfony\Component\Config\Tests\Builder\Fixtures\NodeInitialValues;
12+
use Symfony\Component\DependencyInjection\Loader\Configurator\AbstractConfigurator;
13+
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
1214
use Symfony\Config\AddToListConfig;
1315

1416
/**
@@ -30,6 +32,14 @@ public function fixtureNames()
3032
foreach ($array as $name => $alias) {
3133
yield $name => [$name, $alias];
3234
}
35+
36+
/*
37+
* Force load ContainerConfigurator to make env(), param() etc available
38+
* and also check if symfony/dependency-injection is installed
39+
*/
40+
if (class_exists(ContainerConfigurator::class)) {
41+
yield 'Placeholders' => ['Placeholders', 'placeholders'];
42+
}
3343
}
3444

3545
/**
@@ -45,7 +55,11 @@ public function testConfig(string $name, string $alias)
4555

4656
$this->assertInstanceOf(ConfigBuilderInterface::class, $configBuilder);
4757
$this->assertSame($alias, $configBuilder->getExtensionAlias());
48-
$this->assertSame($expectedOutput, $configBuilder->toArray());
58+
$output = $configBuilder->toArray();
59+
if (class_exists(AbstractConfigurator::class)) {
60+
$output = AbstractConfigurator::processValue($output);
61+
}
62+
$this->assertSame($expectedOutput, $output);
4963
}
5064

5165
/**

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
1313

14+
use Symfony\Component\Config\Loader\ParamConfigurator;
1415
use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
1516
use Symfony\Component\DependencyInjection\Argument\ArgumentInterface;
1617
use Symfony\Component\DependencyInjection\Definition;
@@ -83,7 +84,7 @@ public static function processValue($value, $allowServices = false)
8384
return $def;
8485
}
8586

86-
if ($value instanceof EnvConfigurator) {
87+
if ($value instanceof ParamConfigurator) {
8788
return (string) $value;
8889
}
8990

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
1313

14+
use Symfony\Component\Config\Loader\ParamConfigurator;
1415
use Symfony\Component\DependencyInjection\Argument\AbstractArgument;
1516
use Symfony\Component\DependencyInjection\Argument\IteratorArgument;
1617
use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument;
@@ -96,9 +97,9 @@ final public function withPath(string $path): self
9697
/**
9798
* Creates a parameter.
9899
*/
99-
function param(string $name): string
100+
function param(string $name): ParamConfigurator
100101
{
101-
return '%'.$name.'%';
102+
return new ParamConfigurator($name);
102103
}
103104

104105
/**

0 commit comments

Comments
 (0)