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

Skip to content

Commit a4a2962

Browse files
committed
Allow scalar configuration in PHP Configuration
1 parent 432c2ef commit a4a2962

File tree

8 files changed

+116
-59
lines changed

8 files changed

+116
-59
lines changed

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

Lines changed: 53 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\Config\Definition\ArrayNode;
1515
use Symfony\Component\Config\Definition\BaseNode;
1616
use Symfony\Component\Config\Definition\BooleanNode;
17+
use Symfony\Component\Config\Definition\Builder\ExprBuilder;
1718
use Symfony\Component\Config\Definition\ConfigurationInterface;
1819
use Symfony\Component\Config\Definition\EnumNode;
1920
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
@@ -141,8 +142,9 @@ private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $n
141142
$node->getName(),
142143
$this->getType($childClass->getFqcn(), $hasNormalizationClosures)
143144
);
145+
$nodeTypes = $this->getParameterTypes($node);
144146
$body = $hasNormalizationClosures ? '
145-
COMMENTpublic function NAME(mixed $value = []): CLASS|static
147+
COMMENTpublic function NAME(PARAM_TYPE $value = []): CLASS|static
146148
{
147149
if (!\is_array($value)) {
148150
$this->_usedProperties[\'PROPERTY\'] = true;
@@ -172,7 +174,12 @@ private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $n
172174
return $this->PROPERTY;
173175
}';
174176
$class->addUse(InvalidConfigurationException::class);
175-
$class->addMethod($node->getName(), $body, ['COMMENT' => $comment, 'PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn()]);
177+
$class->addMethod($node->getName(), $body, [
178+
'COMMENT' => $comment,
179+
'PROPERTY' => $property->getName(),
180+
'CLASS' => $childClass->getFqcn(),
181+
'PARAM_TYPE' => \in_array('mixed', $nodeTypes, true) ? 'mixed' : implode('|', $nodeTypes),
182+
]);
176183

177184
$this->buildNode($node, $childClass, $this->getSubNamespace($childClass));
178185
}
@@ -209,19 +216,21 @@ private function handlePrototypedArrayNode(PrototypedArrayNode $node, ClassBuild
209216
$methodName = $name;
210217
$hasNormalizationClosures = $this->hasNormalizationClosures($node) || $this->hasNormalizationClosures($prototype);
211218

212-
$parameterType = $this->getParameterType($prototype);
213-
if (null !== $parameterType || $prototype instanceof ScalarNode) {
219+
$nodeParameterTypes = $this->getParameterTypes($node);
220+
$prototypeParameterTypes = $this->getParameterTypes($prototype);
221+
if (!($prototype instanceof ArrayNode && (!$prototype instanceof PrototypedArrayNode || !$prototype->getPrototype() instanceof ScalarNode))) {
214222
$class->addUse(ParamConfigurator::class);
215223
$property = $class->addProperty($node->getName());
216224
if (null === $key = $node->getKeyAttribute()) {
217225
// This is an array of values; don't use singular name
226+
$nodeTypesWithoutArray = array_filter($nodeParameterTypes, static fn ($type) => 'array' !== $type);
218227
$body = '
219228
/**
220-
* @param PHPDOC_TYPE $value
229+
* @param ParamConfigurator|list<ParamConfigurator|PROTOTYPE_TYPE>EXTRA_TYPE $value
221230
*
222231
* @return $this
223232
*/
224-
public function NAME(TYPE $value): static
233+
public function NAME(PARAM_TYPE $value): static
225234
{
226235
$this->_usedProperties[\'PROPERTY\'] = true;
227236
$this->PROPERTY = $value;
@@ -231,8 +240,9 @@ public function NAME(TYPE $value): static
231240

232241
$class->addMethod($node->getName(), $body, [
233242
'PROPERTY' => $property->getName(),
234-
'TYPE' => $hasNormalizationClosures ? 'mixed' : 'ParamConfigurator|array',
235-
'PHPDOC_TYPE' => $hasNormalizationClosures ? 'mixed' : sprintf('ParamConfigurator|list<ParamConfigurator|%s>', '' === $parameterType ? 'mixed' : $parameterType),
243+
'PROTOTYPE_TYPE' => implode('|', $prototypeParameterTypes),
244+
'EXTRA_TYPE' => $nodeTypesWithoutArray ? '|'.implode('|', $nodeTypesWithoutArray) : '',
245+
'PARAM_TYPE' => \in_array('mixed', $nodeParameterTypes, true) ? 'mixed' : 'ParamConfigurator|'.implode('|', $nodeParameterTypes),
236246
]);
237247
} else {
238248
$body = '
@@ -249,7 +259,7 @@ public function NAME(string $VAR, TYPE $VALUE): static
249259

250260
$class->addMethod($methodName, $body, [
251261
'PROPERTY' => $property->getName(),
252-
'TYPE' => $hasNormalizationClosures || '' === $parameterType ? 'mixed' : 'ParamConfigurator|'.$parameterType,
262+
'TYPE' => \in_array('mixed', $prototypeParameterTypes, true) ? 'mixed' : 'ParamConfigurator|'.implode('|', $prototypeParameterTypes),
253263
'VAR' => '' === $key ? 'key' : $key,
254264
'VALUE' => 'value' === $key ? 'data' : 'value',
255265
]);
@@ -282,7 +292,7 @@ public function NAME(string $VAR, TYPE $VALUE): static
282292

283293
if (null === $key = $node->getKeyAttribute()) {
284294
$body = $hasNormalizationClosures ? '
285-
COMMENTpublic function NAME(mixed $value = []): CLASS|static
295+
COMMENTpublic function NAME(PARAM_TYPE $value = []): CLASS|static
286296
{
287297
$this->_usedProperties[\'PROPERTY\'] = true;
288298
if (!\is_array($value)) {
@@ -299,10 +309,15 @@ public function NAME(string $VAR, TYPE $VALUE): static
299309
300310
return $this->PROPERTY[] = new CLASS($value);
301311
}';
302-
$class->addMethod($methodName, $body, ['COMMENT' => $comment, 'PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn()]);
312+
$class->addMethod($methodName, $body, [
313+
'COMMENT' => $comment,
314+
'PROPERTY' => $property->getName(),
315+
'CLASS' => $childClass->getFqcn(),
316+
'PARAM_TYPE' => \in_array('mixed', $nodeParameterTypes, true) ? 'mixed' : implode('|', $nodeParameterTypes),
317+
]);
303318
} else {
304319
$body = $hasNormalizationClosures ? '
305-
COMMENTpublic function NAME(string $VAR, mixed $VALUE = []): CLASS|static
320+
COMMENTpublic function NAME(string $VAR, PARAM_TYPE $VALUE = []): CLASS|static
306321
{
307322
if (!\is_array($VALUE)) {
308323
$this->_usedProperties[\'PROPERTY\'] = true;
@@ -337,6 +352,7 @@ public function NAME(string $VAR, TYPE $VALUE): static
337352
'CLASS' => $childClass->getFqcn(),
338353
'VAR' => '' === $key ? 'key' : $key,
339354
'VALUE' => 'value' === $key ? 'data' : 'value',
355+
'PARAM_TYPE' => \in_array('mixed', $prototypeParameterTypes, true) ? 'mixed' : implode('|', $prototypeParameterTypes),
340356
]);
341357
}
342358

@@ -364,35 +380,33 @@ public function NAME($value): static
364380
$class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'COMMENT' => $comment]);
365381
}
366382

367-
private function getParameterType(NodeInterface $node): ?string
383+
private function getParameterTypes(NodeInterface $node): array
368384
{
369-
if ($node instanceof BooleanNode) {
370-
return 'bool';
371-
}
372-
373-
if ($node instanceof IntegerNode) {
374-
return 'int';
375-
}
376-
377-
if ($node instanceof FloatNode) {
378-
return 'float';
379-
}
380-
381-
if ($node instanceof EnumNode) {
382-
return '';
383-
}
384-
385-
if ($node instanceof PrototypedArrayNode && $node->getPrototype() instanceof ScalarNode) {
386-
// This is just an array of variables
387-
return 'array';
385+
$paramTypes = [];
386+
if ($node instanceof BaseNode) {
387+
$types = $node->getNormalizedTypes();
388+
if (\in_array(ExprBuilder::TYPE_ANY, $types, true)) {
389+
$paramTypes[] = 'mixed';
390+
}
391+
if (\in_array(ExprBuilder::TYPE_STRING, $types, true)) {
392+
$paramTypes[] = 'string';
393+
}
388394
}
389-
390-
if ($node instanceof VariableNode) {
391-
// mixed
392-
return '';
395+
if ($node instanceof BooleanNode) {
396+
$paramTypes[] = 'bool';
397+
} elseif ($node instanceof IntegerNode) {
398+
$paramTypes[] = 'int';
399+
} elseif ($node instanceof FloatNode) {
400+
$paramTypes[] = 'float';
401+
} elseif ($node instanceof EnumNode) {
402+
$paramTypes[] = 'mixed';
403+
} elseif ($node instanceof ArrayNode) {
404+
$paramTypes[] = 'array';
405+
} elseif ($node instanceof VariableNode) {
406+
$paramTypes[] = 'mixed';
393407
}
394408

395-
return null;
409+
return array_unique($paramTypes);
396410
}
397411

398412
private function getComment(BaseNode $node): string
@@ -416,11 +430,8 @@ private function getComment(BaseNode $node): string
416430
return var_export($a, true);
417431
}, $node->getValues())))."\n";
418432
} else {
419-
$parameterType = $this->getParameterType($node);
420-
if (null === $parameterType || '' === $parameterType) {
421-
$parameterType = 'mixed';
422-
}
423-
$comment .= ' * @param ParamConfigurator|'.$parameterType.' $value'."\n";
433+
$parameterTypes = $this->getParameterTypes($node);
434+
$comment .= ' * @param ParamConfigurator|'.implode('|', $parameterTypes).' $value'."\n";
424435
}
425436
} else {
426437
foreach ((array) ($node->getExample() ?? []) as $example) {

src/Symfony/Component/Config/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55
---
66

77
* Deprecate calling `NodeBuilder::setParent()` without any arguments
8+
* Add a more accurate typehint in generated PHP config
89

910
6.1
1011
---

src/Symfony/Component/Config/Definition/BaseNode.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ abstract class BaseNode implements NodeInterface
3232
protected $name;
3333
protected $parent;
3434
protected $normalizationClosures = [];
35+
protected $normalizedTypes = [];
3536
protected $finalValidationClosures = [];
3637
protected $allowOverwrite = true;
3738
protected $required = false;
@@ -212,6 +213,26 @@ public function setNormalizationClosures(array $closures)
212213
$this->normalizationClosures = $closures;
213214
}
214215

216+
/**
217+
* Sets the list of types supported by normalization.
218+
*
219+
* see ExprBuilder::TYPE_* constants.
220+
*/
221+
public function setNormalizedTypes(array $types)
222+
{
223+
$this->normalizedTypes = $types;
224+
}
225+
226+
/**
227+
* Gets the list of types supported by normalization.
228+
*
229+
* see ExprBuilder::TYPE_* constants.
230+
*/
231+
public function getNormalizedTypes(): array
232+
{
233+
return $this->normalizedTypes;
234+
}
235+
215236
/**
216237
* Sets the closures used for final validation.
217238
*

src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ protected function createNode(): NodeInterface
405405

406406
if (null !== $this->normalization) {
407407
$node->setNormalizationClosures($this->normalization->before);
408+
$node->setNormalizedTypes($this->normalization->declaredTypes);
408409
$node->setXmlRemappings($this->normalization->remappings);
409410
}
410411

src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,14 @@
2121
*/
2222
class ExprBuilder
2323
{
24+
public const TYPE_ANY = 'any';
25+
public const TYPE_STRING = 'string';
26+
public const TYPE_NULL = 'null';
27+
public const TYPE_ARRAY = 'array';
28+
2429
protected $node;
30+
31+
public $allowedTypes;
2532
public $ifPart;
2633
public $thenPart;
2734

@@ -37,7 +44,8 @@ public function __construct(NodeDefinition $node)
3744
*/
3845
public function always(\Closure $then = null): static
3946
{
40-
$this->ifPart = function () { return true; };
47+
$this->ifPart = static function () { return true; };
48+
$this->allowedTypes = self::TYPE_ANY;
4149

4250
if (null !== $then) {
4351
$this->thenPart = $then;
@@ -56,10 +64,11 @@ public function always(\Closure $then = null): static
5664
public function ifTrue(\Closure $closure = null): static
5765
{
5866
if (null === $closure) {
59-
$closure = function ($v) { return true === $v; };
67+
$closure = static function ($v) { return true === $v; };
6068
}
6169

6270
$this->ifPart = $closure;
71+
$this->allowedTypes = self::TYPE_ANY;
6372

6473
return $this;
6574
}
@@ -71,7 +80,8 @@ public function ifTrue(\Closure $closure = null): static
7180
*/
7281
public function ifString(): static
7382
{
74-
$this->ifPart = function ($v) { return \is_string($v); };
83+
$this->ifPart = static function ($v) { return \is_string($v); };
84+
$this->allowedTypes = self::TYPE_STRING;
7585

7686
return $this;
7787
}
@@ -83,7 +93,8 @@ public function ifString(): static
8393
*/
8494
public function ifNull(): static
8595
{
86-
$this->ifPart = function ($v) { return null === $v; };
96+
$this->ifPart = static function ($v) { return null === $v; };
97+
$this->allowedTypes = self::TYPE_NULL;
8798

8899
return $this;
89100
}
@@ -95,7 +106,8 @@ public function ifNull(): static
95106
*/
96107
public function ifEmpty(): static
97108
{
98-
$this->ifPart = function ($v) { return empty($v); };
109+
$this->ifPart = static function ($v) { return empty($v); };
110+
$this->allowedTypes = self::TYPE_ANY;
99111

100112
return $this;
101113
}
@@ -107,7 +119,8 @@ public function ifEmpty(): static
107119
*/
108120
public function ifArray(): static
109121
{
110-
$this->ifPart = function ($v) { return \is_array($v); };
122+
$this->ifPart = static function ($v) { return \is_array($v); };
123+
$this->allowedTypes = self::TYPE_ARRAY;
111124

112125
return $this;
113126
}
@@ -119,7 +132,8 @@ public function ifArray(): static
119132
*/
120133
public function ifInArray(array $array): static
121134
{
122-
$this->ifPart = function ($v) use ($array) { return \in_array($v, $array, true); };
135+
$this->ifPart = static function ($v) use ($array) { return \in_array($v, $array, true); };
136+
$this->allowedTypes = self::TYPE_ANY;
123137

124138
return $this;
125139
}
@@ -131,7 +145,8 @@ public function ifInArray(array $array): static
131145
*/
132146
public function ifNotInArray(array $array): static
133147
{
134-
$this->ifPart = function ($v) use ($array) { return !\in_array($v, $array, true); };
148+
$this->ifPart = static function ($v) use ($array) { return !\in_array($v, $array, true); };
149+
$this->allowedTypes = self::TYPE_ANY;
135150

136151
return $this;
137152
}
@@ -143,8 +158,9 @@ public function ifNotInArray(array $array): static
143158
*/
144159
public function castToArray(): static
145160
{
146-
$this->ifPart = function ($v) { return !\is_array($v); };
147-
$this->thenPart = function ($v) { return [$v]; };
161+
$this->ifPart = static function ($v) { return !\is_array($v); };
162+
$this->allowedTypes = self::TYPE_ANY;
163+
$this->thenPart = static function ($v) { return [$v]; };
148164

149165
return $this;
150166
}
@@ -168,7 +184,7 @@ public function then(\Closure $closure): static
168184
*/
169185
public function thenEmptyArray(): static
170186
{
171-
$this->thenPart = function () { return []; };
187+
$this->thenPart = static function () { return []; };
172188

173189
return $this;
174190
}
@@ -184,7 +200,7 @@ public function thenEmptyArray(): static
184200
*/
185201
public function thenInvalid(string $message): static
186202
{
187-
$this->thenPart = function ($v) use ($message) { throw new \InvalidArgumentException(sprintf($message, json_encode($v))); };
203+
$this->thenPart = static function ($v) use ($message) { throw new \InvalidArgumentException(sprintf($message, json_encode($v))); };
188204

189205
return $this;
190206
}
@@ -198,7 +214,7 @@ public function thenInvalid(string $message): static
198214
*/
199215
public function thenUnset(): static
200216
{
201-
$this->thenPart = function () { throw new UnsetKeyException('Unsetting key.'); };
217+
$this->thenPart = static function () { throw new UnsetKeyException('Unsetting key.'); };
202218

203219
return $this;
204220
}
@@ -231,7 +247,7 @@ public static function buildExpressions(array $expressions): array
231247
if ($expr instanceof self) {
232248
$if = $expr->ifPart;
233249
$then = $expr->thenPart;
234-
$expressions[$k] = function ($v) use ($if, $then) {
250+
$expressions[$k] = static function ($v) use ($if, $then) {
235251
return $if($v) ? $then($v) : $v;
236252
};
237253
}

src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,13 @@ public function getNode(bool $forceRootNode = false): NodeInterface
106106
}
107107

108108
if (null !== $this->normalization) {
109+
$allowedTypes = [];
110+
foreach ($this->normalization->before as $expr) {
111+
$allowedTypes[] = $expr->allowedTypes;
112+
}
113+
$allowedTypes = array_unique($allowedTypes);
109114
$this->normalization->before = ExprBuilder::buildExpressions($this->normalization->before);
115+
$this->normalization->declaredTypes = $allowedTypes;
110116
}
111117

112118
if (null !== $this->validation) {

0 commit comments

Comments
 (0)