From 0e590dc8b9a2ca37567cee2af126a73225330842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Sat, 20 Nov 2021 19:20:10 +0100 Subject: [PATCH] Allow scalar configuration in PHP Configuration --- .../Config/Builder/ConfigBuilderGenerator.php | 95 +++++++++++-------- src/Symfony/Component/Config/CHANGELOG.md | 1 + .../Component/Config/Definition/BaseNode.php | 21 ++++ .../Builder/ArrayNodeDefinition.php | 1 + .../Config/Definition/Builder/ExprBuilder.php | 44 ++++++--- .../Definition/Builder/NodeDefinition.php | 6 ++ .../Builder/NormalizationBuilder.php | 1 + .../Config/ScalarNormalizedTypesConfig.php | 6 +- 8 files changed, 116 insertions(+), 59 deletions(-) diff --git a/src/Symfony/Component/Config/Builder/ConfigBuilderGenerator.php b/src/Symfony/Component/Config/Builder/ConfigBuilderGenerator.php index 93290a9b5023e..8dc7f4cb5f885 100644 --- a/src/Symfony/Component/Config/Builder/ConfigBuilderGenerator.php +++ b/src/Symfony/Component/Config/Builder/ConfigBuilderGenerator.php @@ -14,6 +14,7 @@ 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; @@ -141,8 +142,9 @@ private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $n $node->getName(), $this->getType($childClass->getFqcn(), $hasNormalizationClosures) ); + $nodeTypes = $this->getParameterTypes($node); $body = $hasNormalizationClosures ? ' -COMMENTpublic function NAME(mixed $value = []): CLASS|static +COMMENTpublic function NAME(PARAM_TYPE $value = []): CLASS|static { if (!\is_array($value)) { $this->_usedProperties[\'PROPERTY\'] = true; @@ -172,7 +174,12 @@ private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $n return $this->PROPERTY; }'; $class->addUse(InvalidConfigurationException::class); - $class->addMethod($node->getName(), $body, ['COMMENT' => $comment, 'PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn()]); + $class->addMethod($node->getName(), $body, [ + 'COMMENT' => $comment, + 'PROPERTY' => $property->getName(), + 'CLASS' => $childClass->getFqcn(), + 'PARAM_TYPE' => \in_array('mixed', $nodeTypes, true) ? 'mixed' : implode('|', $nodeTypes), + ]); $this->buildNode($node, $childClass, $this->getSubNamespace($childClass)); } @@ -209,19 +216,21 @@ private function handlePrototypedArrayNode(PrototypedArrayNode $node, ClassBuild $methodName = $name; $hasNormalizationClosures = $this->hasNormalizationClosures($node) || $this->hasNormalizationClosures($prototype); - $parameterType = $this->getParameterType($prototype); - if (null !== $parameterType || $prototype instanceof ScalarNode) { + $nodeParameterTypes = $this->getParameterTypes($node); + $prototypeParameterTypes = $this->getParameterTypes($prototype); + if (!$prototype instanceof ArrayNode || ($prototype instanceof PrototypedArrayNode && $prototype->getPrototype() 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 PHPDOC_TYPE $value + * @param ParamConfigurator|listEXTRA_TYPE $value * * @return $this */ -public function NAME(TYPE $value): static +public function NAME(PARAM_TYPE $value): static { $this->_usedProperties[\'PROPERTY\'] = true; $this->PROPERTY = $value; @@ -231,8 +240,9 @@ public function NAME(TYPE $value): static $class->addMethod($node->getName(), $body, [ 'PROPERTY' => $property->getName(), - 'TYPE' => $hasNormalizationClosures ? 'mixed' : 'ParamConfigurator|array', - 'PHPDOC_TYPE' => $hasNormalizationClosures ? 'mixed' : sprintf('ParamConfigurator|list', '' === $parameterType ? 'mixed' : $parameterType), + 'PROTOTYPE_TYPE' => implode('|', $prototypeParameterTypes), + 'EXTRA_TYPE' => $nodeTypesWithoutArray ? '|'.implode('|', $nodeTypesWithoutArray) : '', + 'PARAM_TYPE' => \in_array('mixed', $nodeParameterTypes, true) ? 'mixed' : 'ParamConfigurator|'.implode('|', $nodeParameterTypes), ]); } else { $body = ' @@ -249,7 +259,7 @@ public function NAME(string $VAR, TYPE $VALUE): static $class->addMethod($methodName, $body, [ 'PROPERTY' => $property->getName(), - 'TYPE' => $hasNormalizationClosures || '' === $parameterType ? 'mixed' : 'ParamConfigurator|'.$parameterType, + 'TYPE' => \in_array('mixed', $prototypeParameterTypes, true) ? 'mixed' : 'ParamConfigurator|'.implode('|', $prototypeParameterTypes), 'VAR' => '' === $key ? 'key' : $key, 'VALUE' => 'value' === $key ? 'data' : 'value', ]); @@ -282,7 +292,7 @@ public function NAME(string $VAR, TYPE $VALUE): static if (null === $key = $node->getKeyAttribute()) { $body = $hasNormalizationClosures ? ' -COMMENTpublic function NAME(mixed $value = []): CLASS|static +COMMENTpublic function NAME(PARAM_TYPE $value = []): CLASS|static { $this->_usedProperties[\'PROPERTY\'] = true; if (!\is_array($value)) { @@ -299,10 +309,15 @@ public function NAME(string $VAR, TYPE $VALUE): static return $this->PROPERTY[] = new CLASS($value); }'; - $class->addMethod($methodName, $body, ['COMMENT' => $comment, 'PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn()]); + $class->addMethod($methodName, $body, [ + 'COMMENT' => $comment, + 'PROPERTY' => $property->getName(), + 'CLASS' => $childClass->getFqcn(), + 'PARAM_TYPE' => \in_array('mixed', $nodeParameterTypes, true) ? 'mixed' : implode('|', $nodeParameterTypes), + ]); } else { $body = $hasNormalizationClosures ? ' -COMMENTpublic function NAME(string $VAR, mixed $VALUE = []): CLASS|static +COMMENTpublic function NAME(string $VAR, PARAM_TYPE $VALUE = []): CLASS|static { if (!\is_array($VALUE)) { $this->_usedProperties[\'PROPERTY\'] = true; @@ -337,6 +352,7 @@ 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), ]); } @@ -364,35 +380,33 @@ public function NAME($value): static $class->addMethod($node->getName(), $body, ['PROPERTY' => $property->getName(), 'COMMENT' => $comment]); } - private function getParameterType(NodeInterface $node): ?string + private function getParameterTypes(NodeInterface $node): array { - if ($node instanceof BooleanNode) { - return 'bool'; - } - - if ($node instanceof IntegerNode) { - return 'int'; - } - - 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'; + $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 VariableNode) { - // mixed - return ''; + 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 null; + return array_unique($paramTypes); } private function getComment(BaseNode $node): string @@ -416,11 +430,8 @@ private function getComment(BaseNode $node): string return var_export($a, true); }, $node->getValues())))."\n"; } else { - $parameterType = $this->getParameterType($node); - if (null === $parameterType || '' === $parameterType) { - $parameterType = 'mixed'; - } - $comment .= ' * @param ParamConfigurator|'.$parameterType.' $value'."\n"; + $parameterTypes = $this->getParameterTypes($node); + $comment .= ' * @param ParamConfigurator|'.implode('|', $parameterTypes).' $value'."\n"; } } else { foreach ((array) ($node->getExample() ?? []) as $example) { diff --git a/src/Symfony/Component/Config/CHANGELOG.md b/src/Symfony/Component/Config/CHANGELOG.md index c88f2a321dc15..0fac3a8a550c7 100644 --- a/src/Symfony/Component/Config/CHANGELOG.md +++ b/src/Symfony/Component/Config/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Deprecate calling `NodeBuilder::setParent()` without any arguments + * Add a more accurate typehint in generated PHP config 6.1 --- diff --git a/src/Symfony/Component/Config/Definition/BaseNode.php b/src/Symfony/Component/Config/Definition/BaseNode.php index d5d145e96a631..814376091f9c7 100644 --- a/src/Symfony/Component/Config/Definition/BaseNode.php +++ b/src/Symfony/Component/Config/Definition/BaseNode.php @@ -32,6 +32,7 @@ abstract class BaseNode implements NodeInterface protected $name; protected $parent; protected $normalizationClosures = []; + protected $normalizedTypes = []; protected $finalValidationClosures = []; protected $allowOverwrite = true; protected $required = false; @@ -212,6 +213,26 @@ public function setNormalizationClosures(array $closures) $this->normalizationClosures = $closures; } + /** + * Sets the list of types supported by normalization. + * + * see ExprBuilder::TYPE_* constants. + */ + 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. * diff --git a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php index dba73cb2a3a2f..e1ff059f3b0ce 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php @@ -405,6 +405,7 @@ protected function createNode(): NodeInterface if (null !== $this->normalization) { $node->setNormalizationClosures($this->normalization->before); + $node->setNormalizedTypes($this->normalization->declaredTypes); $node->setXmlRemappings($this->normalization->remappings); } diff --git a/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php b/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php index a9f158e8ea259..07df0f4e26ee7 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php +++ b/src/Symfony/Component/Config/Definition/Builder/ExprBuilder.php @@ -21,7 +21,14 @@ */ class ExprBuilder { + public const TYPE_ANY = 'any'; + public const TYPE_STRING = 'string'; + public const TYPE_NULL = 'null'; + public const TYPE_ARRAY = 'array'; + protected $node; + + public $allowedTypes; public $ifPart; public $thenPart; @@ -37,7 +44,8 @@ public function __construct(NodeDefinition $node) */ public function always(\Closure $then = null): static { - $this->ifPart = function () { return true; }; + $this->ifPart = static function () { return true; }; + $this->allowedTypes = self::TYPE_ANY; if (null !== $then) { $this->thenPart = $then; @@ -56,10 +64,11 @@ public function always(\Closure $then = null): static public function ifTrue(\Closure $closure = null): static { if (null === $closure) { - $closure = function ($v) { return true === $v; }; + $closure = static function ($v) { return true === $v; }; } $this->ifPart = $closure; + $this->allowedTypes = self::TYPE_ANY; return $this; } @@ -71,7 +80,8 @@ public function ifTrue(\Closure $closure = null): static */ public function ifString(): static { - $this->ifPart = function ($v) { return \is_string($v); }; + $this->ifPart = static function ($v) { return \is_string($v); }; + $this->allowedTypes = self::TYPE_STRING; return $this; } @@ -83,7 +93,8 @@ public function ifString(): static */ public function ifNull(): static { - $this->ifPart = function ($v) { return null === $v; }; + $this->ifPart = static function ($v) { return null === $v; }; + $this->allowedTypes = self::TYPE_NULL; return $this; } @@ -95,7 +106,8 @@ public function ifNull(): static */ public function ifEmpty(): static { - $this->ifPart = function ($v) { return empty($v); }; + $this->ifPart = static function ($v) { return empty($v); }; + $this->allowedTypes = self::TYPE_ANY; return $this; } @@ -107,7 +119,8 @@ public function ifEmpty(): static */ public function ifArray(): static { - $this->ifPart = function ($v) { return \is_array($v); }; + $this->ifPart = static function ($v) { return \is_array($v); }; + $this->allowedTypes = self::TYPE_ARRAY; return $this; } @@ -119,7 +132,8 @@ public function ifArray(): static */ public function ifInArray(array $array): static { - $this->ifPart = function ($v) use ($array) { return \in_array($v, $array, true); }; + $this->ifPart = static function ($v) use ($array) { return \in_array($v, $array, true); }; + $this->allowedTypes = self::TYPE_ANY; return $this; } @@ -131,7 +145,8 @@ public function ifInArray(array $array): static */ public function ifNotInArray(array $array): static { - $this->ifPart = function ($v) use ($array) { return !\in_array($v, $array, true); }; + $this->ifPart = static function ($v) use ($array) { return !\in_array($v, $array, true); }; + $this->allowedTypes = self::TYPE_ANY; return $this; } @@ -143,8 +158,9 @@ public function ifNotInArray(array $array): static */ public function castToArray(): static { - $this->ifPart = function ($v) { return !\is_array($v); }; - $this->thenPart = function ($v) { return [$v]; }; + $this->ifPart = static function ($v) { return !\is_array($v); }; + $this->allowedTypes = self::TYPE_ANY; + $this->thenPart = static function ($v) { return [$v]; }; return $this; } @@ -168,7 +184,7 @@ public function then(\Closure $closure): static */ public function thenEmptyArray(): static { - $this->thenPart = function () { return []; }; + $this->thenPart = static function () { return []; }; return $this; } @@ -184,7 +200,7 @@ public function thenEmptyArray(): static */ public function thenInvalid(string $message): static { - $this->thenPart = function ($v) use ($message) { throw new \InvalidArgumentException(sprintf($message, json_encode($v))); }; + $this->thenPart = static function ($v) use ($message) { throw new \InvalidArgumentException(sprintf($message, json_encode($v))); }; return $this; } @@ -198,7 +214,7 @@ public function thenInvalid(string $message): static */ public function thenUnset(): static { - $this->thenPart = function () { throw new UnsetKeyException('Unsetting key.'); }; + $this->thenPart = static function () { throw new UnsetKeyException('Unsetting key.'); }; return $this; } @@ -231,7 +247,7 @@ public static function buildExpressions(array $expressions): array if ($expr instanceof self) { $if = $expr->ifPart; $then = $expr->thenPart; - $expressions[$k] = function ($v) use ($if, $then) { + $expressions[$k] = static function ($v) use ($if, $then) { return $if($v) ? $then($v) : $v; }; } diff --git a/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php index 173b08c7f7089..2792507796961 100644 --- a/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/NodeDefinition.php @@ -106,7 +106,13 @@ public function getNode(bool $forceRootNode = false): NodeInterface } if (null !== $this->normalization) { + $allowedTypes = []; + foreach ($this->normalization->before as $expr) { + $allowedTypes[] = $expr->allowedTypes; + } + $allowedTypes = array_unique($allowedTypes); $this->normalization->before = ExprBuilder::buildExpressions($this->normalization->before); + $this->normalization->declaredTypes = $allowedTypes; } if (null !== $this->validation) { diff --git a/src/Symfony/Component/Config/Definition/Builder/NormalizationBuilder.php b/src/Symfony/Component/Config/Definition/Builder/NormalizationBuilder.php index 59f92ffc6864b..0e362d9fa3c90 100644 --- a/src/Symfony/Component/Config/Definition/Builder/NormalizationBuilder.php +++ b/src/Symfony/Component/Config/Definition/Builder/NormalizationBuilder.php @@ -20,6 +20,7 @@ class NormalizationBuilder { protected $node; public $before = []; + public $declaredTypes = []; public $remappings = []; public function __construct(NodeDefinition $node) diff --git a/src/Symfony/Component/Config/Tests/Builder/Fixtures/ScalarNormalizedTypes/Symfony/Config/ScalarNormalizedTypesConfig.php b/src/Symfony/Component/Config/Tests/Builder/Fixtures/ScalarNormalizedTypes/Symfony/Config/ScalarNormalizedTypesConfig.php index 4ea170a4bdd4b..c514b8be4b8e1 100644 --- a/src/Symfony/Component/Config/Tests/Builder/Fixtures/ScalarNormalizedTypes/Symfony/Config/ScalarNormalizedTypesConfig.php +++ b/src/Symfony/Component/Config/Tests/Builder/Fixtures/ScalarNormalizedTypes/Symfony/Config/ScalarNormalizedTypesConfig.php @@ -24,11 +24,11 @@ class ScalarNormalizedTypesConfig implements \Symfony\Component\Config\Builder\C private $_usedProperties = []; /** - * @param mixed $value + * @param ParamConfigurator|list|string $value * * @return $this */ - public function simpleArray(mixed $value): static + public function simpleArray(ParamConfigurator|string|array $value): static { $this->_usedProperties['simpleArray'] = true; $this->simpleArray = $value; @@ -39,7 +39,7 @@ public function simpleArray(mixed $value): static /** * @return $this */ - public function keyedArray(string $name, mixed $value): static + public function keyedArray(string $name, ParamConfigurator|string|array $value): static { $this->_usedProperties['keyedArray'] = true; $this->keyedArray[$name] = $value; 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