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

Skip to content

Commit ed06540

Browse files
committed
Allow scalar configuration in PHP Configuration
1 parent ba6de87 commit ed06540

11 files changed

+307
-60
lines changed

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

+147-45
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
namespace Symfony\Component\Config\Builder;
1313

1414
use Symfony\Component\Config\Definition\ArrayNode;
15+
use Symfony\Component\Config\Definition\BaseNode;
1516
use Symfony\Component\Config\Definition\BooleanNode;
17+
use Symfony\Component\Config\Definition\Builder\ExprBuilder;
1618
use Symfony\Component\Config\Definition\ConfigurationInterface;
1719
use Symfony\Component\Config\Definition\EnumNode;
1820
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
@@ -131,8 +133,11 @@ private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $n
131133
$this->classes[] = $childClass;
132134

133135
$property = $class->addProperty($node->getName(), $childClass->getFqcn());
134-
$body = '
135-
public function NAME(array $value = []): CLASS
136+
$nodeType = $this->getParameterType($node);
137+
138+
if ('array' === $nodeType) {
139+
$body = '
140+
public function NAME(PARAM_TYPE $value = []): RETURN_TYPEHINT
136141
{
137142
if (null === $this->PROPERTY) {
138143
$this->PROPERTY = new CLASS($value);
@@ -142,8 +147,33 @@ public function NAME(array $value = []): CLASS
142147
143148
return $this->PROPERTY;
144149
}';
150+
} else {
151+
$body = '
152+
public function NAME(PARAM_TYPE $value = []): RETURN_TYPEHINT
153+
{
154+
if (null === $this->PROPERTY) {
155+
if (\is_array($value)) {
156+
$this->PROPERTY = new CLASS($value);
157+
} else {
158+
$this->PROPERTY = $value;
159+
}
160+
} elseif (!$this->PROPERTY instanceof CLASS) {
161+
throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized with a scalar value. You cannot call NAME() anymore.\');
162+
} elseif ([] !== $value) {
163+
throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\');
164+
}
165+
166+
return $this->PROPERTY;
167+
}';
168+
}
169+
145170
$class->addUse(InvalidConfigurationException::class);
146-
$class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn()]);
171+
$class->addMethod($node->getName(), $body, [
172+
'PROPERTY' => $property->getName(),
173+
'CLASS' => $childClass->getFqcn(),
174+
'RETURN_TYPEHINT' => 'array' === $nodeType ? $childClass->getFqcn() : 'self|'.$childClass->getFqcn(),
175+
'PARAM_TYPE' => $nodeType,
176+
]);
147177

148178
$this->buildNode($node, $childClass, $this->getSubNamespace($childClass));
149179
}
@@ -174,39 +204,53 @@ private function handlePrototypedArrayNode(PrototypedArrayNode $node, ClassBuild
174204
$prototype = $node->getPrototype();
175205
$methodName = $name;
176206

177-
$parameterType = $this->getParameterType($prototype);
178-
if (null !== $parameterType || $prototype instanceof ScalarNode) {
207+
$nodeType = $this->getParameterType($node);
208+
$prototypeType = $this->getParameterType($prototype);
209+
210+
$isObject = $prototype instanceof ArrayNode && (!$prototype instanceof PrototypedArrayNode || !$prototype->getPrototype() instanceof ScalarNode);
211+
if (!$isObject) {
179212
$class->addUse(ParamConfigurator::class);
180213
$property = $class->addProperty($node->getName());
181214
if (null === $key = $node->getKeyAttribute()) {
182215
// This is an array of values; don't use singular name
216+
$nodeTypeWithoutArray = implode('|', array_filter(explode('|', $nodeType), fn ($type) => $type !== 'array'));
183217
$body = '
184218
/**
185-
* @param ParamConfigurator|list<ParamConfigurator|TYPE> $value
219+
* @param ParamConfigurator|list<ParamConfigurator|PROTOTYPE_TYPE>EXTRA_TYPE $value
186220
*
187221
* @return $this
188222
*/
189-
public function NAME(ParamConfigurator|array $value): static
223+
public function NAME(PARAM_TYPE $value): static
190224
{
191225
$this->PROPERTY = $value;
192226
193227
return $this;
194228
}';
195229

196-
$class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'TYPE' => '' === $parameterType ? 'mixed' : $parameterType]);
230+
$class->addMethod($node->getName(), $body, [
231+
'PROPERTY' => $property->getName(),
232+
'PROTOTYPE_TYPE' => $prototypeType,
233+
'EXTRA_TYPE' => $nodeTypeWithoutArray ? '|'.$nodeTypeWithoutArray : '',
234+
'PARAM_TYPE' => $nodeType === 'mixed' ? 'mixed' : 'ParamConfigurator|'.$nodeType,
235+
]);
197236
} else {
198237
$body = '
199238
/**
200239
* @return $this
201240
*/
202-
public function NAME(string $VAR, TYPE $VALUE): static
241+
public function NAME(string $VAR, PARAM_TYPE $VALUE): static
203242
{
204243
$this->PROPERTY[$VAR] = $VALUE;
205244
206245
return $this;
207246
}';
208247

209-
$class->addMethod($methodName, $body, ['PROPERTY' => $property->getName(), 'TYPE' => '' === $parameterType ? 'mixed' : 'ParamConfigurator|'.$parameterType, 'VAR' => '' === $key ? 'key' : $key, 'VALUE' => 'value' === $key ? 'data' : 'value']);
248+
$class->addMethod($methodName, $body, [
249+
'PROPERTY' => $property->getName(),
250+
'VAR' => '' === $key ? 'key' : $key,
251+
'VALUE' => 'value' === $key ? 'data' : 'value',
252+
'PARAM_TYPE' => $prototypeType === 'mixed' ? 'mixed' : 'ParamConfigurator|'.$prototypeType,
253+
]);
210254
}
211255

212256
return;
@@ -216,20 +260,41 @@ public function NAME(string $VAR, TYPE $VALUE): static
216260
if ($prototype instanceof ArrayNode) {
217261
$childClass->setAllowExtraKeys($prototype->shouldIgnoreExtraKeys());
218262
}
263+
219264
$class->addRequire($childClass);
220265
$this->classes[] = $childClass;
221266
$property = $class->addProperty($node->getName(), $childClass->getFqcn().'[]');
222267

223268
if (null === $key = $node->getKeyAttribute()) {
224-
$body = '
225-
public function NAME(array $value = []): CLASS
269+
if ('array' === $nodeType) {
270+
$body = '
271+
public function NAME(PARAM_TYPE $value = []): RETURN_TYPEHINT
226272
{
227273
return $this->PROPERTY[] = new CLASS($value);
228274
}';
229-
$class->addMethod($methodName, $body, ['PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn()]);
275+
} else {
276+
$body = '
277+
public function NAME(PARAM_TYPE $value = []): RETURN_TYPEHINT
278+
{
279+
if (\is_array($value)) {
280+
return $this->PROPERTY[] = new CLASS($value);
281+
}
282+
283+
$this->PROPERTY[] = $value;
284+
return $this;
285+
}';
286+
}
287+
288+
$class->addMethod($methodName, $body, [
289+
'PROPERTY' => $property->getName(),
290+
'CLASS' => $childClass->getFqcn(),
291+
'RETURN_TYPEHINT' => 'array' === $nodeType ? $childClass->getFqcn() : 'self|'.$childClass->getFqcn(),
292+
'PARAM_TYPE' => $nodeType,
293+
]);
230294
} else {
231-
$body = '
232-
public function NAME(string $VAR, array $VALUE = []): CLASS
295+
if ('array' === $nodeType) {
296+
$body = '
297+
public function NAME(string $VAR, PARAM_TYPE $VALUE = []): CLASS
233298
{
234299
if (!isset($this->PROPERTY[$VAR])) {
235300
return $this->PROPERTY[$VAR] = new CLASS($value);
@@ -240,8 +305,39 @@ public function NAME(string $VAR, array $VALUE = []): CLASS
240305
241306
throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\');
242307
}';
308+
} else {
309+
$body = '
310+
public function NAME(string $VAR, PARAM_TYPE $VALUE = []): RETURN_TYPEHINT
311+
{
312+
if (!isset($this->PROPERTY[$VAR])) {
313+
if (\is_array($VALUE)) {
314+
return $this->PROPERTY[$VAR] = new CLASS($value);
315+
} else {
316+
$this->PROPERTY[$VAR] = $VALUE;
317+
318+
return $this;
319+
}
320+
}
321+
if (!$this->PROPERTY[$VAR] instanceof CLASS) {
322+
throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized with a scalar value. You cannot call NAME() anymore.\');
323+
}
324+
if ([] === $value) {
325+
return $this->PROPERTY[$VAR];
326+
}
327+
328+
throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\');
329+
}';
330+
}
331+
243332
$class->addUse(InvalidConfigurationException::class);
244-
$class->addMethod($methodName, $body, ['PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn(), 'VAR' => '' === $key ? 'key' : $key, 'VALUE' => 'value' === $key ? 'data' : 'value']);
333+
$class->addMethod($methodName, $body, [
334+
'PROPERTY' => $property->getName(),
335+
'CLASS' => $childClass->getFqcn(),
336+
'RETURN_TYPEHINT' => 'array' === $nodeType ? $childClass->getFqcn() : 'self|'.$childClass->getFqcn(),
337+
'VAR' => '' === $key ? 'key' : $key,
338+
'VALUE' => 'value' === $key ? 'data' : 'value',
339+
'PARAM_TYPE' => $nodeType,
340+
]);
245341
}
246342

247343
$this->buildNode($prototype, $childClass, $namespace.'\\'.$childClass->getName());
@@ -267,35 +363,38 @@ public function NAME($value): static
267363
$class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'COMMENT' => $comment]);
268364
}
269365

270-
private function getParameterType(NodeInterface $node): ?string
366+
private function getParameterType(NodeInterface $node): string
271367
{
272-
if ($node instanceof BooleanNode) {
273-
return 'bool';
274-
}
275-
276-
if ($node instanceof IntegerNode) {
277-
return 'int';
278-
}
279-
280-
if ($node instanceof FloatNode) {
281-
return 'float';
282-
}
283-
284-
if ($node instanceof EnumNode) {
285-
return '';
286-
}
368+
$paramTypes = [];
369+
if ($node instanceof BaseNode) {
370+
$types = $node->getNormalizedTypes();
371+
if (\in_array(ExprBuilder::TYPE_ANY, $types, true)) {
372+
return 'mixed';
373+
}
287374

288-
if ($node instanceof PrototypedArrayNode && $node->getPrototype() instanceof ScalarNode) {
289-
// This is just an array of variables
290-
return 'array';
375+
if (\in_array(ExprBuilder::TYPE_STRING, $types, true)) {
376+
$paramTypes[] = 'string';
377+
}
378+
if (\in_array(ExprBuilder::TYPE_NULL, $types, true)) {
379+
$paramTypes[] = 'null';
380+
}
291381
}
292382

293-
if ($node instanceof VariableNode) {
294-
// mixed
295-
return '';
383+
if ($node instanceof BooleanNode) {
384+
$paramTypes[] = 'bool';
385+
} elseif ($node instanceof IntegerNode) {
386+
$paramTypes[] = 'int';
387+
} elseif ($node instanceof FloatNode) {
388+
$paramTypes[] = 'float';
389+
} elseif ($node instanceof EnumNode) {
390+
$paramTypes[] = 'mixed';
391+
} elseif ($node instanceof ArrayNode) {
392+
$paramTypes[] = 'array';
393+
} elseif ($node instanceof VariableNode) {
394+
$paramTypes[] = 'mixed';
296395
}
297396

298-
return null;
397+
return implode('|', $paramTypes);
299398
}
300399

301400
private function getComment(VariableNode $node): string
@@ -319,7 +418,7 @@ private function getComment(VariableNode $node): string
319418
}, $node->getValues())))."\n";
320419
} else {
321420
$parameterType = $this->getParameterType($node);
322-
if (null === $parameterType || '' === $parameterType) {
421+
if (null === $parameterType) {
323422
$parameterType = 'mixed';
324423
}
325424
$comment .= ' * @param ParamConfigurator|'.$parameterType.' $value'."\n";
@@ -361,16 +460,20 @@ private function buildToArray(ClassBuilder $class): void
361460
$code = '$this->PROPERTY';
362461
if (null !== $p->getType()) {
363462
if ($p->isArray()) {
364-
$code = 'array_map(function ($v) { return $v->toArray(); }, $this->PROPERTY)';
463+
$code = 'array_map(function ($v) { return $v instanceof CLASS ? $v->toArray() : $v; }, $this->PROPERTY)';
365464
} else {
366-
$code = '$this->PROPERTY->toArray()';
465+
$code = '$this->PROPERTY instanceof CLASS ? $this->PROPERTY->toArray() : $this->PROPERTY';
367466
}
368467
}
369468

370469
$body .= strtr('
371470
if (null !== $this->PROPERTY) {
372471
$output[\'ORG_NAME\'] = '.$code.';
373-
}', ['PROPERTY' => $p->getName(), 'ORG_NAME' => $p->getOriginalName()]);
472+
}', [
473+
'PROPERTY' => $p->getName(),
474+
'ORG_NAME' => $p->getOriginalName(),
475+
'CLASS' => $p->getType(),
476+
]);
374477
}
375478

376479
$extraKeys = $class->shouldAllowExtraKeys() ? ' + $this->_extraKeys' : '';
@@ -420,8 +523,7 @@ private function buildConstructor(ClassBuilder $class): void
420523

421524
$class->addMethod('__construct', '
422525
public function __construct(array $value = [])
423-
{
424-
'.$body.'
526+
{'.$body.'
425527
}');
426528
}
427529

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

+19
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

+1
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ protected function createNode(): NodeInterface
420420

421421
if (null !== $this->normalization) {
422422
$node->setNormalizationClosures($this->normalization->before);
423+
$node->setNormalizedTypes($this->normalization->declaredTypes);
423424
$node->setXmlRemappings($this->normalization->remappings);
424425
}
425426

0 commit comments

Comments
 (0)