From c79c6a29acbd3b3f707f433853d6a87e9985c3ba Mon Sep 17 00:00:00 2001 From: Bob van de Vijver Date: Tue, 26 Nov 2024 13:13:31 +0100 Subject: [PATCH] [Config] Do not generate unreachable configuration paths --- .../Config/Builder/ConfigBuilderGenerator.php | 43 ++++++--- .../Builder/Fixtures/ArrayValues.config.php | 19 ++++ .../Builder/Fixtures/ArrayValues.output.php | 24 +++++ .../Tests/Builder/Fixtures/ArrayValues.php | 42 ++++++++ .../Config/ArrayValues/ErrorPagesConfig.php | 75 +++++++++++++++ .../Config/ArrayValues/TransportsConfig.php | 52 ++++++++++ .../Symfony/Config/ArrayValuesConfig.php | 96 +++++++++++++++++++ .../ScalarNormalizedTypes/NestedConfig.php | 4 +- .../Config/ScalarNormalizedTypesConfig.php | 6 +- .../Tests/Builder/GeneratedConfigTest.php | 1 + 10 files changed, 344 insertions(+), 18 deletions(-) create mode 100644 src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues.config.php create mode 100644 src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues.output.php create mode 100644 src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues.php create mode 100644 src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues/Symfony/Config/ArrayValues/ErrorPagesConfig.php create mode 100644 src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues/Symfony/Config/ArrayValues/TransportsConfig.php create mode 100644 src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues/Symfony/Config/ArrayValuesConfig.php diff --git a/src/Symfony/Component/Config/Builder/ConfigBuilderGenerator.php b/src/Symfony/Component/Config/Builder/ConfigBuilderGenerator.php index 333d323aca29c..0ae8b86236059 100644 --- a/src/Symfony/Component/Config/Builder/ConfigBuilderGenerator.php +++ b/src/Symfony/Component/Config/Builder/ConfigBuilderGenerator.php @@ -127,10 +127,13 @@ private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $n $class->addRequire($childClass); $this->classes[] = $childClass; + $nodeTypes = $this->getParameterTypes($node); + $paramType = $this->getParamType($nodeTypes); + $hasNormalizationClosures = $this->hasNormalizationClosures($node); $comment = $this->getComment($node); - if ($hasNormalizationClosures) { - $comment = \sprintf(" * @template TValue\n * @param TValue \$value\n%s", $comment); + if ($hasNormalizationClosures && 'array' !== $paramType) { + $comment = \sprintf(" * @template TValue of %s\n * @param TValue \$value\n%s", $paramType, $comment); $comment .= \sprintf(' * @return %s|$this'."\n", $childClass->getFqcn()); $comment .= \sprintf(' * @psalm-return (TValue is array ? %s : static)'."\n ", $childClass->getFqcn()); } @@ -142,8 +145,7 @@ private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $n $node->getName(), $this->getType($childClass->getFqcn(), $hasNormalizationClosures) ); - $nodeTypes = $this->getParameterTypes($node); - $body = $hasNormalizationClosures ? ' + $body = $hasNormalizationClosures && 'array' !== $paramType ? ' COMMENTpublic function NAME(PARAM_TYPE $value = []): CLASS|static { if (!\is_array($value)) { @@ -178,7 +180,7 @@ private function handleArrayNode(ArrayNode $node, ClassBuilder $class, string $n 'COMMENT' => $comment, 'PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn(), - 'PARAM_TYPE' => \in_array('mixed', $nodeTypes, true) ? 'mixed' : implode('|', $nodeTypes), + 'PARAM_TYPE' => $paramType, ]); $this->buildNode($node, $childClass, $this->getSubNamespace($childClass)); @@ -218,10 +220,11 @@ private function handlePrototypedArrayNode(PrototypedArrayNode $node, ClassBuild $nodeParameterTypes = $this->getParameterTypes($node); $prototypeParameterTypes = $this->getParameterTypes($prototype); + $noKey = null === $key = $node->getKeyAttribute(); 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()) { + if ($noKey) { // This is an array of values; don't use singular name $nodeTypesWithoutArray = array_filter($nodeParameterTypes, static fn ($type) => 'array' !== $type); $body = ' @@ -242,7 +245,7 @@ public function NAME(PARAM_TYPE $value): static 'PROPERTY' => $property->getName(), 'PROTOTYPE_TYPE' => implode('|', $prototypeParameterTypes), 'EXTRA_TYPE' => $nodeTypesWithoutArray ? '|'.implode('|', $nodeTypesWithoutArray) : '', - 'PARAM_TYPE' => \in_array('mixed', $nodeParameterTypes, true) ? 'mixed' : 'ParamConfigurator|'.implode('|', $nodeParameterTypes), + 'PARAM_TYPE' => $this->getParamType($nodeParameterTypes, true), ]); } else { $body = ' @@ -259,7 +262,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' => $this->getParamType($prototypeParameterTypes, true), 'VAR' => '' === $key ? 'key' : $key, 'VALUE' => 'value' === $key ? 'data' : 'value', ]); @@ -280,18 +283,27 @@ public function NAME(string $VAR, TYPE $VALUE): static $this->getType($childClass->getFqcn().'[]', $hasNormalizationClosures) ); + $paramType = $this->getParamType($noKey ? $nodeParameterTypes : $prototypeParameterTypes); + $comment = $this->getComment($node); +<<<<<<< HEAD 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 ($hasNormalizationClosures && 'array' !== $paramType) { + $comment = sprintf(" * @template TValue of %s\n * @param TValue \$value\n%s", $paramType, $comment); + $comment .= sprintf(' * @return %s|$this'."\n", $childClass->getFqcn()); + $comment .= sprintf(' * @psalm-return (TValue is array ? %s : static)'."\n ", $childClass->getFqcn()); +>>>>>>> 100c683018d ([Config] Do not generate unreachable configuration paths) } if ('' !== $comment) { $comment = "/**\n$comment*/\n"; } - if (null === $key = $node->getKeyAttribute()) { - $body = $hasNormalizationClosures ? ' + if ($noKey) { + $body = $hasNormalizationClosures && 'array' !== $paramType ? ' COMMENTpublic function NAME(PARAM_TYPE $value = []): CLASS|static { $this->_usedProperties[\'PROPERTY\'] = true; @@ -313,10 +325,10 @@ public function NAME(string $VAR, TYPE $VALUE): static 'COMMENT' => $comment, 'PROPERTY' => $property->getName(), 'CLASS' => $childClass->getFqcn(), - 'PARAM_TYPE' => \in_array('mixed', $nodeParameterTypes, true) ? 'mixed' : implode('|', $nodeParameterTypes), + 'PARAM_TYPE' => $paramType, ]); } else { - $body = $hasNormalizationClosures ? ' + $body = $hasNormalizationClosures && 'array' !== $paramType ? ' COMMENTpublic function NAME(string $VAR, PARAM_TYPE $VALUE = []): CLASS|static { if (!\is_array($VALUE)) { @@ -352,7 +364,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), + 'PARAM_TYPE' => $paramType, ]); } @@ -597,4 +609,9 @@ private function getType(string $classType, bool $hasNormalizationClosures): str { return $classType.($hasNormalizationClosures ? '|scalar' : ''); } + + private function getParamType(array $types, bool $withParamConfigurator = false): string + { + return \in_array('mixed', $types, true) ? 'mixed' : ($withParamConfigurator ? 'ParamConfigurator|' : '').implode('|', $types); + } } diff --git a/src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues.config.php b/src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues.config.php new file mode 100644 index 0000000000000..642345ca5dd63 --- /dev/null +++ b/src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues.config.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Config\ArrayValuesConfig; + +return static function (ArrayValuesConfig $config) { + $config->transports('foo')->dsn('bar'); + $config->transports('bar', ['dsn' => 'foobar']); + + $config->errorPages()->withTrace(false); +}; diff --git a/src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues.output.php b/src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues.output.php new file mode 100644 index 0000000000000..ab56d53a2f562 --- /dev/null +++ b/src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues.output.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +return [ + 'transports' => [ + 'foo' => [ + 'dsn' => 'bar', + ], + 'bar' => [ + 'dsn' => 'foobar', + ], + ], + 'error_pages' => [ + 'with_trace' => false, + ] +]; diff --git a/src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues.php b/src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues.php new file mode 100644 index 0000000000000..457e78a1f7118 --- /dev/null +++ b/src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues.php @@ -0,0 +1,42 @@ +getRootNode(); + $rootNode + ->children() + ->arrayNode('transports') + ->normalizeKeys(false) + ->useAttributeAsKey('name') + ->arrayPrototype() + ->beforeNormalization() + ->ifString() + ->then(function (string $dsn) { + return ['dsn' => $dsn]; + }) + ->end() + ->fixXmlConfig('option') + ->children() + ->scalarNode('dsn')->end() + ->end() + ->end() + ->end() + ->arrayNode('error_pages') + ->canBeEnabled() + ->children() + ->booleanNode('with_trace')->end() + ->end() + ->end() + ->end(); + + return $tb; + } +} diff --git a/src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues/Symfony/Config/ArrayValues/ErrorPagesConfig.php b/src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues/Symfony/Config/ArrayValues/ErrorPagesConfig.php new file mode 100644 index 0000000000000..36ccb016dc6d9 --- /dev/null +++ b/src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues/Symfony/Config/ArrayValues/ErrorPagesConfig.php @@ -0,0 +1,75 @@ +_usedProperties['enabled'] = true; + $this->enabled = $value; + + return $this; + } + + /** + * @default null + * @param ParamConfigurator|bool $value + * @return $this + */ + public function withTrace($value): static + { + $this->_usedProperties['withTrace'] = true; + $this->withTrace = $value; + + return $this; + } + + public function __construct(array $value = []) + { + if (array_key_exists('enabled', $value)) { + $this->_usedProperties['enabled'] = true; + $this->enabled = $value['enabled']; + unset($value['enabled']); + } + + if (array_key_exists('with_trace', $value)) { + $this->_usedProperties['withTrace'] = true; + $this->withTrace = $value['with_trace']; + unset($value['with_trace']); + } + + if ([] !== $value) { + throw new InvalidConfigurationException(sprintf('The following keys are not supported by "%s": ', __CLASS__).implode(', ', array_keys($value))); + } + } + + public function toArray(): array + { + $output = []; + if (isset($this->_usedProperties['enabled'])) { + $output['enabled'] = $this->enabled; + } + if (isset($this->_usedProperties['withTrace'])) { + $output['with_trace'] = $this->withTrace; + } + + return $output; + } + +} diff --git a/src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues/Symfony/Config/ArrayValues/TransportsConfig.php b/src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues/Symfony/Config/ArrayValues/TransportsConfig.php new file mode 100644 index 0000000000000..c1ad5437834f8 --- /dev/null +++ b/src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues/Symfony/Config/ArrayValues/TransportsConfig.php @@ -0,0 +1,52 @@ +_usedProperties['dsn'] = true; + $this->dsn = $value; + + return $this; + } + + public function __construct(array $value = []) + { + if (array_key_exists('dsn', $value)) { + $this->_usedProperties['dsn'] = true; + $this->dsn = $value['dsn']; + unset($value['dsn']); + } + + if ([] !== $value) { + throw new InvalidConfigurationException(sprintf('The following keys are not supported by "%s": ', __CLASS__).implode(', ', array_keys($value))); + } + } + + public function toArray(): array + { + $output = []; + if (isset($this->_usedProperties['dsn'])) { + $output['dsn'] = $this->dsn; + } + + return $output; + } + +} diff --git a/src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues/Symfony/Config/ArrayValuesConfig.php b/src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues/Symfony/Config/ArrayValuesConfig.php new file mode 100644 index 0000000000000..818a14fcbcd9b --- /dev/null +++ b/src/Symfony/Component/Config/Tests/Builder/Fixtures/ArrayValues/Symfony/Config/ArrayValuesConfig.php @@ -0,0 +1,96 @@ +_usedProperties['transports'] = true; + $this->transports[$name] = $value; + + return $this; + } + + if (!isset($this->transports[$name]) || !$this->transports[$name] instanceof \Symfony\Config\ArrayValues\TransportsConfig) { + $this->_usedProperties['transports'] = true; + $this->transports[$name] = new \Symfony\Config\ArrayValues\TransportsConfig($value); + } elseif (1 < \func_num_args()) { + throw new InvalidConfigurationException('The node created by "transports()" has already been initialized. You cannot pass values the second time you call transports().'); + } + + return $this->transports[$name]; + } + + /** + * @default {"enabled":false} + */ + public function errorPages(array $value = []): \Symfony\Config\ArrayValues\ErrorPagesConfig + { + if (null === $this->errorPages) { + $this->_usedProperties['errorPages'] = true; + $this->errorPages = new \Symfony\Config\ArrayValues\ErrorPagesConfig($value); + } elseif (0 < \func_num_args()) { + throw new InvalidConfigurationException('The node created by "errorPages()" has already been initialized. You cannot pass values the second time you call errorPages().'); + } + + return $this->errorPages; + } + + public function getExtensionAlias(): string + { + return 'array_values'; + } + + public function __construct(array $value = []) + { + if (array_key_exists('transports', $value)) { + $this->_usedProperties['transports'] = true; + $this->transports = array_map(fn ($v) => \is_array($v) ? new \Symfony\Config\ArrayValues\TransportsConfig($v) : $v, $value['transports']); + unset($value['transports']); + } + + if (array_key_exists('error_pages', $value)) { + $this->_usedProperties['errorPages'] = true; + $this->errorPages = \is_array($value['error_pages']) ? new \Symfony\Config\ArrayValues\ErrorPagesConfig($value['error_pages']) : $value['error_pages']; + unset($value['error_pages']); + } + + if ([] !== $value) { + throw new InvalidConfigurationException(sprintf('The following keys are not supported by "%s": ', __CLASS__).implode(', ', array_keys($value))); + } + } + + public function toArray(): array + { + $output = []; + if (isset($this->_usedProperties['transports'])) { + $output['transports'] = array_map(fn ($v) => $v instanceof \Symfony\Config\ArrayValues\TransportsConfig ? $v->toArray() : $v, $this->transports); + } + if (isset($this->_usedProperties['errorPages'])) { + $output['error_pages'] = $this->errorPages instanceof \Symfony\Config\ArrayValues\ErrorPagesConfig ? $this->errorPages->toArray() : $this->errorPages; + } + + return $output; + } + +} diff --git a/src/Symfony/Component/Config/Tests/Builder/Fixtures/ScalarNormalizedTypes/Symfony/Config/ScalarNormalizedTypes/NestedConfig.php b/src/Symfony/Component/Config/Tests/Builder/Fixtures/ScalarNormalizedTypes/Symfony/Config/ScalarNormalizedTypes/NestedConfig.php index 2cc1fb3275e78..2b3f19e87fb6f 100644 --- a/src/Symfony/Component/Config/Tests/Builder/Fixtures/ScalarNormalizedTypes/Symfony/Config/ScalarNormalizedTypes/NestedConfig.php +++ b/src/Symfony/Component/Config/Tests/Builder/Fixtures/ScalarNormalizedTypes/Symfony/Config/ScalarNormalizedTypes/NestedConfig.php @@ -17,7 +17,7 @@ class NestedConfig private $_usedProperties = []; /** - * @template TValue + * @template TValue of mixed * @param TValue $value * @default {"enabled":null} * @return \Symfony\Config\ScalarNormalizedTypes\Nested\NestedObjectConfig|$this @@ -43,7 +43,7 @@ public function nestedObject(mixed $value = []): \Symfony\Config\ScalarNormalize } /** - * @template TValue + * @template TValue of mixed * @param TValue $value * @return \Symfony\Config\ScalarNormalizedTypes\Nested\NestedListObjectConfig|$this * @psalm-return (TValue is array ? \Symfony\Config\ScalarNormalizedTypes\Nested\NestedListObjectConfig : static) 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 1794ede72e18c..66107b8f19730 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 @@ -48,7 +48,7 @@ public function keyedArray(string $name, ParamConfigurator|string|array $value): } /** - * @template TValue + * @template TValue of mixed * @param TValue $value * @default {"enabled":null} * @return \Symfony\Config\ScalarNormalizedTypes\ObjectConfig|$this @@ -74,7 +74,7 @@ public function object(mixed $value = []): \Symfony\Config\ScalarNormalizedTypes } /** - * @template TValue + * @template TValue of mixed * @param TValue $value * @return \Symfony\Config\ScalarNormalizedTypes\ListObjectConfig|$this * @psalm-return (TValue is array ? \Symfony\Config\ScalarNormalizedTypes\ListObjectConfig : static) @@ -92,7 +92,7 @@ public function listObject(mixed $value = []): \Symfony\Config\ScalarNormalizedT } /** - * @template TValue + * @template TValue of mixed * @param TValue $value * @return \Symfony\Config\ScalarNormalizedTypes\KeyedListObjectConfig|$this * @psalm-return (TValue is array ? \Symfony\Config\ScalarNormalizedTypes\KeyedListObjectConfig : static) diff --git a/src/Symfony/Component/Config/Tests/Builder/GeneratedConfigTest.php b/src/Symfony/Component/Config/Tests/Builder/GeneratedConfigTest.php index c15e4776897ea..d722807043aea 100644 --- a/src/Symfony/Component/Config/Tests/Builder/GeneratedConfigTest.php +++ b/src/Symfony/Component/Config/Tests/Builder/GeneratedConfigTest.php @@ -62,6 +62,7 @@ public static function fixtureNames() 'AddToList' => 'add_to_list', 'NodeInitialValues' => 'node_initial_values', 'ArrayExtraKeys' => 'array_extra_keys', + 'ArrayValues' => 'array_values', ]; foreach ($array as $name => $alias) { 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