From 2b3b4bfe7dd496fdf94ba2691815ae59a9259929 Mon Sep 17 00:00:00 2001 From: Alexandre Daubois Date: Tue, 9 Jul 2024 11:31:02 +0200 Subject: [PATCH] [Config] Allow using an enum FQCN with `EnumNode` --- src/Symfony/Component/Config/CHANGELOG.md | 1 + .../Definition/Builder/EnumNodeDefinition.php | 27 +++- .../Component/Config/Definition/EnumNode.php | 82 +++++++++++- .../Fixtures/PrimitiveTypes.config.php | 2 + .../Fixtures/PrimitiveTypes.output.php | 2 + .../Tests/Builder/Fixtures/PrimitiveTypes.php | 3 + .../Symfony/Config/PrimitiveTypesConfig.php | 46 +++++++ .../Builder/EnumNodeDefinitionTest.php | 24 +++- .../Dumper/XmlReferenceDumperTest.php | 4 + .../Dumper/YamlReferenceDumperTest.php | 2 + .../Config/Tests/Definition/EnumNodeTest.php | 122 ++++++++++++++++++ .../Configuration/ExampleConfiguration.php | 3 + .../Tests/Fixtures/IntegerBackedTestEnum.php | 9 ++ .../Tests/Fixtures/StringBackedTestEnum.php | 9 ++ .../Tests/Fixtures/StringBackedTestEnum2.php | 9 ++ 15 files changed, 334 insertions(+), 11 deletions(-) create mode 100644 src/Symfony/Component/Config/Tests/Fixtures/IntegerBackedTestEnum.php create mode 100644 src/Symfony/Component/Config/Tests/Fixtures/StringBackedTestEnum.php create mode 100644 src/Symfony/Component/Config/Tests/Fixtures/StringBackedTestEnum2.php diff --git a/src/Symfony/Component/Config/CHANGELOG.md b/src/Symfony/Component/Config/CHANGELOG.md index 577fbbca53645..0a9a6c0e08372 100644 --- a/src/Symfony/Component/Config/CHANGELOG.md +++ b/src/Symfony/Component/Config/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * Add `ExprBuilder::ifFalse()` * Add support for info on `ArrayNodeDefinition::canBeEnabled()` and `ArrayNodeDefinition::canBeDisabled()` + * Allow using an enum FQCN with `EnumNode` 7.2 --- diff --git a/src/Symfony/Component/Config/Definition/Builder/EnumNodeDefinition.php b/src/Symfony/Component/Config/Definition/Builder/EnumNodeDefinition.php index 99f31812331c1..a020c5f278ee9 100644 --- a/src/Symfony/Component/Config/Definition/Builder/EnumNodeDefinition.php +++ b/src/Symfony/Component/Config/Definition/Builder/EnumNodeDefinition.php @@ -21,6 +21,7 @@ class EnumNodeDefinition extends ScalarNodeDefinition { private array $values; + private string $enumFqcn; /** * @return $this @@ -36,6 +37,22 @@ public function values(array $values): static return $this; } + /** + * @param class-string<\UnitEnum> $enumFqcn + * + * @return $this + */ + public function enumFqcn(string $enumFqcn): static + { + if (!enum_exists($enumFqcn)) { + throw new \InvalidArgumentException(\sprintf('The enum class "%s" does not exist.', $enumFqcn)); + } + + $this->enumFqcn = $enumFqcn; + + return $this; + } + /** * Instantiate a Node. * @@ -43,10 +60,14 @@ public function values(array $values): static */ protected function instantiateNode(): EnumNode { - if (!isset($this->values)) { - throw new \RuntimeException('You must call ->values() on enum nodes.'); + if (!isset($this->values) && !isset($this->enumFqcn)) { + throw new \RuntimeException('You must call either ->values() or ->enumFqcn() on enum nodes.'); + } + + if (isset($this->values) && isset($this->enumFqcn)) { + throw new \RuntimeException('You must call either ->values() or ->enumFqcn() on enum nodes but not both.'); } - return new EnumNode($this->name, $this->parent, $this->values, $this->pathSeparator); + return new EnumNode($this->name, $this->parent, $this->values ?? [], $this->pathSeparator, $this->enumFqcn ?? null); } } diff --git a/src/Symfony/Component/Config/Definition/EnumNode.php b/src/Symfony/Component/Config/Definition/EnumNode.php index b253406c8f874..401bb48fc6ed4 100644 --- a/src/Symfony/Component/Config/Definition/EnumNode.php +++ b/src/Symfony/Component/Config/Definition/EnumNode.php @@ -21,13 +21,30 @@ class EnumNode extends ScalarNode { private array $values; + private ?string $enumFqcn = null; - public function __construct(?string $name, ?NodeInterface $parent = null, array $values = [], string $pathSeparator = BaseNode::DEFAULT_PATH_SEPARATOR) + /** + * @param class-string<\UnitEnum>|null $enumFqcn + */ + public function __construct(?string $name, ?NodeInterface $parent = null, array $values = [], string $pathSeparator = BaseNode::DEFAULT_PATH_SEPARATOR, ?string $enumFqcn = null) { - if (!$values) { + if (!$values && !$enumFqcn) { throw new \InvalidArgumentException('$values must contain at least one element.'); } + if ($values && $enumFqcn) { + throw new \InvalidArgumentException('$values or $enumFqcn cannot be both set.'); + } + + if (null !== $enumFqcn) { + if (!enum_exists($enumFqcn)) { + throw new \InvalidArgumentException(\sprintf('The "%s" enum does not exist.', $enumFqcn)); + } + + $values = $enumFqcn::cases(); + $this->enumFqcn = $enumFqcn; + } + foreach ($values as $value) { if (null === $value || \is_scalar($value)) { continue; @@ -51,11 +68,20 @@ public function getValues(): array return $this->values; } + public function getEnumFqcn(): ?string + { + return $this->enumFqcn; + } + /** * @internal */ public function getPermissibleValues(string $separator): string { + if (is_subclass_of($this->enumFqcn, \BackedEnum::class)) { + return implode($separator, array_column($this->enumFqcn::cases(), 'value')); + } + return implode($separator, array_unique(array_map(static function (mixed $value): string { if (!$value instanceof \UnitEnum) { return json_encode($value); @@ -78,13 +104,55 @@ protected function finalizeValue(mixed $value): mixed { $value = parent::finalizeValue($value); - if (!\in_array($value, $this->values, true)) { - $ex = new InvalidConfigurationException(\sprintf('The value %s is not allowed for path "%s". Permissible values: %s', json_encode($value), $this->getPath(), $this->getPermissibleValues(', '))); - $ex->setPath($this->getPath()); + if (!$this->enumFqcn) { + if (!\in_array($value, $this->values, true)) { + throw $this->createInvalidValueException($value); + } - throw $ex; + return $value; } - return $value; + if ($value instanceof $this->enumFqcn) { + return $value; + } + + if (!is_subclass_of($this->enumFqcn, \BackedEnum::class)) { + // value is not an instance of the enum, and the enum is not + // backed, meaning no cast is possible + throw $this->createInvalidValueException($value); + } + + if ($value instanceof \UnitEnum && !$value instanceof $this->enumFqcn) { + throw new InvalidConfigurationException(\sprintf('The value should be part of the "%s" enum, got a value from the "%s" enum.', $this->enumFqcn, get_debug_type($value))); + } + + if (!\is_string($value) && !\is_int($value)) { + throw new InvalidConfigurationException(\sprintf('Only strings and integers can be cast to a case of the "%s" enum, got value of type "%s".', $this->enumFqcn, get_debug_type($value))); + } + + try { + return $this->enumFqcn::from($value); + } catch (\TypeError|\ValueError) { + throw $this->createInvalidValueException($value); + } + } + + private function createInvalidValueException(mixed $value): InvalidConfigurationException + { + $displayValue = match (true) { + \is_int($value) => $value, + \is_string($value) => \sprintf('"%s"', $value), + default => \sprintf('of type "%s"', get_debug_type($value)), + }; + + $message = \sprintf('The value %s is not allowed for path "%s". Permissible values: %s.', $displayValue, $this->getPath(), $this->getPermissibleValues(', ')); + if ($this->enumFqcn) { + $message = substr_replace($message, \sprintf(' (cases of the "%s" enum)', $this->enumFqcn), -1, 0); + } + + $e = new InvalidConfigurationException($message); + $e->setPath($this->getPath()); + + return $e; } } diff --git a/src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes.config.php b/src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes.config.php index fd4c0ce5f2cfa..39ae3a144e4f1 100644 --- a/src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes.config.php +++ b/src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes.config.php @@ -14,6 +14,8 @@ return static function (PrimitiveTypesConfig $config) { $config->booleanNode(true); $config->enumNode('foo'); + $config->fqcnEnumNode('bar'); + $config->fqcnUnitEnumNode(\Symfony\Component\Config\Tests\Fixtures\TestEnum::Bar); $config->floatNode(47.11); $config->integerNode(1337); $config->scalarNode('foobar'); diff --git a/src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes.output.php b/src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes.output.php index 867e387dbf3c2..377590b687a64 100644 --- a/src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes.output.php +++ b/src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes.output.php @@ -12,6 +12,8 @@ return [ 'boolean_node' => true, 'enum_node' => 'foo', + 'fqcn_enum_node' => 'bar', + 'fqcn_unit_enum_node' => \Symfony\Component\Config\Tests\Fixtures\TestEnum::Bar, 'float_node' => 47.11, 'integer_node' => 1337, 'scalar_node' => 'foobar', diff --git a/src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes.php b/src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes.php index 6b0dbd72fc921..35d3400d152d6 100644 --- a/src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes.php +++ b/src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes.php @@ -13,6 +13,7 @@ use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum; use Symfony\Component\Config\Tests\Fixtures\TestEnum; class PrimitiveTypes implements ConfigurationInterface @@ -25,6 +26,8 @@ public function getConfigTreeBuilder(): TreeBuilder ->children() ->booleanNode('boolean_node')->end() ->enumNode('enum_node')->values(['foo', 'bar', 'baz', TestEnum::Bar])->end() + ->enumNode('fqcn_enum_node')->enumFqcn(StringBackedTestEnum::class)->end() + ->enumNode('fqcn_unit_enum_node')->enumFqcn(TestEnum::class)->end() ->floatNode('float_node')->end() ->integerNode('integer_node')->end() ->scalarNode('scalar_node')->end() diff --git a/src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes/Symfony/Config/PrimitiveTypesConfig.php b/src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes/Symfony/Config/PrimitiveTypesConfig.php index b34e0413b8a23..4208b97dde1bc 100644 --- a/src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes/Symfony/Config/PrimitiveTypesConfig.php +++ b/src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes/Symfony/Config/PrimitiveTypesConfig.php @@ -12,6 +12,8 @@ class PrimitiveTypesConfig implements \Symfony\Component\Config\Builder\ConfigBu { private $booleanNode; private $enumNode; + private $fqcnEnumNode; + private $fqcnUnitEnumNode; private $floatNode; private $integerNode; private $scalarNode; @@ -44,6 +46,32 @@ public function enumNode($value): static return $this; } + /** + * @default null + * @param ParamConfigurator|\Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum::Foo|\Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum::Bar $value + * @return $this + */ + public function fqcnEnumNode($value): static + { + $this->_usedProperties['fqcnEnumNode'] = true; + $this->fqcnEnumNode = $value; + + return $this; + } + + /** + * @default null + * @param ParamConfigurator|\Symfony\Component\Config\Tests\Fixtures\TestEnum::Foo|\Symfony\Component\Config\Tests\Fixtures\TestEnum::Bar|\Symfony\Component\Config\Tests\Fixtures\TestEnum::Ccc $value + * @return $this + */ + public function fqcnUnitEnumNode($value): static + { + $this->_usedProperties['fqcnUnitEnumNode'] = true; + $this->fqcnUnitEnumNode = $value; + + return $this; + } + /** * @default null * @param ParamConfigurator|float $value @@ -115,6 +143,18 @@ public function __construct(array $value = []) unset($value['enum_node']); } + if (array_key_exists('fqcn_enum_node', $value)) { + $this->_usedProperties['fqcnEnumNode'] = true; + $this->fqcnEnumNode = $value['fqcn_enum_node']; + unset($value['fqcn_enum_node']); + } + + if (array_key_exists('fqcn_unit_enum_node', $value)) { + $this->_usedProperties['fqcnUnitEnumNode'] = true; + $this->fqcnUnitEnumNode = $value['fqcn_unit_enum_node']; + unset($value['fqcn_unit_enum_node']); + } + if (array_key_exists('float_node', $value)) { $this->_usedProperties['floatNode'] = true; $this->floatNode = $value['float_node']; @@ -153,6 +193,12 @@ public function toArray(): array if (isset($this->_usedProperties['enumNode'])) { $output['enum_node'] = $this->enumNode; } + if (isset($this->_usedProperties['fqcnEnumNode'])) { + $output['fqcn_enum_node'] = $this->fqcnEnumNode; + } + if (isset($this->_usedProperties['fqcnUnitEnumNode'])) { + $output['fqcn_unit_enum_node'] = $this->fqcnUnitEnumNode; + } if (isset($this->_usedProperties['floatNode'])) { $output['float_node'] = $this->floatNode; } diff --git a/src/Symfony/Component/Config/Tests/Definition/Builder/EnumNodeDefinitionTest.php b/src/Symfony/Component/Config/Tests/Definition/Builder/EnumNodeDefinitionTest.php index c75841afe9439..6ede6c4e9b9bc 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Builder/EnumNodeDefinitionTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Builder/EnumNodeDefinitionTest.php @@ -13,6 +13,8 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Config\Definition\Builder\EnumNodeDefinition; +use Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum; +use Symfony\Component\Config\Tests\Fixtures\TestEnum; class EnumNodeDefinitionTest extends TestCase { @@ -25,14 +27,34 @@ public function testWithOneValue() $this->assertEquals(['foo'], $node->getValues()); } + public function testWithUnitEnumFqcn() + { + $def = new EnumNodeDefinition('foo'); + $def->enumFqcn(TestEnum::class); + + $node = $def->getNode(); + $this->assertEquals(TestEnum::class, $node->getEnumFqcn()); + } + public function testNoValuesPassed() { $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage('You must call ->values() on enum nodes.'); + $this->expectExceptionMessage('You must call either ->values() or ->enumFqcn() on enum nodes.'); $def = new EnumNodeDefinition('foo'); $def->getNode(); } + public function testBothValuesAndEnumFqcnPassed() + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('You must call either ->values() or ->enumFqcn() on enum nodes but not both.'); + $def = new EnumNodeDefinition('foo'); + $def->values([123]) + ->enumFqcn(StringBackedTestEnum::class); + + $def->getNode(); + } + public function testWithNoValues() { $this->expectException(\InvalidArgumentException::class); diff --git a/src/Symfony/Component/Config/Tests/Definition/Dumper/XmlReferenceDumperTest.php b/src/Symfony/Component/Config/Tests/Definition/Dumper/XmlReferenceDumperTest.php index 84d9f596c1892..a2e5ba6f089b7 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Dumper/XmlReferenceDumperTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Dumper/XmlReferenceDumperTest.php @@ -42,6 +42,8 @@ private function getConfigurationAsString() + + diff --git a/src/Symfony/Component/Config/Tests/Definition/Dumper/YamlReferenceDumperTest.php b/src/Symfony/Component/Config/Tests/Definition/Dumper/YamlReferenceDumperTest.php index cb33603f6cbb0..534372bc6eace 100644 --- a/src/Symfony/Component/Config/Tests/Definition/Dumper/YamlReferenceDumperTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/Dumper/YamlReferenceDumperTest.php @@ -103,6 +103,8 @@ private function getConfigurationAsString(): string node_with_a_looong_name: ~ enum_with_default: this # One of "this"; "that" enum: ~ # One of "this"; "that"; Symfony\Component\Config\Tests\Fixtures\TestEnum::Ccc + enum_with_class: ~ # One of foo; bar + unit_enum_with_class: ~ # One of Symfony\Component\Config\Tests\Fixtures\TestEnum::Foo; Symfony\Component\Config\Tests\Fixtures\TestEnum::Bar; Symfony\Component\Config\Tests\Fixtures\TestEnum::Ccc # some info array: diff --git a/src/Symfony/Component/Config/Tests/Definition/EnumNodeTest.php b/src/Symfony/Component/Config/Tests/Definition/EnumNodeTest.php index 48bfc4895d1a4..6bfbfdfd4c113 100644 --- a/src/Symfony/Component/Config/Tests/Definition/EnumNodeTest.php +++ b/src/Symfony/Component/Config/Tests/Definition/EnumNodeTest.php @@ -14,6 +14,9 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\Config\Definition\EnumNode; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\Tests\Fixtures\IntegerBackedTestEnum; +use Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum; +use Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum2; use Symfony\Component\Config\Tests\Fixtures\TestEnum; use Symfony\Component\Config\Tests\Fixtures\TestEnum2; @@ -33,6 +36,20 @@ public function testConstructionWithNoValues() new EnumNode('foo', null, []); } + public function testConstructionWithBothValuesAndEnumFqcn() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('$values or $enumFqcn cannot be both set.'); + new EnumNode('foo', null, [1, 2], enumFqcn: StringBackedTestEnum::class); + } + + public function testConstructionWithInvlaidEnumFqcn() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('The "Symfony\Component\Config\Tests\Definition\InvalidEnum" enum does not exist.'); + new EnumNode('foo', null, enumFqcn: InvalidEnum::class); + } + public function testConstructionWithOneValue() { $node = new EnumNode('foo', null, ['foo']); @@ -61,6 +78,111 @@ public function testFinalizeWithInvalidValue() $node->finalize('foobar'); } + public function testFinalizeUnitEnumFqcnWithInvalidValue() + { + $node = new EnumNode('foo', null, enumFqcn: TestEnum::class); + + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('The value "foobar" is not allowed for path "foo". Permissible values: Symfony\Component\Config\Tests\Fixtures\TestEnum::Foo, Symfony\Component\Config\Tests\Fixtures\TestEnum::Bar, Symfony\Component\Config\Tests\Fixtures\TestEnum::Ccc (cases of the "Symfony\Component\Config\Tests\Fixtures\TestEnum" enum)'); + + $node->finalize('foobar'); + } + + public function testFinalizeWithStringEnumFqcn() + { + $node = new EnumNode('foo', null, enumFqcn: StringBackedTestEnum::class); + + $this->assertSame(StringBackedTestEnum::Foo, $node->finalize(StringBackedTestEnum::Foo)); + } + + public function testFinalizeWithIntegerEnumFqcn() + { + $node = new EnumNode('foo', null, enumFqcn: IntegerBackedTestEnum::class); + + $this->assertSame(IntegerBackedTestEnum::One, $node->finalize(IntegerBackedTestEnum::One)); + } + + public function testFinalizeWithUnitEnumFqcn() + { + $node = new EnumNode('foo', null, enumFqcn: TestEnum::class); + + $this->assertSame(TestEnum::Foo, $node->finalize(TestEnum::Foo)); + } + + public function testFinalizeAnotherEnumWithEnumFqcn() + { + $node = new EnumNode('foo', null, enumFqcn: StringBackedTestEnum::class); + + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('The value should be part of the "Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum" enum, got a value from the "Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum2" enum.'); + + $node->finalize(StringBackedTestEnum2::Foo); + } + + public function testFinalizeWithEnumFqcnWorksWithPlainString() + { + $node = new EnumNode('foo', null, enumFqcn: StringBackedTestEnum::class); + + $this->assertSame(StringBackedTestEnum::Foo, $node->finalize('foo')); + } + + public function testFinalizeWithEnumFqcnWorksWithInteger() + { + $node = new EnumNode('foo', null, enumFqcn: IntegerBackedTestEnum::class); + + $this->assertSame(IntegerBackedTestEnum::One, $node->finalize(1)); + } + + public function testFinalizeWithStringEnumFqcnWithWrongCase() + { + $node = new EnumNode('foo', null, enumFqcn: StringBackedTestEnum::class); + + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('The value "qux" is not allowed for path "foo". Permissible values: foo, bar (cases of the "Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum" enum)'); + + $node->finalize('qux'); + } + + public function testFinalizeWithStringEnumFqcnWithIntegerCase() + { + $node = new EnumNode('foo', null, enumFqcn: StringBackedTestEnum::class); + + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('The value 1 is not allowed for path "foo". Permissible values: foo, bar (cases of the "Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum" enum).'); + + $node->finalize(1); + } + + public function testFinalizeWithIntegerEnumFqcnWithWrongCase() + { + $node = new EnumNode('foo', null, enumFqcn: IntegerBackedTestEnum::class); + + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('The value 3 is not allowed for path "foo". Permissible values: 1, 2 (cases of the "Symfony\Component\Config\Tests\Fixtures\IntegerBackedTestEnum" enum).'); + + $node->finalize(3); + } + + public function testFinalizeWithIntegerEnumFqcnWithStringCase() + { + $node = new EnumNode('foo', null, enumFqcn: IntegerBackedTestEnum::class); + + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('The value "my string" is not allowed for path "foo". Permissible values: 1, 2 (cases of the "Symfony\Component\Config\Tests\Fixtures\IntegerBackedTestEnum" enum).'); + + $node->finalize('my string'); + } + + public function testFinalizeWithEnumFqcnWithWrongType() + { + $node = new EnumNode('foo', null, enumFqcn: StringBackedTestEnum::class); + + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('Only strings and integers can be cast to a case of the "Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum" enum, got value of type "bool".'); + + $node->finalize(true); + } + public function testWithPlaceHolderWithValidValue() { $node = new EnumNode('cookie_samesite', null, ['lax', 'strict', 'none']); diff --git a/src/Symfony/Component/Config/Tests/Fixtures/Configuration/ExampleConfiguration.php b/src/Symfony/Component/Config/Tests/Fixtures/Configuration/ExampleConfiguration.php index 9f62a684a38fa..ef5256bb2d8a0 100644 --- a/src/Symfony/Component/Config/Tests/Fixtures/Configuration/ExampleConfiguration.php +++ b/src/Symfony/Component/Config/Tests/Fixtures/Configuration/ExampleConfiguration.php @@ -13,6 +13,7 @@ use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum; use Symfony\Component\Config\Tests\Fixtures\TestEnum; class ExampleConfiguration implements ConfigurationInterface @@ -40,6 +41,8 @@ public function getConfigTreeBuilder(): TreeBuilder ->scalarNode('node_with_a_looong_name')->end() ->enumNode('enum_with_default')->values(['this', 'that'])->defaultValue('this')->end() ->enumNode('enum')->values(['this', 'that', TestEnum::Ccc])->end() + ->enumNode('enum_with_class')->enumFqcn(StringBackedTestEnum::class)->end() + ->enumNode('unit_enum_with_class')->enumFqcn(TestEnum::class)->end() ->arrayNode('array') ->info('some info') ->canBeUnset() diff --git a/src/Symfony/Component/Config/Tests/Fixtures/IntegerBackedTestEnum.php b/src/Symfony/Component/Config/Tests/Fixtures/IntegerBackedTestEnum.php new file mode 100644 index 0000000000000..50246352ddeac --- /dev/null +++ b/src/Symfony/Component/Config/Tests/Fixtures/IntegerBackedTestEnum.php @@ -0,0 +1,9 @@ +