From 88b53a0fa0589940d1b6cc1faeedd82635b36ed8 Mon Sep 17 00:00:00 2001 From: Raffaele Carelle Date: Tue, 1 Oct 2024 11:07:21 +0200 Subject: [PATCH] [Config] Add `StringNode` --- src/Symfony/Component/Config/CHANGELOG.md | 3 ++ .../Builder/ArrayNodeDefinition.php | 5 ++ .../Config/Definition/Builder/NodeBuilder.php | 9 ++++ .../Builder/StringNodeDefinition.php | 34 +++++++++++++ .../Config/Definition/StringNode.php | 40 +++++++++++++++ .../Builder/ArrayNodeDefinitionTest.php | 6 +++ .../Definition/Builder/NodeBuilderTest.php | 9 ++++ .../Tests/Definition/StringNodeTest.php | 49 +++++++++++++++++++ 8 files changed, 155 insertions(+) create mode 100644 src/Symfony/Component/Config/Definition/Builder/StringNodeDefinition.php create mode 100644 src/Symfony/Component/Config/Definition/StringNode.php create mode 100644 src/Symfony/Component/Config/Tests/Definition/StringNodeTest.php diff --git a/src/Symfony/Component/Config/CHANGELOG.md b/src/Symfony/Component/Config/CHANGELOG.md index e60b76d3b1f00..e38639e4d1ae8 100644 --- a/src/Symfony/Component/Config/CHANGELOG.md +++ b/src/Symfony/Component/Config/CHANGELOG.md @@ -8,6 +8,9 @@ CHANGELOG * Generate a meta file in JSON format for resource tracking * Add `SkippingResourceChecker` * Add support for `defaultNull()` on `BooleanNode` + * Add `StringNode` and `StringNodeDefinition` + * Add `ArrayNodeDefinition::stringPrototype()` method + * Add `NodeBuilder::stringNode()` method 7.1 --- diff --git a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php index 9012ed5890318..6b75ba137a806 100644 --- a/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/ArrayNodeDefinition.php @@ -73,6 +73,11 @@ public function scalarPrototype(): ScalarNodeDefinition return $this->prototype('scalar'); } + public function stringPrototype(): StringNodeDefinition + { + return $this->prototype('string'); + } + public function booleanPrototype(): BooleanNodeDefinition { return $this->prototype('boolean'); diff --git a/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php b/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php index 94e83a967c63f..b3fa7bfd555a0 100644 --- a/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php +++ b/src/Symfony/Component/Config/Definition/Builder/NodeBuilder.php @@ -31,6 +31,7 @@ public function __construct() 'float' => FloatNodeDefinition::class, 'array' => ArrayNodeDefinition::class, 'enum' => EnumNodeDefinition::class, + 'string' => StringNodeDefinition::class, ]; } @@ -102,6 +103,14 @@ public function variableNode(string $name): VariableNodeDefinition return $this->node($name, 'variable'); } + /** + * Creates a child string node. + */ + public function stringNode(string $name): StringNodeDefinition + { + return $this->node($name, 'string'); + } + /** * Returns the parent node. */ diff --git a/src/Symfony/Component/Config/Definition/Builder/StringNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/StringNodeDefinition.php new file mode 100644 index 0000000000000..c86f1bfd7e14d --- /dev/null +++ b/src/Symfony/Component/Config/Definition/Builder/StringNodeDefinition.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition\Builder; + +use Symfony\Component\Config\Definition\StringNode; + +/** + * This class provides a fluent interface for defining a node. + * + * @author Raffaele Carelle + */ +class StringNodeDefinition extends ScalarNodeDefinition +{ + public function __construct(?string $name, ?NodeParentInterface $parent = null) + { + parent::__construct($name, $parent); + + $this->nullEquivalent = ''; + } + + protected function instantiateNode(): StringNode + { + return new StringNode($this->name, $this->parent, $this->pathSeparator); + } +} diff --git a/src/Symfony/Component/Config/Definition/StringNode.php b/src/Symfony/Component/Config/Definition/StringNode.php new file mode 100644 index 0000000000000..6687b8825be1b --- /dev/null +++ b/src/Symfony/Component/Config/Definition/StringNode.php @@ -0,0 +1,40 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Definition; + +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; + +/** + * This node represents a String value in the config tree. + * + * @author Raffaele Carelle + */ +class StringNode extends ScalarNode +{ + protected function validateType(mixed $value): void + { + if (!\is_string($value)) { + $ex = new InvalidTypeException(\sprintf('Invalid type for path "%s". Expected "string", but got "%s".', $this->getPath(), get_debug_type($value))); + if ($hint = $this->getInfo()) { + $ex->addHint($hint); + } + $ex->setPath($this->getPath()); + + throw $ex; + } + } + + protected function getValidPlaceholderTypes(): array + { + return ['string']; + } +} diff --git a/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php b/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php index 459e4fb611f0c..ca376ea8a79df 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Builder/ArrayNodeDefinitionTest.php @@ -267,6 +267,12 @@ public function testPrototypeBoolean() $this->assertEquals($node->prototype('boolean'), $node->booleanPrototype()); } + public function testPrototypeString() + { + $node = new ArrayNodeDefinition('root'); + $this->assertEquals($node->prototype('string'), $node->stringPrototype()); + } + public function testPrototypeInteger() { $node = new ArrayNodeDefinition('root'); diff --git a/src/Symfony/Component/Config/Tests/Definition/Builder/NodeBuilderTest.php b/src/Symfony/Component/Config/Tests/Definition/Builder/NodeBuilderTest.php index 1338c58a68721..bee64388c785b 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Builder/NodeBuilderTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Builder/NodeBuilderTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Config\Definition\Builder\FloatNodeDefinition; use Symfony\Component\Config\Definition\Builder\IntegerNodeDefinition; use Symfony\Component\Config\Definition\Builder\NodeBuilder as BaseNodeBuilder; +use Symfony\Component\Config\Definition\Builder\StringNodeDefinition; use Symfony\Component\Config\Definition\Builder\VariableNodeDefinition as BaseVariableNodeDefinition; class NodeBuilderTest extends TestCase @@ -86,6 +87,14 @@ public function testNumericNodeCreation() $node = $builder->floatNode('bar')->min(3.0)->max(5.0); $this->assertInstanceOf(FloatNodeDefinition::class, $node); } + + public function testStringNodeCreation() + { + $builder = new BaseNodeBuilder(); + + $node = $builder->stringNode('foo bar'); + $this->assertInstanceOf(StringNodeDefinition::class, $node); + } } class SomeNodeDefinition extends BaseVariableNodeDefinition diff --git a/src/Symfony/Component/Config/Tests/Definition/StringNodeTest.php b/src/Symfony/Component/Config/Tests/Definition/StringNodeTest.php new file mode 100644 index 0000000000000..ddc219d243657 --- /dev/null +++ b/src/Symfony/Component/Config/Tests/Definition/StringNodeTest.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Config\Tests\Definition; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Definition\Exception\InvalidTypeException; +use Symfony\Component\Config\Definition\StringNode; + +class StringNodeTest extends TestCase +{ + /** + * @testWith [""] + * ["valid string"] + */ + public function testNormalize(string $value) + { + $node = new StringNode('test'); + $this->assertSame($value, $node->normalize($value)); + } + + /** + * @testWith [null] + * [false] + * [true] + * [0] + * [1] + * [0.0] + * [0.1] + * [{}] + * [{"foo": "bar"}] + */ + public function testNormalizeThrowsExceptionOnInvalidValues($value) + { + $node = new StringNode('test'); + + $this->expectException(InvalidTypeException::class); + + $node->normalize($value); + } +}