Skip to content

Commit 2c32330

Browse files
bug #62563 [Config] Fix array shape generation for backed enums (OskarStark)
This PR was squashed before being merged into the 7.4 branch. Discussion ---------- [Config] Fix array shape generation for backed enums | Q | A | ------------- | --- | Branch? | 7.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Issues | -- | License | MIT Spotted in symfony/ai#1013 ➡️ https://github.com/symfony/ai/actions/runs/19780810683/job/56680686767?pr=1013 It works for `redis`, but not for `postgres`, because `redis` is using `->values(Distance::cases())` and `postgres` is using `->enumFqcn(PostgresDistance::class)`: ### Redis #### Enum ```php enum Distance: string { use Comparable; case Cosine = 'COSINE'; case L2 = 'L2'; case Ip = 'IP'; } ``` #### Config ```php ->enumNode('distance') ->info('Distance metric to use for vector similarity search') ->values(Distance::cases()) ->defaultValue(Distance::Cosine) ->end() ``` #### RESULT ```php * redis?: array<string, array{ // Default: [] * connection_parameters?: mixed, // see https://github.com/phpredis/phpredis?tab=readme-ov-file#example-1 * client?: string, // a service id of a Redis client * index_name: string, * key_prefix?: string, // Default: "vector:" * distance?: \Symfony\AI\Store\Bridge\Redis\Distance::Cosine|\Symfony\AI\Store\Bridge\Redis\Distance::L2|\Symfony\AI\Store\Bridge\Redis\Distance::Ip, // Distance metric to use for vector similarity search // Default: "COSINE" * }>, ``` ### Postgres #### Enum ```php enum Distance: string { use Comparable; case Cosine = 'cosine'; case InnerProduct = 'inner_product'; case L1 = 'l1'; case L2 = 'l2'; public function getComparisonSign(): string { return match ($this) { self::Cosine => '<=>', self::InnerProduct => '<#>', self::L1 => '<+>', self::L2 => '<->', }; } } ``` #### Config ```php ->enumNode('distance') ->info('Distance metric to use for vector similarity search') ->enumFqcn(PostgresDistance::class) ->defaultValue(PostgresDistance::L2) ->end() ``` #### RESULT ```php * postgres?: array<string, array{ // Default: [] * dsn?: string, * username?: string, * password?: string, * table_name: string, * vector_field?: string, * distance?: cosine|inner_product|l1|l2, // Distance metric to use for vector similarity search // Default: "l2" * dbal_connection?: string, * }>, ``` you can see, that the result is different: FQCN: `distance?: \Symfony\AI\Store\Bridge\Redis\Distance::Cosine|\Symfony\AI\Store\Bridge\Redis\Distance::L2|\Symfony\AI\Store\Bridge\Redis\Distance::Ip,` vs. strings: `distance?: cosine|inner_product|l1|l2,` Why? Commits ------- 62422c43483 [Config] Fix array shape generation for backed enums
2 parents 4c407fe + 9b48b61 commit 2c32330

File tree

7 files changed

+18
-8
lines changed

7 files changed

+18
-8
lines changed

Definition/EnumNode.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,18 @@ public function getEnumFqcn(): ?string
7979
public function getPermissibleValues(string $separator, bool $trim = true): string
8080
{
8181
if (is_subclass_of($this->enumFqcn, \BackedEnum::class)) {
82-
return implode($separator, array_column($this->enumFqcn::cases(), 'value'));
82+
if (!$trim) {
83+
return 'value-of<\\'.$this->enumFqcn.'>'.$separator.'\\'.$this->enumFqcn;
84+
}
85+
86+
$values = array_column($this->enumFqcn::cases(), 'value');
87+
88+
return implode($separator, array_map(static fn ($value) => json_encode($value, \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE), $values));
8389
}
8490

8591
return implode($separator, array_unique(array_map(static function ($value) use ($trim) {
8692
if (!$value instanceof \UnitEnum) {
87-
return json_encode($value);
93+
return json_encode($value, \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE | \JSON_PRESERVE_ZERO_FRACTION);
8894
}
8995

9096
return $trim ? ltrim(var_export($value, true), '\\') : var_export($value, true);

Tests/Builder/Fixtures/PrimitiveTypes/Symfony/Config/PrimitiveTypesConfig.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public function enumNode($value): static
5353

5454
/**
5555
* @default null
56-
* @param ParamConfigurator|\Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum::Foo|\Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum::Bar $value
56+
* @param ParamConfigurator|\Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum::Foo|\Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum::BarBaz $value
5757
* @return $this
5858
* @deprecated since Symfony 7.4
5959
*/

Tests/Definition/ArrayShapeGeneratorTest.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
use Symfony\Component\Config\Definition\ScalarNode;
2626
use Symfony\Component\Config\Definition\StringNode;
2727
use Symfony\Component\Config\Definition\VariableNode;
28+
use Symfony\Component\Config\Tests\Fixtures\IntegerBackedTestEnum;
29+
use Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum;
2830

2931
class ArrayShapeGeneratorTest extends TestCase
3032
{
@@ -52,6 +54,8 @@ public static function provideNodes(): iterable
5254

5355
yield [$nullableBooleanNode, 'bool|null'];
5456
yield [new EnumNode('node', values: ['a', 'b']), '"a"|"b"'];
57+
yield [new EnumNode('node', enumFqcn: StringBackedTestEnum::class), 'value-of<\Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum>|\Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum'];
58+
yield [new EnumNode('node', enumFqcn: IntegerBackedTestEnum::class), 'value-of<\Symfony\Component\Config\Tests\Fixtures\IntegerBackedTestEnum>|\Symfony\Component\Config\Tests\Fixtures\IntegerBackedTestEnum'];
5559
yield [new ScalarNode('node'), 'scalar|null'];
5660
yield [new VariableNode('node'), 'mixed'];
5761

Tests/Definition/Dumper/XmlReferenceDumperTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ 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 -->
45+
<!-- enum-with-class: One of "foo"; "bar baz" -->
4646
<!-- 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 -->
4747
<!-- variable: Example: foo, bar -->
4848
<config

Tests/Definition/Dumper/YamlReferenceDumperTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ private function getConfigurationAsString(): string
102102
node_with_a_looong_name: ~
103103
enum_with_default: this # One of "this"; "that"
104104
enum: ~ # One of "this"; "that"; Symfony\Component\Config\Tests\Fixtures\TestEnum::Ccc
105-
enum_with_class: ~ # One of foo; bar
105+
enum_with_class: ~ # One of "foo"; "bar baz"
106106
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
107107
108108
# some info

Tests/Definition/EnumNodeTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ public function testFinalizeWithStringEnumFqcnWithWrongCase()
138138
$node = new EnumNode('foo', null, enumFqcn: StringBackedTestEnum::class);
139139

140140
$this->expectException(InvalidConfigurationException::class);
141-
$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)');
141+
$this->expectExceptionMessage('The value "qux" is not allowed for path "foo". Permissible values: "foo", "bar baz" (cases of the "Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum" enum)');
142142

143143
$node->finalize('qux');
144144
}
@@ -148,7 +148,7 @@ public function testFinalizeWithStringEnumFqcnWithIntegerCase()
148148
$node = new EnumNode('foo', null, enumFqcn: StringBackedTestEnum::class);
149149

150150
$this->expectException(InvalidConfigurationException::class);
151-
$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).');
151+
$this->expectExceptionMessage('The value 1 is not allowed for path "foo". Permissible values: "foo", "bar baz" (cases of the "Symfony\Component\Config\Tests\Fixtures\StringBackedTestEnum" enum).');
152152

153153
$node->finalize(1);
154154
}

Tests/Fixtures/StringBackedTestEnum.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
enum StringBackedTestEnum: string
66
{
77
case Foo = 'foo';
8-
case Bar = 'bar';
8+
case BarBaz = 'bar baz';
99
}

0 commit comments

Comments
 (0)