Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: symfony/yaml
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v6.2.10
Choose a base ref
...
head repository: symfony/yaml
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v6.3.0
Choose a head ref

Commits on Dec 19, 2022

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    9d49876 View commit details
  2. feature #48127 [Yaml] Add flag to dump numeric key as string (alamira…

    …ult)
    
    This PR was squashed before being merged into the 6.3 branch.
    
    Discussion
    ----------
    
    [Yaml] Add flag to dump numeric key as string
    
    | Q             | A
    | ------------- | ---
    | Branch?       | 6.3
    | Bug fix?      | no
    | New feature?  | yes <!-- please update src/**/CHANGELOG.md files -->
    | Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
    | Tickets       | Fix #48061  <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead -->
    | License       | MIT
    | Doc PR        | symfony/symfony-docs#... <!-- required for new features -->
    
    Sometime Yaml keys must be always string. (OpenApi spec for example)
    
    This PR add support to dump int as string by using the `Yaml::DUMP_NUMERIC_KEY_AS_STRING` flag.
    
    Without flag (default)
    
    ```yaml
    200: foo
    ```
    
    With flag
    
    ```yaml
    '200': foo
    ```
    
    (Unrelated fabbot failure)
    
    Commits
    -------
    
    35890e1ca9 [Yaml] Add flag to dump numeric key as string
    derrabus committed Dec 19, 2022
    Copy the full SHA
    240ccc2 View commit details

Commits on Jan 1, 2023

  1. Merge branch '6.2' into 6.3

    * 6.2:
      Bump LICENSE year
      Bump LICENSE year
      Fix LICENSE year
      Bump license year to 2023
      Fix CS
      Fix CS
    fabpot committed Jan 1, 2023

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    f18e233 View commit details

Commits on Jan 11, 2023

  1. Merge branch '6.2' into 6.3

    * 6.2:
      Revert "minor #47721 [Notifier] Use local copy of stella-maris/clock when testing (nicolas-grekas)"
      [Yaml] Minor: Update Inline parse phpdoc
      [FrameworkBundle] Fix deprecation when accessing a "container.private" service from the test container
      [VarExporter] Fix signature of `Lazy*Trait::createLazy*()`
      [DependencyInjection] Fix dumping inlined withers
      [HttpClient] Move Http clients data collecting at a late level
      [Serializer] Fix SerializerInterface for PHPStan
      [DependencyInjection] Fix support for named arguments on non-autowired services
      [FrameworkBundle] restore call to addGlobalIgnoredName
      Allow EmailValidator 4
      Fix detecting mapping with one line annotations
      [Intl] Get emoji-test.txt from unicode.org
    nicolas-grekas committed Jan 11, 2023

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    8d6fc4d View commit details

Commits on Jan 13, 2023

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    9383748 View commit details
  2. minor #48793 Leverage arrow function syntax for closure (tigitz)

    This PR was merged into the 6.3 branch.
    
    Discussion
    ----------
    
    Leverage arrow function syntax for closure
    
    | Q             | A
    | ------------- | ---
    | Branch?       | 6.3
    | Bug fix?      | no
    | New feature?  | no <!-- please update src/**/CHANGELOG.md files -->
    | Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files -->
    | Tickets       | Fix #47658 <!-- prefix each issue number with "Fix #", no need to create an issue if none exists, explain below instead -->
    | License       | MIT
    | Doc PR        | <!-- required for new features -->
    
    Rationale in the RFC [here](https://wiki.php.net/rfc/arrow_functions_v2#introduction)
    
    It's also notable that using arrow function syntax rather than the classic one has been enforced in the past by symfony core member: symfony/symfony#48069 (comment)
    
    So this PR would be consistent.
    
    Commits
    -------
    
    f5802d3a2a Leverage arrow function syntax for closure
    nicolas-grekas committed Jan 13, 2023
    Copy the full SHA
    baf5e57 View commit details

Commits on Feb 2, 2023

  1. Merge branch '6.2' into 6.3

    * 6.2:
      Fix LICENSE year
      Fix LICENSE CI check
      [Notifier] Mention Prisma Media as backer of v6.2
      fixes retrieving multiple values for extra fields
      Clarifies when to update UPGRADE-7.0.md
      [String] Remove duplicates in fold maps
      fail with a meaningful error when a needed package is missing
      [6.2] Remove mentions of v6.1 backers
      [Translation] Crowdin renewed their sponsorship for v6.2
      [DependencyInjection] Fix combinatory explosion when autowiring union and intersection types
      Update license years (last time)
      [Tests] New iteration of removing `$this` occurrences in future static data providers
    fabpot committed Feb 2, 2023
    Copy the full SHA
    f06e75f View commit details

Commits on Feb 4, 2023

  1. Copy the full SHA
    eaa9afa View commit details
  2. feature #49164 [Yaml] Feature #48920 Allow milliseconds and microseco…

    …nds in dates (dustinwilson)
    
    This PR was squashed before being merged into the 6.3 branch.
    
    Discussion
    ----------
    
    [Yaml] Feature #48920  Allow milliseconds and microseconds in dates
    
    | Q             | A
    | ------------- | ---
    | Branch?       | 6.3
    | Bug fix?      | no
    | New feature?  | yes
    | Deprecations? | no
    | Tickets       | Feature #48920
    | License       | MIT
    | Doc PR        | forthcoming
    
    Allows Yaml to parse dates with milliseconds or microseconds and to dump them as well.
    
    Commits
    -------
    
    8afb4c7702 [Yaml] Feature #48920  Allow milliseconds and microseconds in dates
    fabpot committed Feb 4, 2023
    Copy the full SHA
    864837e View commit details

Commits on Feb 5, 2023

  1. Copy the full SHA
    8151e65 View commit details

Commits on Feb 7, 2023

  1. Merge branch '6.2' into 6.3

    * 6.2:
      minor #49253 [PHPUnit 10] Use `TestCase` suffix for abstract tests in `/Tests/` (OskarStark)
      [Intl] Generate all emoji short name returned by slack api
      [Serializer] Fix CsvEncoder decode on empty data
      [Tests] Migrate data providers to static ones
      stop using assertObjectHasAttribute()/assertObjectHasNotAttribute()
      [Cache] Fix Redis proxies
      [Dotenv] Fix phpdoc Dotenv
      [Config] Fix phpdoc nullable
      Replace deprecated/removed way to configure enabled_locales
      fix typo
      Fix some typos
    nicolas-grekas committed Feb 7, 2023
    Copy the full SHA
    a5e8304 View commit details

Commits on Feb 8, 2023

  1. Remove some dead code

    fancyweb authored and fabpot committed Feb 8, 2023
    Copy the full SHA
    20dfa1e View commit details

Commits on Feb 13, 2023

  1. Copy the full SHA
    a8161cc View commit details
  2. Add void return types

    wouterj authored and nicolas-grekas committed Feb 13, 2023

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    c5f3f54 View commit details

Commits on Feb 14, 2023

  1. Merge branch '6.2' into 6.3

    * 6.2:
      Fix merge
      Migrate to `static` data providers using `rector/rector`
    nicolas-grekas committed Feb 14, 2023
    Copy the full SHA
    17bb6ce View commit details
  2. Copy the full SHA
    55071ed View commit details

Commits on Apr 2, 2023

  1. Copy the full SHA
    e2bacb4 View commit details

Commits on Apr 8, 2023

  1. Copy the full SHA
    abb0f24 View commit details

Commits on Apr 21, 2023

  1. Copy the full SHA
    2e5a87f View commit details

Commits on Apr 24, 2023

  1. Copy the full SHA
    9c50aaf View commit details

Commits on Apr 28, 2023

  1. Merge branch '6.2' into 6.3

    * 6.2:
      Fix test class name
      trim(): Argument #1 () must be of type string, bool given
      Check if trace.curlCommand is defined in profiler
      [Dumper] Trim leading newlines when checking if value begins with a space
      [FrameworkBundle] Make service edges unique
      Fix the list of supported shells for completions in a phar
      Fix the usage of the zsh completion through the fpath discovery
    nicolas-grekas committed Apr 28, 2023
    Copy the full SHA
    a9a8337 View commit details
Showing with 294 additions and 43 deletions.
  1. +5 −0 CHANGELOG.md
  2. +17 −17 Command/LintCommand.php
  3. +4 −0 Dumper.php
  4. +7 −1 Exception/ParseException.php
  5. +14 −2 Inline.php
  6. +4 −4 Parser.php
  7. +1 −1 Tag/TaggedValue.php
  8. +0 −10 Tests/Command/LintCommandTest.php
  9. +136 −0 Tests/DumperTest.php
  10. +95 −2 Tests/InlineTest.php
  11. +9 −0 Tests/ParserTest.php
  12. +1 −3 Unescaper.php
  13. +1 −0 Yaml.php
  14. +0 −3 composer.json
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
CHANGELOG
=========

6.3
---

* Add support to dump int keys as strings by using the `Yaml::DUMP_NUMERIC_KEY_AS_STRING` flag

6.2
---

34 changes: 17 additions & 17 deletions Command/LintCommand.php
Original file line number Diff line number Diff line change
@@ -50,11 +50,14 @@ public function __construct(string $name = null, callable $directoryIteratorProv
$this->isReadableProvider = null === $isReadableProvider ? null : $isReadableProvider(...);
}

/**
* @return void
*/
protected function configure()
{
$this
->addArgument('filename', InputArgument::IS_ARRAY, 'A file, a directory or "-" for reading from STDIN')
->addOption('format', null, InputOption::VALUE_REQUIRED, 'The output format')
->addOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format ("%s")', implode('", "', $this->getAvailableFormatOptions())))
->addOption('exclude', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Path(s) to exclude')
->addOption('parse-tags', null, InputOption::VALUE_NEGATABLE, 'Parse custom tags', null)
->setHelp(<<<EOF
@@ -91,10 +94,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$this->format = $input->getOption('format');
$flags = $input->getOption('parse-tags');

if ('github' === $this->format && !class_exists(GithubActionReporter::class)) {
throw new \InvalidArgumentException('The "github" format is only available since "symfony/console" >= 5.3.');
}

if (null === $this->format) {
// Autodetect format according to CI environment
$this->format = class_exists(GithubActionReporter::class) && GithubActionReporter::isGithubActionEnvironment() ? 'github' : 'txt';
@@ -128,7 +127,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return $this->display($io, $filesInfo);
}

private function validate(string $content, int $flags, string $file = null)
private function validate(string $content, int $flags, string $file = null): array
{
$prevErrorHandler = set_error_handler(function ($level, $message, $file, $line) use (&$prevErrorHandler) {
if (\E_USER_DEPRECATED === $level) {
@@ -155,7 +154,7 @@ private function display(SymfonyStyle $io, array $files): int
'txt' => $this->displayTxt($io, $files),
'json' => $this->displayJson($io, $files),
'github' => $this->displayTxt($io, $files, true),
default => throw new InvalidArgumentException(sprintf('The format "%s" is not supported.', $this->format)),
default => throw new InvalidArgumentException(sprintf('Supported formats are "%s".', implode('", "', $this->getAvailableFormatOptions()))),
};
}

@@ -240,12 +239,10 @@ private function getParser(): Parser

private function getDirectoryIterator(string $directory): iterable
{
$default = function ($directory) {
return new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
\RecursiveIteratorIterator::LEAVES_ONLY
);
};
$default = fn ($directory) => new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($directory, \FilesystemIterator::SKIP_DOTS | \FilesystemIterator::FOLLOW_SYMLINKS),
\RecursiveIteratorIterator::LEAVES_ONLY
);

if (null !== $this->directoryIteratorProvider) {
return ($this->directoryIteratorProvider)($directory, $default);
@@ -256,9 +253,7 @@ private function getDirectoryIterator(string $directory): iterable

private function isReadable(string $fileOrDirectory): bool
{
$default = function ($fileOrDirectory) {
return is_readable($fileOrDirectory);
};
$default = is_readable(...);

if (null !== $this->isReadableProvider) {
return ($this->isReadableProvider)($fileOrDirectory, $default);
@@ -270,7 +265,12 @@ private function isReadable(string $fileOrDirectory): bool
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestOptionValuesFor('format')) {
$suggestions->suggestValues(['txt', 'json', 'github']);
$suggestions->suggestValues($this->getAvailableFormatOptions());
}
}

private function getAvailableFormatOptions(): array
{
return ['txt', 'json', 'github'];
}
}
4 changes: 4 additions & 0 deletions Dumper.php
Original file line number Diff line number Diff line change
@@ -66,6 +66,10 @@ public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags
$output .= "\n";
}

if (\is_int($key) && Yaml::DUMP_NUMERIC_KEY_AS_STRING & $flags) {
$key = (string) $key;
}

if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value) && str_contains($value, "\n") && !str_contains($value, "\r")) {
$blockIndentationIndicator = $this->getBlockIndentationIndicator($value);

8 changes: 7 additions & 1 deletion Exception/ParseException.php
Original file line number Diff line number Diff line change
@@ -51,6 +51,8 @@ public function getSnippet(): string

/**
* Sets the snippet of code near the error.
*
* @return void
*/
public function setSnippet(string $snippet)
{
@@ -71,6 +73,8 @@ public function getParsedFile(): string

/**
* Sets the filename where the error occurred.
*
* @return void
*/
public function setParsedFile(string $parsedFile)
{
@@ -89,6 +93,8 @@ public function getParsedLine(): int

/**
* Sets the line where the error occurred.
*
* @return void
*/
public function setParsedLine(int $parsedLine)
{
@@ -97,7 +103,7 @@ public function setParsedLine(int $parsedLine)
$this->updateRepr();
}

private function updateRepr()
private function updateRepr(): void
{
$this->message = $this->rawMessage;

16 changes: 14 additions & 2 deletions Inline.php
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ class Inline
private static bool $objectForMap = false;
private static bool $constantSupport = false;

public static function initialize(int $flags, int $parsedLineNumber = null, string $parsedFilename = null)
public static function initialize(int $flags, int $parsedLineNumber = null, string $parsedFilename = null): void
{
self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags);
self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags);
@@ -110,7 +110,11 @@ public static function dump(mixed $value, int $flags = 0): string

return self::dumpNull($flags);
case $value instanceof \DateTimeInterface:
return $value->format('c');
return $value->format(match (true) {
!$length = \strlen(rtrim($value->format('u'), '0')) => 'c',
$length < 4 => 'Y-m-d\TH:i:s.vP',
default => 'Y-m-d\TH:i:s.uP',
});
case $value instanceof \UnitEnum:
return sprintf('!php/const %s::%s', $value::class, $value->name);
case \is_object($value):
@@ -239,6 +243,10 @@ private static function dumpHashArray(array|\ArrayObject|\stdClass $value, int $
{
$output = [];
foreach ($value as $key => $val) {
if (\is_int($key) && Yaml::DUMP_NUMERIC_KEY_AS_STRING & $flags) {
$key = (string) $key;
}

$output[] = sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags));
}

@@ -708,6 +716,10 @@ private static function evaluateScalar(string $scalar, int $flags, array &$refer
return $time;
}

if ('' !== rtrim($time->format('u'), '0')) {
return (float) $time->format('U.u');
}

try {
if (false !== $scalar = $time->getTimestamp()) {
return $scalar;
8 changes: 4 additions & 4 deletions Parser.php
Original file line number Diff line number Diff line change
@@ -99,7 +99,7 @@ public function parse(string $value, int $flags = 0): mixed
return $data;
}

private function doParse(string $value, int $flags)
private function doParse(string $value, int $flags): mixed
{
$this->currentLineNb = -1;
$this->currentLine = '';
@@ -503,7 +503,7 @@ private function doParse(string $value, int $flags)
return empty($data) ? null : $data;
}

private function parseBlock(int $offset, string $yaml, int $flags)
private function parseBlock(int $offset, string $yaml, int $flags): mixed
{
$skippedLineNumbers = $this->skippedLineNumbers;

@@ -844,8 +844,8 @@ private function parseBlockScalar(string $style, string $chomping = '', int $ind

while (
$notEOF && (
$isCurrentLineBlank ||
self::preg_match($pattern, $this->currentLine, $matches)
$isCurrentLineBlank
|| self::preg_match($pattern, $this->currentLine, $matches)
)
) {
if ($isCurrentLineBlank && \strlen($this->currentLine) > $indentation) {
2 changes: 1 addition & 1 deletion Tag/TaggedValue.php
Original file line number Diff line number Diff line change
@@ -31,7 +31,7 @@ public function getTag(): string
return $this->tag;
}

public function getValue()
public function getValue(): mixed
{
return $this->value;
}
10 changes: 0 additions & 10 deletions Tests/Command/LintCommandTest.php
Original file line number Diff line number Diff line change
@@ -13,7 +13,6 @@

use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\CI\GithubActionReporter;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Tester\CommandCompletionTester;
@@ -68,11 +67,6 @@ public function testLintIncorrectFile()

public function testLintIncorrectFileWithGithubFormat()
{
if (!class_exists(GithubActionReporter::class)) {
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('The "github" format is only available since "symfony/console" >= 5.3.');
}

$incorrectContent = <<<YAML
foo:
bar
@@ -82,10 +76,6 @@ public function testLintIncorrectFileWithGithubFormat()

$tester->execute(['filename' => $filename, '--format' => 'github'], ['decorated' => false]);

if (!class_exists(GithubActionReporter::class)) {
return;
}

self::assertEquals(1, $tester->getStatusCode(), 'Returns 1 in case of error');
self::assertStringMatchesFormat('%A::error file=%s,line=2,col=0::Unable to parse at line 2 (near "bar")%A', trim($tester->getDisplay()));
}
136 changes: 136 additions & 0 deletions Tests/DumperTest.php
Original file line number Diff line number Diff line change
@@ -853,6 +853,96 @@ public function testDumpNullAsTilde()
$this->assertSame('{ foo: ~ }', $this->dumper->dump(['foo' => null], 0, 0, Yaml::DUMP_NULL_AS_TILDE));
}

/**
* @dataProvider getNumericKeyData
*/
public function testDumpInlineNumericKeyAsString(array $input, bool $inline, int $flags, string $expected)
{
$this->assertSame($expected, $this->dumper->dump($input, $inline ? 0 : 4, 0, $flags));
}

public static function getNumericKeyData()
{
yield 'Int key with flag inline' => [
[200 => 'foo'],
true,
Yaml::DUMP_NUMERIC_KEY_AS_STRING,
"{ '200': foo }",
];

yield 'Int key without flag inline' => [
[200 => 'foo'],
true,
0,
'{ 200: foo }',
];

$expected = <<<'YAML'
'200': foo

YAML;

yield 'Int key with flag' => [
[200 => 'foo'],
false,
Yaml::DUMP_NUMERIC_KEY_AS_STRING,
$expected,
];

$expected = <<<'YAML'
200: foo

YAML;

yield 'Int key without flag' => [
[200 => 'foo'],
false,
0,
$expected,
];

$expected = <<<'YAML'
- 200
- foo

YAML;

yield 'List array with flag' => [
[200, 'foo'],
false,
Yaml::DUMP_NUMERIC_KEY_AS_STRING,
$expected,
];

$expected = <<<'YAML'
'200': !number 5

YAML;

yield 'Int tagged value with flag' => [
[
200 => new TaggedValue('number', 5),
],
false,
Yaml::DUMP_NUMERIC_KEY_AS_STRING,
$expected,
];

$expected = <<<'YAML'
200: !number 5

YAML;

yield 'Int tagged value without flag' => [
[
200 => new TaggedValue('number', 5),
],
false,
0,
$expected,
];
}

public function testDumpIdeographicSpaces()
{
$expected = <<<YAML
@@ -868,6 +958,52 @@ public function testDumpIdeographicSpaces()
], 2));
}

/**
* @dataProvider getDateTimeData
*/
public function testDumpDateTime(array $input, string $expected)
{
$this->assertSame($expected, rtrim($this->dumper->dump($input, 1)));
}

public static function getDateTimeData()
{
yield 'Date without subsecond precision' => [
['date' => new \DateTimeImmutable('2023-01-24T01:02:03Z')],
'date: 2023-01-24T01:02:03+00:00',
];

yield 'Date with one digit for milliseconds' => [
['date' => new \DateTimeImmutable('2023-01-24T01:02:03.4Z')],
'date: 2023-01-24T01:02:03.400+00:00',
];

yield 'Date with two digits for milliseconds' => [
['date' => new \DateTimeImmutable('2023-01-24T01:02:03.45Z')],
'date: 2023-01-24T01:02:03.450+00:00',
];

yield 'Date with full milliseconds' => [
['date' => new \DateTimeImmutable('2023-01-24T01:02:03.456Z')],
'date: 2023-01-24T01:02:03.456+00:00',
];

yield 'Date with four digits for microseconds' => [
['date' => new \DateTimeImmutable('2023-01-24T01:02:03.4567Z')],
'date: 2023-01-24T01:02:03.456700+00:00',
];

yield 'Date with five digits for microseconds' => [
['date' => new \DateTimeImmutable('2023-01-24T01:02:03.45678Z')],
'date: 2023-01-24T01:02:03.456780+00:00',
];

yield 'Date with full microseconds' => [
['date' => new \DateTimeImmutable('2023-01-24T01:02:03.456789Z')],
'date: 2023-01-24T01:02:03.456789+00:00',
];
}

private function assertSameData($expected, $actual)
{
$this->assertEquals($expected, $actual);
97 changes: 95 additions & 2 deletions Tests/InlineTest.php
Original file line number Diff line number Diff line change
@@ -563,9 +563,10 @@ public static function getTestsForDump()
/**
* @dataProvider getTimestampTests
*/
public function testParseTimestampAsUnixTimestampByDefault(string $yaml, int $year, int $month, int $day, int $hour, int $minute, int $second)
public function testParseTimestampAsUnixTimestampByDefault(string $yaml, int $year, int $month, int $day, int $hour, int $minute, int $second, int $microsecond)
{
$this->assertSame(gmmktime($hour, $minute, $second, $month, $day, $year), Inline::parse($yaml));
$expectedDate = (new \DateTimeImmutable($yaml, new \DateTimeZone('UTC')))->format('U');
$this->assertSame($microsecond ? (float) "$expectedDate.$microsecond" : (int) $expectedDate, Inline::parse($yaml));
}

/**
@@ -617,6 +618,98 @@ public function testDumpDateTime($dateTime, $expected)
$this->assertSame($expected, Inline::dump($dateTime));
}

/**
* @dataProvider getNumericKeyData
*/
public function testDumpNumericKeyAsString(array|int $input, int $flags, string $expected)
{
$this->assertSame($expected, Inline::dump($input, $flags));
}

public static function getNumericKeyData()
{
yield 'Int with flag' => [
200,
Yaml::DUMP_NUMERIC_KEY_AS_STRING,
'200',
];

yield 'Int key with flag' => [
[200 => 'foo'],
Yaml::DUMP_NUMERIC_KEY_AS_STRING,
"{ '200': foo }",
];

yield 'Int value with flag' => [
[200 => 200],
Yaml::DUMP_NUMERIC_KEY_AS_STRING,
"{ '200': 200 }",
];

yield 'String key with flag' => [
['200' => 'foo'],
Yaml::DUMP_NUMERIC_KEY_AS_STRING,
"{ '200': foo }",
];

yield 'Mixed with flag' => [
[42 => 'a', 'b' => 'c', 'd' => 43],
Yaml::DUMP_NUMERIC_KEY_AS_STRING,
"{ '42': a, b: c, d: 43 }",
];

yield 'Auto-index with flag' => [
['a', 'b', 42],
Yaml::DUMP_NUMERIC_KEY_AS_STRING,
'[a, b, 42]',
];

yield 'Complex mixed array with flag' => [
[
42 => [
'foo' => 43,
44 => 'bar',
],
45 => 'baz',
46,
],
Yaml::DUMP_NUMERIC_KEY_AS_STRING,
"{ '42': { foo: 43, '44': bar }, '45': baz, '46': 46 }",
];

yield 'Int tagged value with flag' => [
[
'count' => new TaggedValue('number', 5),
],
Yaml::DUMP_NUMERIC_KEY_AS_STRING,
'{ count: !number 5 }',
];

yield 'Array tagged value with flag' => [
[
'user' => new TaggedValue('metadata', [
'john',
42,
]),
],
Yaml::DUMP_NUMERIC_KEY_AS_STRING,
'{ user: !metadata [john, 42] }',
];

$arrayObject = new \ArrayObject();
$arrayObject['foo'] = 'bar';
$arrayObject[42] = 'baz';
$arrayObject['baz'] = 43;

yield 'Object value with flag' => [
[
'user' => $arrayObject,
],
Yaml::DUMP_NUMERIC_KEY_AS_STRING | Yaml::DUMP_OBJECT_AS_MAP,
"{ user: { foo: bar, '42': baz, baz: 43 } }",
];
}

public function testDumpUnitEnum()
{
$this->assertSame("!php/const Symfony\Component\Yaml\Tests\Fixtures\FooUnitEnum::BAR", Inline::dump(FooUnitEnum::BAR));
9 changes: 9 additions & 0 deletions Tests/ParserTest.php
Original file line number Diff line number Diff line change
@@ -1541,6 +1541,15 @@ public static function getInvalidBinaryData()
];
}

public function testParseDateWithSubseconds()
{
$yaml = <<<'EOT'
date: 2002-12-14T01:23:45.670000Z
EOT;

$this->assertSameData(['date' => 1039829025.67], $this->parser->parse($yaml));
}

public function testParseDateAsMappingValue()
{
$yaml = <<<'EOT'
4 changes: 1 addition & 3 deletions Unescaper.php
Original file line number Diff line number Diff line change
@@ -45,9 +45,7 @@ public function unescapeSingleQuotedString(string $value): string
*/
public function unescapeDoubleQuotedString(string $value): string
{
$callback = function ($match) {
return $this->unescapeCharacter($match[0]);
};
$callback = fn ($match) => $this->unescapeCharacter($match[0]);

// evaluate the string
return preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u', $callback, $value);
1 change: 1 addition & 0 deletions Yaml.php
Original file line number Diff line number Diff line change
@@ -34,6 +34,7 @@ class Yaml
public const PARSE_CUSTOM_TAGS = 512;
public const DUMP_EMPTY_ARRAY_AS_SEQUENCE = 1024;
public const DUMP_NULL_AS_TILDE = 2048;
public const DUMP_NUMERIC_KEY_AS_STRING = 4096;

/**
* Parses a YAML file into a PHP value.
3 changes: 0 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
@@ -25,9 +25,6 @@
"conflict": {
"symfony/console": "<5.4"
},
"suggest": {
"symfony/console": "For validating YAML files using the lint command"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Yaml\\": "" },
"exclude-from-classmap": [