Skip to content

Commit 150c4cb

Browse files
committed
feature #46771 [Yaml] Add support for !php/enum *->value syntax (nicolas-grekas)
This PR was merged into the 6.2 branch. Discussion ---------- [Yaml] Add support for `!php/enum *->value` syntax | Q | A | ------------- | --- | Branch? | 6.2 | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | Fix #44203 and the likes | License | MIT | Doc PR | - This PR allows one to use the following Yaml together with enums: ```yaml - !php/enum SomeEnum::Bar - !php/enum SomeEnum::Bar->value ``` The first line gets the `SomeEnum::Bar` instance. It's the same as writing `!php/const SomeEnum::Bar` but with an additional check that this is really an enum. The second line is the one that is really needed as it allows referencing the value backed by an enum, which is something that is not possible currently. Commits ------- f71e4ab614 [Yaml] Add support for `!php/enum *->value` syntax
2 parents 518ff4c + 03fba3f commit 150c4cb

File tree

4 files changed

+103
-2
lines changed

4 files changed

+103
-2
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
6.2
5+
---
6+
7+
* Add support for `!php/enum` and `!php/enum *->value`
8+
49
6.1
510
---
611

Inline.php

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ private static function parseMapping(string $mapping, int $flags, int &$i = 0, a
432432
throw new ParseException('Missing mapping key.', self::$parsedLineNumber + 1, $mapping);
433433
}
434434

435-
if ('!php/const' === $key) {
435+
if ('!php/const' === $key || '!php/enum' === $key) {
436436
$key .= ' '.self::parseScalar($mapping, $flags, [':'], $i, false);
437437
$key = self::evaluateScalar($key, $flags);
438438
}
@@ -623,6 +623,40 @@ private static function evaluateScalar(string $scalar, int $flags, array &$refer
623623
throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Did you forget to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
624624
}
625625

626+
return null;
627+
case str_starts_with($scalar, '!php/enum'):
628+
if (self::$constantSupport) {
629+
if (!isset($scalar[11])) {
630+
throw new ParseException('Missing value for tag "!php/enum".', self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
631+
}
632+
633+
$i = 0;
634+
$enum = self::parseScalar(substr($scalar, 10), 0, null, $i, false);
635+
if ($useValue = str_ends_with($enum, '->value')) {
636+
$enum = substr($enum, 0, -7);
637+
}
638+
if (!\defined($enum)) {
639+
throw new ParseException(sprintf('The enum "%s" is not defined.', $enum), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
640+
}
641+
642+
$value = \constant($enum);
643+
644+
if (!$value instanceof \UnitEnum) {
645+
throw new ParseException(sprintf('The string "%s" is not the name of a valid enum.', $enum), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
646+
}
647+
if (!$useValue) {
648+
return $value;
649+
}
650+
if (!$value instanceof \BackedEnum) {
651+
throw new ParseException(sprintf('The enum "%s" defines no value next to its name.', $enum), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
652+
}
653+
654+
return $value->value;
655+
}
656+
if (self::$exceptionOnInvalidType) {
657+
throw new ParseException(sprintf('The string "%s" could not be parsed as an enum. Did you forget to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename);
658+
}
659+
626660
return null;
627661
case str_starts_with($scalar, '!!float '):
628662
return (float) substr($scalar, 8);
@@ -703,7 +737,7 @@ private static function parseTag(string $value, int &$i, int $flags): ?string
703737
}
704738

705739
// Is followed by a scalar and is a built-in tag
706-
if ('' !== $tag && (!isset($value[$nextOffset]) || !\in_array($value[$nextOffset], ['[', '{'], true)) && ('!' === $tag[0] || 'str' === $tag || 'php/const' === $tag || 'php/object' === $tag)) {
740+
if ('' !== $tag && (!isset($value[$nextOffset]) || !\in_array($value[$nextOffset], ['[', '{'], true)) && ('!' === $tag[0] || \in_array($tag, ['str', 'php/const', 'php/enum', 'php/object'], true))) {
707741
// Manage in {@link self::evaluateScalar()}
708742
return null;
709743
}

Tests/Fixtures/FooBackedEnum.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace Symfony\Component\Yaml\Tests\Fixtures;
4+
5+
enum FooBackedEnum: string
6+
{
7+
case BAR = 'bar';
8+
}

Tests/InlineTest.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Symfony\Component\Yaml\Exception\ParseException;
1616
use Symfony\Component\Yaml\Inline;
1717
use Symfony\Component\Yaml\Tag\TaggedValue;
18+
use Symfony\Component\Yaml\Tests\Fixtures\FooBackedEnum;
1819
use Symfony\Component\Yaml\Tests\Fixtures\FooUnitEnum;
1920
use Symfony\Component\Yaml\Yaml;
2021

@@ -72,13 +73,45 @@ public function testParsePhpConstantThrowsExceptionWhenUndefined()
7273
Inline::parse('!php/const WRONG_CONSTANT', Yaml::PARSE_CONSTANT);
7374
}
7475

76+
public function testParsePhpEnumThrowsExceptionWhenUndefined()
77+
{
78+
$this->expectException(ParseException::class);
79+
$this->expectExceptionMessage('The enum "SomeEnum::Foo" is not defined');
80+
Inline::parse('!php/enum SomeEnum::Foo', Yaml::PARSE_CONSTANT);
81+
}
82+
83+
public function testParsePhpEnumThrowsExceptionWhenNotAnEnum()
84+
{
85+
$this->expectException(ParseException::class);
86+
$this->expectExceptionMessage('The string "PHP_INT_MAX" is not the name of a valid enum');
87+
Inline::parse('!php/enum PHP_INT_MAX', Yaml::PARSE_CONSTANT);
88+
}
89+
90+
public function testParsePhpEnumThrowsExceptionWhenNotBacked()
91+
{
92+
$this->expectException(ParseException::class);
93+
$this->expectExceptionMessage('The enum "Symfony\Component\Yaml\Tests\Fixtures\FooUnitEnum::BAR" defines no value next to its name');
94+
Inline::parse('!php/enum Symfony\Component\Yaml\Tests\Fixtures\FooUnitEnum::BAR->value', Yaml::PARSE_CONSTANT);
95+
}
96+
7597
public function testParsePhpConstantThrowsExceptionOnInvalidType()
7698
{
99+
$this->assertNull(Inline::parse('!php/const PHP_INT_MAX'));
100+
77101
$this->expectException(ParseException::class);
78102
$this->expectExceptionMessageMatches('#The string "!php/const PHP_INT_MAX" could not be parsed as a constant.*#');
79103
Inline::parse('!php/const PHP_INT_MAX', Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE);
80104
}
81105

106+
public function testParsePhpEnumThrowsExceptionOnInvalidType()
107+
{
108+
$this->assertNull(Inline::parse('!php/enum SomeEnum::Foo'));
109+
110+
$this->expectException(ParseException::class);
111+
$this->expectExceptionMessageMatches('#The string "!php/enum SomeEnum::Foo" could not be parsed as an enum.*#');
112+
Inline::parse('!php/enum SomeEnum::Foo', Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE);
113+
}
114+
82115
/**
83116
* @dataProvider getTestsForDump
84117
*/
@@ -589,6 +622,16 @@ public function testDumpUnitEnum()
589622
$this->assertSame("!php/const Symfony\Component\Yaml\Tests\Fixtures\FooUnitEnum::BAR", Inline::dump(FooUnitEnum::BAR));
590623
}
591624

625+
public function testParseUnitEnum()
626+
{
627+
$this->assertSame(FooUnitEnum::BAR, Inline::parse("!php/enum Symfony\Component\Yaml\Tests\Fixtures\FooUnitEnum::BAR", Yaml::PARSE_CONSTANT));
628+
}
629+
630+
public function testParseBackedEnumValue()
631+
{
632+
$this->assertSame(FooBackedEnum::BAR->value, Inline::parse("!php/enum Symfony\Component\Yaml\Tests\Fixtures\FooBackedEnum::BAR->value", Yaml::PARSE_CONSTANT));
633+
}
634+
592635
public function getDateTimeDumpTests()
593636
{
594637
$tests = [];
@@ -818,6 +861,17 @@ public function testPhpConstTagWithEmptyValue(string $value)
818861
Inline::parse($value, Yaml::PARSE_CONSTANT);
819862
}
820863

864+
/**
865+
* @dataProvider phpConstTagWithEmptyValueProvider
866+
*/
867+
public function testPhpEnumTagWithEmptyValue(string $value)
868+
{
869+
$this->expectException(ParseException::class);
870+
$this->expectExceptionMessage('Missing value for tag "!php/enum" at line 1 (near "!php/enum").');
871+
872+
Inline::parse(str_replace('!php/const', '!php/enum', $value), Yaml::PARSE_CONSTANT);
873+
}
874+
821875
public function phpConstTagWithEmptyValueProvider()
822876
{
823877
return [

0 commit comments

Comments
 (0)