Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 55 additions & 108 deletions src/Symfony/Component/Config/Builder/ConfigBuilderGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
use Symfony\Component\Config\Definition\ArrayNode;
use Symfony\Component\Config\Definition\BaseNode;
use Symfony\Component\Config\Definition\BooleanNode;
use Symfony\Component\Config\Definition\Builder\ExprBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\EnumNode;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
Expand Down Expand Up @@ -127,24 +126,18 @@ private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $n
$class->addRequire($childClass);
$this->classes[] = $childClass;

$hasNormalizationClosures = $this->hasNormalizationClosures($node);
$comment = $this->getComment($node);
if ($hasNormalizationClosures) {
$comment = sprintf(" * @template TValue\n * @param TValue \$value\n%s", $comment);
$comment .= sprintf(' * @return %s|$this'."\n", $childClass->getFqcn());
$comment .= sprintf(' * @psalm-return (TValue is array ? %s : static)'."\n ", $childClass->getFqcn());
}
if ('' !== $comment) {
$comment = "/**\n$comment*/\n";
}
$comment = sprintf(" * @template TValue\n * @param TValue \$value\n%s", $comment);
$comment .= sprintf(' * @return %s|$this'."\n", $childClass->getFqcn());
$comment .= sprintf(' * @psalm-return (TValue is array ? %s : static)'."\n ", $childClass->getFqcn());
$comment = "/**\n$comment*/\n";

$property = $class->addProperty(
$node->getName(),
$this->getType($childClass->getFqcn(), $hasNormalizationClosures)
$childClass->getFqcn().'|scalar'
);
$nodeTypes = $this->getParameterTypes($node);
$body = $hasNormalizationClosures ? '
COMMENTpublic function NAME(PARAM_TYPE $value = []): CLASS|static
$body = '
COMMENTpublic function NAME(mixed $value = []): CLASS|static
{
if (!\is_array($value)) {
$this->_usedProperties[\'PROPERTY\'] = true;
Expand All @@ -160,26 +153,10 @@ private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $n
throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\');
}

return $this->PROPERTY;
}' : '
COMMENTpublic function NAME(array $value = []): CLASS
{
if (null === $this->PROPERTY) {
$this->_usedProperties[\'PROPERTY\'] = true;
$this->PROPERTY = new CLASS($value);
} elseif (0 < \func_num_args()) {
throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\');
}

return $this->PROPERTY;
}';
$class->addUse(InvalidConfigurationException::class);
$class->addMethod($node->getName(), $body, [
'COMMENT' => $comment,
'PROPERTY' => $property->getName(),
'CLASS' => $childClass->getFqcn(),
'PARAM_TYPE' => \in_array('mixed', $nodeTypes, true) ? 'mixed' : implode('|', $nodeTypes),
]);
$class->addMethod($node->getName(), $body, ['COMMENT' => $comment, 'PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn()]);

$this->buildNode($node, $childClass, $this->getSubNamespace($childClass));
}
Expand Down Expand Up @@ -216,21 +193,19 @@ private function handlePrototypedArrayNode(PrototypedArrayNode $node, ClassBuild
$methodName = $name;
$hasNormalizationClosures = $this->hasNormalizationClosures($node) || $this->hasNormalizationClosures($prototype);

$nodeParameterTypes = $this->getParameterTypes($node);
$prototypeParameterTypes = $this->getParameterTypes($prototype);
if (!$prototype instanceof ArrayNode || ($prototype instanceof PrototypedArrayNode && $prototype->getPrototype() instanceof ScalarNode)) {
$parameterType = $this->getParameterType($prototype);
if (null !== $parameterType || $prototype instanceof ScalarNode) {
$class->addUse(ParamConfigurator::class);
$property = $class->addProperty($node->getName());
if (null === $key = $node->getKeyAttribute()) {
// This is an array of values; don't use singular name
$nodeTypesWithoutArray = array_filter($nodeParameterTypes, static fn ($type) => 'array' !== $type);
$body = '
/**
* @param ParamConfigurator|list<ParamConfigurator|PROTOTYPE_TYPE>EXTRA_TYPE $value
* @param PHPDOC_TYPE $value
*
* @return $this
*/
public function NAME(PARAM_TYPE $value): static
public function NAME(TYPE $value): static
{
$this->_usedProperties[\'PROPERTY\'] = true;
$this->PROPERTY = $value;
Expand All @@ -240,9 +215,8 @@ public function NAME(PARAM_TYPE $value): static

$class->addMethod($node->getName(), $body, [
'PROPERTY' => $property->getName(),
'PROTOTYPE_TYPE' => implode('|', $prototypeParameterTypes),
'EXTRA_TYPE' => $nodeTypesWithoutArray ? '|'.implode('|', $nodeTypesWithoutArray) : '',
'PARAM_TYPE' => \in_array('mixed', $nodeParameterTypes, true) ? 'mixed' : 'ParamConfigurator|'.implode('|', $nodeParameterTypes),
'TYPE' => $hasNormalizationClosures ? 'mixed' : 'ParamConfigurator|array',
'PHPDOC_TYPE' => $hasNormalizationClosures ? 'mixed' : sprintf('ParamConfigurator|list<ParamConfigurator|%s>', '' === $parameterType ? 'mixed' : $parameterType),
]);
} else {
$body = '
Expand All @@ -259,7 +233,7 @@ public function NAME(string $VAR, TYPE $VALUE): static

$class->addMethod($methodName, $body, [
'PROPERTY' => $property->getName(),
'TYPE' => \in_array('mixed', $prototypeParameterTypes, true) ? 'mixed' : 'ParamConfigurator|'.implode('|', $prototypeParameterTypes),
'TYPE' => 'mixed',
'VAR' => '' === $key ? 'key' : $key,
'VALUE' => 'value' === $key ? 'data' : 'value',
]);
Expand All @@ -277,22 +251,18 @@ public function NAME(string $VAR, TYPE $VALUE): static

$property = $class->addProperty(
$node->getName(),
$this->getType($childClass->getFqcn().'[]', $hasNormalizationClosures)
$childClass->getFqcn().'[]|scalar'
);

$comment = $this->getComment($node);
if ($hasNormalizationClosures) {
$comment = sprintf(" * @template TValue\n * @param TValue \$value\n%s", $comment);
$comment .= sprintf(' * @return %s|$this'."\n", $childClass->getFqcn());
$comment .= sprintf(' * @psalm-return (TValue is array ? %s : static)'."\n ", $childClass->getFqcn());
}
if ('' !== $comment) {
$comment = "/**\n$comment*/\n";
}
$comment = sprintf(" * @template TValue\n * @param TValue \$value\n%s", $comment);
$comment .= sprintf(' * @return %s|$this'."\n", $childClass->getFqcn());
$comment .= sprintf(' * @psalm-return (TValue is array ? %s : static)'."\n ", $childClass->getFqcn());
$comment = "/**\n$comment*/\n";

if (null === $key = $node->getKeyAttribute()) {
$body = $hasNormalizationClosures ? '
COMMENTpublic function NAME(PARAM_TYPE $value = []): CLASS|static
$body = '
COMMENTpublic function NAME(mixed $value = []): CLASS|static
{
$this->_usedProperties[\'PROPERTY\'] = true;
if (!\is_array($value)) {
Expand All @@ -301,23 +271,12 @@ public function NAME(string $VAR, TYPE $VALUE): static
return $this;
}

return $this->PROPERTY[] = new CLASS($value);
}' : '
COMMENTpublic function NAME(array $value = []): CLASS
{
$this->_usedProperties[\'PROPERTY\'] = true;

return $this->PROPERTY[] = new CLASS($value);
}';
$class->addMethod($methodName, $body, [
'COMMENT' => $comment,
'PROPERTY' => $property->getName(),
'CLASS' => $childClass->getFqcn(),
'PARAM_TYPE' => \in_array('mixed', $nodeParameterTypes, true) ? 'mixed' : implode('|', $nodeParameterTypes),
]);
$class->addMethod($methodName, $body, ['COMMENT' => $comment, 'PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn()]);
} else {
$body = $hasNormalizationClosures ? '
COMMENTpublic function NAME(string $VAR, PARAM_TYPE $VALUE = []): CLASS|static
$body = '
COMMENTpublic function NAME(string $VAR, mixed $VALUE = []): CLASS|static
{
if (!\is_array($VALUE)) {
$this->_usedProperties[\'PROPERTY\'] = true;
Expand All @@ -333,17 +292,6 @@ public function NAME(string $VAR, TYPE $VALUE): static
throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\');
}

return $this->PROPERTY[$VAR];
}' : '
COMMENTpublic function NAME(string $VAR, array $VALUE = []): CLASS
{
if (!isset($this->PROPERTY[$VAR])) {
$this->_usedProperties[\'PROPERTY\'] = true;
$this->PROPERTY[$VAR] = new CLASS($VALUE);
} elseif (1 < \func_num_args()) {
throw new InvalidConfigurationException(\'The node created by "NAME()" has already been initialized. You cannot pass values the second time you call NAME().\');
}

return $this->PROPERTY[$VAR];
}';
$class->addUse(InvalidConfigurationException::class);
Expand All @@ -352,7 +300,6 @@ public function NAME(string $VAR, TYPE $VALUE): static
'CLASS' => $childClass->getFqcn(),
'VAR' => '' === $key ? 'key' : $key,
'VALUE' => 'value' === $key ? 'data' : 'value',
'PARAM_TYPE' => \in_array('mixed', $prototypeParameterTypes, true) ? 'mixed' : implode('|', $prototypeParameterTypes),
]);
}

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

private function getParameterTypes(NodeInterface $node): array
private function getParameterType(NodeInterface $node): ?string
{
$paramTypes = [];
if ($node instanceof BaseNode) {
$types = $node->getNormalizedTypes();
if (\in_array(ExprBuilder::TYPE_ANY, $types, true)) {
$paramTypes[] = 'mixed';
}
if (\in_array(ExprBuilder::TYPE_STRING, $types, true)) {
$paramTypes[] = 'string';
}
}
if ($node instanceof BooleanNode) {
$paramTypes[] = 'bool';
} elseif ($node instanceof IntegerNode) {
$paramTypes[] = 'int';
} elseif ($node instanceof FloatNode) {
$paramTypes[] = 'float';
} elseif ($node instanceof EnumNode) {
$paramTypes[] = 'mixed';
} elseif ($node instanceof ArrayNode) {
$paramTypes[] = 'array';
} elseif ($node instanceof VariableNode) {
$paramTypes[] = 'mixed';
return 'bool';
}

if ($node instanceof IntegerNode) {
return 'int';
}

return array_unique($paramTypes);
if ($node instanceof FloatNode) {
return 'float';
}

if ($node instanceof EnumNode) {
return '';
}

if ($node instanceof PrototypedArrayNode && $node->getPrototype() instanceof ScalarNode) {
// This is just an array of variables
return 'array';
}

if ($node instanceof VariableNode) {
// mixed
return '';
}

return null;
}

private function getComment(BaseNode $node): string
Expand All @@ -428,8 +377,11 @@ private function getComment(BaseNode $node): string
if ($node instanceof EnumNode) {
$comment .= sprintf(' * @param ParamConfigurator|%s $value', implode('|', array_unique(array_map(fn ($a) => !$a instanceof \UnitEnum ? var_export($a, true) : '\\'.ltrim(var_export($a, true), '\\'), $node->getValues()))))."\n";
} else {
$parameterTypes = $this->getParameterTypes($node);
$comment .= ' * @param ParamConfigurator|'.implode('|', $parameterTypes).' $value'."\n";
$parameterType = $this->getParameterType($node);
if (null === $parameterType || '' === $parameterType) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why returning an empty string to mean mixed instead of returning mixed ?

$parameterType = 'mixed';
}
$comment .= ' * @param ParamConfigurator|'.$parameterType.' $value'."\n";
}
} else {
foreach ((array) ($node->getExample() ?? []) as $example) {
Expand Down Expand Up @@ -592,9 +544,4 @@ private function hasNormalizationClosures(NodeInterface $node): bool

return [] !== $r->getValue($node);
}

private function getType(string $classType, bool $hasNormalizationClosures): string
{
return $classType.($hasNormalizationClosures ? '|scalar' : '');
}
}
23 changes: 0 additions & 23 deletions src/Symfony/Component/Config/Definition/BaseNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ abstract class BaseNode implements NodeInterface
protected $name;
protected $parent;
protected $normalizationClosures = [];
protected $normalizedTypes = [];
protected $finalValidationClosures = [];
protected $allowOverwrite = true;
protected $required = false;
Expand Down Expand Up @@ -236,28 +235,6 @@ public function setNormalizationClosures(array $closures)
$this->normalizationClosures = $closures;
}

/**
* Sets the list of types supported by normalization.
*
* see ExprBuilder::TYPE_* constants.
*
* @return void
*/
public function setNormalizedTypes(array $types)
{
$this->normalizedTypes = $types;
}

/**
* Gets the list of types supported by normalization.
*
* see ExprBuilder::TYPE_* constants.
*/
public function getNormalizedTypes(): array
{
return $this->normalizedTypes;
}

/**
* Sets the closures used for final validation.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ protected function createNode(): NodeInterface

if ($this->default) {
if (!\is_array($this->defaultValue)) {
throw new \InvalidArgumentException(sprintf('%s: the default value of an array node has to be an array.', $node->getPath()));
throw new \InvalidArgumentException(sprintf('"%s": the default value of an array node has to be an array.', $node->getPath()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be reverted, that's a false-positive from fabbot I suppose

}

$node->setDefaultValue($this->defaultValue);
Expand Down Expand Up @@ -406,7 +406,6 @@ protected function createNode(): NodeInterface

if (isset($this->normalization)) {
$node->setNormalizationClosures($this->normalization->before);
$node->setNormalizedTypes($this->normalization->declaredTypes);
$node->setXmlRemappings($this->normalization->remappings);
}

Expand Down
Loading