Skip to content

Commit e3c72bb

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

File tree

7 files changed

+112
-59
lines changed

7 files changed

+112
-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/Definition/BaseNode.php

Lines changed: 19 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,24 @@ public function setNormalizationClosures(array $closures)
212213
$this->normalizationClosures = $closures;
213214
}
214215

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

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: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,13 @@
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+
public $allowedTypes;
2531
public $ifPart;
2632
public $thenPart;
2733

@@ -37,7 +43,8 @@ public function __construct(NodeDefinition $node)
3743
*/
3844
public function always(\Closure $then = null): static
3945
{
40-
$this->ifPart = function () { return true; };
46+
$this->ifPart = static function () { return true; };
47+
$this->allowedTypes = self::TYPE_ANY;
4148

4249
if (null !== $then) {
4350
$this->thenPart = $then;
@@ -56,10 +63,11 @@ public function always(\Closure $then = null): static
5663
public function ifTrue(\Closure $closure = null): static
5764
{
5865
if (null === $closure) {
59-
$closure = function ($v) { return true === $v; };
66+
$closure = static function ($v) { return true === $v; };
6067
}
6168

6269
$this->ifPart = $closure;
70+
$this->allowedTypes = self::TYPE_ANY;
6371

6472
return $this;
6573
}
@@ -71,7 +79,8 @@ public function ifTrue(\Closure $closure = null): static
7179
*/
7280
public function ifString(): static
7381
{
74-
$this->ifPart = function ($v) { return \is_string($v); };
82+
$this->ifPart = static function ($v) { return \is_string($v); };
83+
$this->allowedTypes = self::TYPE_STRING;
7584

7685
return $this;
7786
}
@@ -83,7 +92,8 @@ public function ifString(): static
8392
*/
8493
public function ifNull(): static
8594
{
86-
$this->ifPart = function ($v) { return null === $v; };
95+
$this->ifPart = static function ($v) { return null === $v; };
96+
$this->allowedTypes = self::TYPE_NULL;
8797

8898
return $this;
8999
}
@@ -95,7 +105,8 @@ public function ifNull(): static
95105
*/
96106
public function ifEmpty(): static
97107
{
98-
$this->ifPart = function ($v) { return empty($v); };
108+
$this->ifPart = static function ($v) { return empty($v); };
109+
$this->allowedTypes = self::TYPE_ANY;
99110

100111
return $this;
101112
}
@@ -107,7 +118,8 @@ public function ifEmpty(): static
107118
*/
108119
public function ifArray(): static
109120
{
110-
$this->ifPart = function ($v) { return \is_array($v); };
121+
$this->ifPart = static function ($v) { return \is_array($v); };
122+
$this->allowedTypes = self::TYPE_ARRAY;
111123

112124
return $this;
113125
}
@@ -119,7 +131,8 @@ public function ifArray(): static
119131
*/
120132
public function ifInArray(array $array): static
121133
{
122-
$this->ifPart = function ($v) use ($array) { return \in_array($v, $array, true); };
134+
$this->ifPart = static function ($v) use ($array) { return \in_array($v, $array, true); };
135+
$this->allowedTypes = self::TYPE_ANY;
123136

124137
return $this;
125138
}
@@ -131,7 +144,8 @@ public function ifInArray(array $array): static
131144
*/
132145
public function ifNotInArray(array $array): static
133146
{
134-
$this->ifPart = function ($v) use ($array) { return !\in_array($v, $array, true); };
147+
$this->ifPart = static function ($v) use ($array) { return !\in_array($v, $array, true); };
148+
$this->allowedTypes = self::TYPE_ANY;
135149

136150
return $this;
137151
}
@@ -143,8 +157,9 @@ public function ifNotInArray(array $array): static
143157
*/
144158
public function castToArray(): static
145159
{
146-
$this->ifPart = function ($v) { return !\is_array($v); };
147-
$this->thenPart = function ($v) { return [$v]; };
160+
$this->ifPart = static function ($v) { return !\is_array($v); };
161+
$this->allowedTypes = self::TYPE_ANY;
162+
$this->thenPart = static function ($v) { return [$v]; };
148163

149164
return $this;
150165
}
@@ -168,7 +183,7 @@ public function then(\Closure $closure): static
168183
*/
169184
public function thenEmptyArray(): static
170185
{
171-
$this->thenPart = function () { return []; };
186+
$this->thenPart = static function () { return []; };
172187

173188
return $this;
174189
}
@@ -184,7 +199,7 @@ public function thenEmptyArray(): static
184199
*/
185200
public function thenInvalid(string $message): static
186201
{
187-
$this->thenPart = function ($v) use ($message) { throw new \InvalidArgumentException(sprintf($message, json_encode($v))); };
202+
$this->thenPart = static function ($v) use ($message) { throw new \InvalidArgumentException(sprintf($message, json_encode($v))); };
188203

189204
return $this;
190205
}
@@ -198,7 +213,7 @@ public function thenInvalid(string $message): static
198213
*/
199214
public function thenUnset(): static
200215
{
201-
$this->thenPart = function () { throw new UnsetKeyException('Unsetting key.'); };
216+
$this->thenPart = static function () { throw new UnsetKeyException('Unsetting key.'); };
202217

203218
return $this;
204219
}
@@ -231,7 +246,7 @@ public static function buildExpressions(array $expressions): array
231246
if ($expr instanceof self) {
232247
$if = $expr->ifPart;
233248
$then = $expr->thenPart;
234-
$expressions[$k] = function ($v) use ($if, $then) {
249+
$expressions[$k] = static function ($v) use ($if, $then) {
235250
return $if($v) ? $then($v) : $v;
236251
};
237252
}

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) {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class NormalizationBuilder
2020
{
2121
protected $node;
2222
public $before = [];
23+
public $declaredTypes = [];
2324
public $remappings = [];
2425

2526
public function __construct(NodeDefinition $node)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy