Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8321162

Browse filesBrowse files
feature #44166 [Config] Use better typehint in PHP Configuration (jderusse)
This PR was merged into the 6.2 branch. Discussion ---------- [Config] Use better typehint in PHP Configuration | Q | A | ------------- | --- | Branch? | 6.2 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | - | License | MIT | Doc PR | - This PR now followup #46328 to replace the generic `mixed` type-hint by type inferred from normalizations (ie, if a config use `ifString`, when now that the normalization works only for string, these `mixed` can safely be replaced by `string`) Commits ------- 0e590dc Allow scalar configuration in PHP Configuration
2 parents e322c8a + 0e590dc commit 8321162
Copy full SHA for 8321162

File tree

8 files changed

+116
-59
lines changed
Filter options

8 files changed

+116
-59
lines changed

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Config/Builder/ConfigBuilderGenerator.php
+53-42Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Config/CHANGELOG.md
+1Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Config/Definition/BaseNode.php
+21Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php
+1Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php
+30-14Lines 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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php
+6Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.