Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit6529b5a

Browse files
[Config] Allow using an enum FQCN withEnumNode
1 parent3918524 commit6529b5a

15 files changed

+337
-11
lines changed

‎src/Symfony/Component/Config/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CHANGELOG
66

77
* Add`ExprBuilder::ifFalse()`
88
* Add support for info on`ArrayNodeDefinition::canBeEnabled()` and`ArrayNodeDefinition::canBeDisabled()`
9+
* Allow using an enum FQCN with`EnumNode`
910

1011
7.2
1112
---

‎src/Symfony/Component/Config/Definition/Builder/EnumNodeDefinition.php

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
class EnumNodeDefinitionextends ScalarNodeDefinition
2222
{
2323
privatearray$values;
24+
privatestring$enumFqcn;
2425

2526
/**
2627
* @return $this
@@ -36,17 +37,37 @@ public function values(array $values): static
3637
return$this;
3738
}
3839

40+
/**
41+
* @param class-string<\UnitEnum> $enumFqcn
42+
*
43+
* @return $this
44+
*/
45+
publicfunctionenumFqcn(string$enumFqcn):static
46+
{
47+
if (!enum_exists($enumFqcn)) {
48+
thrownew \InvalidArgumentException(\sprintf('The enum class "%s" does not exist.',$enumFqcn));
49+
}
50+
51+
$this->enumFqcn =$enumFqcn;
52+
53+
return$this;
54+
}
55+
3956
/**
4057
* Instantiate a Node.
4158
*
4259
* @throws \RuntimeException
4360
*/
4461
protectedfunctioninstantiateNode():EnumNode
4562
{
46-
if (!isset($this->values)) {
47-
thrownew \RuntimeException('You must call ->values() on enum nodes.');
63+
if (!isset($this->values) && !isset($this->enumFqcn)) {
64+
thrownew \RuntimeException('You must call either ->values() or ->enumFqcn() on enum nodes.');
65+
}
66+
67+
if (isset($this->values) &&isset($this->enumFqcn)) {
68+
thrownew \RuntimeException('You must call either ->values() or ->enumFqcn() on enum nodes but not both.');
4869
}
4970

50-
returnnewEnumNode($this->name,$this->parent,$this->values,$this->pathSeparator);
71+
returnnewEnumNode($this->name,$this->parent,$this->values ?? [],$this->pathSeparator,$this->enumFqcn ??null);
5172
}
5273
}

‎src/Symfony/Component/Config/Definition/EnumNode.php

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,30 @@
2121
class EnumNodeextends ScalarNode
2222
{
2323
privatearray$values;
24+
private ?string$enumFqcn =null;
2425

25-
publicfunction__construct(?string$name, ?NodeInterface$parent =null,array$values = [],string$pathSeparator = BaseNode::DEFAULT_PATH_SEPARATOR)
26+
/**
27+
* @param class-string<\UnitEnum>|null $enumFqcn
28+
*/
29+
publicfunction__construct(?string$name, ?NodeInterface$parent =null,array$values = [],string$pathSeparator = BaseNode::DEFAULT_PATH_SEPARATOR, ?string$enumFqcn =null)
2630
{
27-
if (!$values) {
31+
if (!$values && !$enumFqcn) {
2832
thrownew \InvalidArgumentException('$values must contain at least one element.');
2933
}
3034

35+
if ($values &&$enumFqcn) {
36+
thrownew \InvalidArgumentException('$values or $enumFqcn cannot be both set.');
37+
}
38+
39+
if (null !==$enumFqcn) {
40+
if (!enum_exists($enumFqcn)) {
41+
thrownew \InvalidArgumentException(\sprintf('The "%s" enum does not exist.',$enumFqcn));
42+
}
43+
44+
$values =$enumFqcn::cases();
45+
$this->enumFqcn =$enumFqcn;
46+
}
47+
3148
foreach ($valuesas$value) {
3249
if (null ===$value ||\is_scalar($value)) {
3350
continue;
@@ -51,11 +68,20 @@ public function getValues(): array
5168
return$this->values;
5269
}
5370

71+
publicfunctiongetEnumFqcn(): ?string
72+
{
73+
return$this->enumFqcn;
74+
}
75+
5476
/**
5577
* @internal
5678
*/
5779
publicfunctiongetPermissibleValues(string$separator):string
5880
{
81+
if (is_subclass_of($this->enumFqcn, \BackedEnum::class)) {
82+
returnimplode($separator,array_column($this->enumFqcn::cases(),'value'));
83+
}
84+
5985
returnimplode($separator,array_unique(array_map(staticfunction (mixed$value):string {
6086
if (!$valueinstanceof \UnitEnum) {
6187
returnjson_encode($value);
@@ -78,13 +104,58 @@ protected function finalizeValue(mixed $value): mixed
78104
{
79105
$value =parent::finalizeValue($value);
80106

81-
if (!\in_array($value,$this->values,true)) {
82-
$ex =newInvalidConfigurationException(\sprintf('The value %s is not allowed for path "%s". Permissible values: %s',json_encode($value),$this->getPath(),$this->getPermissibleValues(',')));
83-
$ex->setPath($this->getPath());
107+
if (!$this->enumFqcn) {
108+
if (!\in_array($value,$this->values,true)) {
109+
throw$this->createInvalidValueException($value);
110+
}
84111

85-
throw$ex;
112+
return$value;
86113
}
87114

88-
return$value;
115+
if ($valueinstanceof$this->enumFqcn) {
116+
return$value;
117+
}
118+
119+
if (!is_subclass_of($this->enumFqcn, \BackedEnum::class)) {
120+
// value is not an instance of the enum, and the enum is not
121+
// backed, meaning no cast is possible
122+
throw$this->createInvalidValueException($value);
123+
}
124+
125+
if (\is_string($value) ||\is_int($value)) {
126+
try {
127+
$case =$this->enumFqcn::tryFrom($value);
128+
}catch (\TypeError) {
129+
thrownewInvalidConfigurationException(\sprintf('The value could not be cast to a case of the "%s" enum. Is the value the same type as the backing type of the enum?',$this->enumFqcn));
130+
}
131+
132+
if (null !==$case) {
133+
return$case;
134+
}
135+
}elseif ($valueinstanceof \UnitEnum && !$valueinstanceof$this->enumFqcn) {
136+
thrownewInvalidConfigurationException(\sprintf('The value should be part of the "%s" enum, got a value from the "%s" enum.',$this->enumFqcn,get_debug_type($value)));
137+
}
138+
139+
throw$this->createInvalidValueException($value);
140+
}
141+
142+
privatefunctioncreateInvalidValueException(mixed$value):InvalidConfigurationException
143+
{
144+
$displayValue =match (true) {
145+
\is_int($value) =>$value,
146+
\is_string($value) =>\sprintf('"%s"',$value),
147+
default =>\sprintf('of type "%s"',get_debug_type($value)),
148+
};
149+
150+
if ($this->enumFqcn) {
151+
$message =\sprintf('The value %s is not allowed for path "%s". Permissible values: %s (cases of the "%s" enum).',$displayValue,$this->getPath(),$this->getPermissibleValues(','),$this->enumFqcn);
152+
}else {
153+
$message =\sprintf('The value %s is not allowed for path "%s". Permissible values: %s.',$displayValue,$this->getPath(),$this->getPermissibleValues(','));
154+
}
155+
156+
$e =newInvalidConfigurationException($message);
157+
$e->setPath($this->getPath());
158+
159+
return$e;
89160
}
90161
}

‎src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes.config.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
returnstaticfunction (PrimitiveTypesConfig$config) {
1515
$config->booleanNode(true);
1616
$config->enumNode('foo');
17+
$config->fqcnEnumNode('bar');
18+
$config->fqcnUnitEnumNode(\Symfony\Component\Config\Tests\Fixtures\TestEnum::Bar);
1719
$config->floatNode(47.11);
1820
$config->integerNode(1337);
1921
$config->scalarNode('foobar');

‎src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes.output.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
return [
1313
'boolean_node' =>true,
1414
'enum_node' =>'foo',
15+
'fqcn_enum_node' =>'bar',
16+
'fqcn_unit_enum_node' => \Symfony\Component\Config\Tests\Fixtures\TestEnum::Bar,
1517
'float_node' =>47.11,
1618
'integer_node' =>1337,
1719
'scalar_node' =>'foobar',

‎src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
useSymfony\Component\Config\Definition\Builder\TreeBuilder;
1515
useSymfony\Component\Config\Definition\ConfigurationInterface;
16+
useSymfony\Component\Config\Tests\Fixtures\StringBackedTestEnum;
1617
useSymfony\Component\Config\Tests\Fixtures\TestEnum;
1718

1819
class PrimitiveTypesimplements ConfigurationInterface
@@ -25,6 +26,8 @@ public function getConfigTreeBuilder(): TreeBuilder
2526
->children()
2627
->booleanNode('boolean_node')->end()
2728
->enumNode('enum_node')->values(['foo','bar','baz', TestEnum::Bar])->end()
29+
->enumNode('fqcn_enum_node')->enumFqcn(StringBackedTestEnum::class)->end()
30+
->enumNode('fqcn_unit_enum_node')->enumFqcn(TestEnum::class)->end()
2831
->floatNode('float_node')->end()
2932
->integerNode('integer_node')->end()
3033
->scalarNode('scalar_node')->end()

‎src/Symfony/Component/Config/Tests/Builder/Fixtures/PrimitiveTypes/Symfony/Config/PrimitiveTypesConfig.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ class PrimitiveTypesConfig implements \Symfony\Component\Config\Builder\ConfigBu
1212
{
1313
private$booleanNode;
1414
private$enumNode;
15+
private$fqcnEnumNode;
16+
private$fqcnUnitEnumNode;
1517
private$floatNode;
1618
private$integerNode;
1719
private$scalarNode;
@@ -44,6 +46,32 @@ public function enumNode($value): static
4446
return$this;
4547
}
4648

49+
/**
50+
* @default null
51+
* @param ParamConfigurator|\Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum::Foo|\Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum::Bar $value
52+
* @return $this
53+
*/
54+
publicfunctionfqcnEnumNode($value):static
55+
{
56+
$this->_usedProperties['fqcnEnumNode'] =true;
57+
$this->fqcnEnumNode =$value;
58+
59+
return$this;
60+
}
61+
62+
/**
63+
* @default null
64+
* @param ParamConfigurator|\Symfony\Component\Config\Tests\Fixtures\TestEnum::Foo|\Symfony\Component\Config\Tests\Fixtures\TestEnum::Bar|\Symfony\Component\Config\Tests\Fixtures\TestEnum::Ccc $value
65+
* @return $this
66+
*/
67+
publicfunctionfqcnUnitEnumNode($value):static
68+
{
69+
$this->_usedProperties['fqcnUnitEnumNode'] =true;
70+
$this->fqcnUnitEnumNode =$value;
71+
72+
return$this;
73+
}
74+
4775
/**
4876
* @default null
4977
* @param ParamConfigurator|float $value
@@ -115,6 +143,18 @@ public function __construct(array $value = [])
115143
unset($value['enum_node']);
116144
}
117145

146+
if (array_key_exists('fqcn_enum_node',$value)) {
147+
$this->_usedProperties['fqcnEnumNode'] =true;
148+
$this->fqcnEnumNode =$value['fqcn_enum_node'];
149+
unset($value['fqcn_enum_node']);
150+
}
151+
152+
if (array_key_exists('fqcn_unit_enum_node',$value)) {
153+
$this->_usedProperties['fqcnUnitEnumNode'] =true;
154+
$this->fqcnUnitEnumNode =$value['fqcn_unit_enum_node'];
155+
unset($value['fqcn_unit_enum_node']);
156+
}
157+
118158
if (array_key_exists('float_node',$value)) {
119159
$this->_usedProperties['floatNode'] =true;
120160
$this->floatNode =$value['float_node'];
@@ -153,6 +193,12 @@ public function toArray(): array
153193
if (isset($this->_usedProperties['enumNode'])) {
154194
$output['enum_node'] =$this->enumNode;
155195
}
196+
if (isset($this->_usedProperties['fqcnEnumNode'])) {
197+
$output['fqcn_enum_node'] =$this->fqcnEnumNode;
198+
}
199+
if (isset($this->_usedProperties['fqcnUnitEnumNode'])) {
200+
$output['fqcn_unit_enum_node'] =$this->fqcnUnitEnumNode;
201+
}
156202
if (isset($this->_usedProperties['floatNode'])) {
157203
$output['float_node'] =$this->floatNode;
158204
}

‎src/Symfony/Component/Config/Tests/Definition/Builder/EnumNodeDefinitionTest.php

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
usePHPUnit\Framework\TestCase;
1515
useSymfony\Component\Config\Definition\Builder\EnumNodeDefinition;
16+
useSymfony\Component\Config\Tests\Fixtures\StringBackedTestEnum;
17+
useSymfony\Component\Config\Tests\Fixtures\TestEnum;
1618

1719
class EnumNodeDefinitionTestextends TestCase
1820
{
@@ -25,14 +27,34 @@ public function testWithOneValue()
2527
$this->assertEquals(['foo'],$node->getValues());
2628
}
2729

30+
publicfunctiontestWithUnitEnumFqcn()
31+
{
32+
$def =newEnumNodeDefinition('foo');
33+
$def->enumFqcn(TestEnum::class);
34+
35+
$node =$def->getNode();
36+
$this->assertEquals(TestEnum::class,$node->getEnumFqcn());
37+
}
38+
2839
publicfunctiontestNoValuesPassed()
2940
{
3041
$this->expectException(\RuntimeException::class);
31-
$this->expectExceptionMessage('You must call ->values() on enum nodes.');
42+
$this->expectExceptionMessage('You must calleither->values() or ->enumFqcn() on enum nodes.');
3243
$def =newEnumNodeDefinition('foo');
3344
$def->getNode();
3445
}
3546

47+
publicfunctiontestBothValuesAndEnumFqcnPassed()
48+
{
49+
$this->expectException(\RuntimeException::class);
50+
$this->expectExceptionMessage('You must call either ->values() or ->enumFqcn() on enum nodes but not both.');
51+
$def =newEnumNodeDefinition('foo');
52+
$def->values([123])
53+
->enumFqcn(StringBackedTestEnum::class);
54+
55+
$def->getNode();
56+
}
57+
3658
publicfunctiontestWithNoValues()
3759
{
3860
$this->expectException(\InvalidArgumentException::class);

‎src/Symfony/Component/Config/Tests/Definition/Dumper/XmlReferenceDumperTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ private function getConfigurationAsString()
4242
<!-- scalar-deprecated-with-message: Deprecated (Since vendor/package 1.1: Deprecation custom message for "scalar_deprecated_with_message" at "acme_root") -->
4343
<!-- enum-with-default: One of "this"; "that" -->
4444
<!-- enum: One of "this"; "that"; Symfony\Component\Config\Tests\Fixtures\TestEnum::Ccc -->
45+
<!-- enum-with-class: One of foo; bar -->
46+
<!-- 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 -->
4547
<!-- variable: Example: foo, bar -->
4648
<config
4749
boolean="true"
@@ -58,6 +60,8 @@ private function getConfigurationAsString()
5860
node-with-a-looong-name=""
5961
enum-with-default="this"
6062
enum=""
63+
enum-with-class=""
64+
unit-enum-with-class=""
6165
variable=""
6266
custom-node="true"
6367
>

‎src/Symfony/Component/Config/Tests/Definition/Dumper/YamlReferenceDumperTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ private function getConfigurationAsString(): string
103103
node_with_a_looong_name: ~
104104
enum_with_default: this # One of "this"; "that"
105105
enum: ~ # One of "this"; "that"; Symfony\Component\Config\Tests\Fixtures\TestEnum::Ccc
106+
enum_with_class: ~ # One of foo; bar
107+
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
106108
107109
# some info
108110
array:

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp