From 24fc166f8375e71316c80fbaff66586700d2326e Mon Sep 17 00:00:00 2001 From: RevZer0 <6ke6rn6@gmail.com> Date: Tue, 18 Aug 2020 14:17:42 +0300 Subject: [PATCH 1/6] [YAML] Unexpected characters using YAML compact syntax #37788 --- src/Symfony/Component/Yaml/Parser.php | 38 ++++++++++----- .../Component/Yaml/Tests/ParserTest.php | 47 +++++++++++++++++++ 2 files changed, 73 insertions(+), 12 deletions(-) diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 94af06cc3fd85..225a60db15746 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -36,6 +36,7 @@ class Parser private $skippedLineNumbers = []; private $locallySkippedLineNumbers = []; private $refsBeingParsed = []; + private $openMappingCount = 0; /** * Parses a YAML file into a PHP value. @@ -1208,18 +1209,12 @@ private function lexInlineMapping(string $yaml): string if ('' === $yaml || '{' !== $yaml[0]) { throw new \InvalidArgumentException(sprintf('"%s" is not a sequence.', $yaml)); } - - for ($i = 1; isset($yaml[$i]) && '}' !== $yaml[$i]; ++$i) { - } - - if (isset($yaml[$i]) && '}' === $yaml[$i]) { - return $yaml; - } - - $lines = [$yaml]; - - while ($this->moveToNextLine()) { - $lines[] = $this->currentLine; + $this->openMappingCount = 0; + $lines = [ + $this->calculateLineOpenMappings($yaml) + ]; + while (!$this->allMappingIsClosed() && $this->moveToNextLine()) { + $lines[] = $this->calculateLineOpenMappings($this->currentLine); } return implode("\n", $lines); @@ -1253,4 +1248,23 @@ private function lexInlineSequence(string $yaml): string return $value; } + + private function allMappingIsClosed(): bool + { + return 0 === $this->openMappingCount; + } + + private function calculateLineOpenMappings(string $yamlLine): string + { + for ($i = 0; isset($yamlLine[$i]); ++$i) { + if ('{' === $yamlLine[$i]) { + ++$this->openMappingCount; + } + if ('}' === $yamlLine[$i]) { + --$this->openMappingCount; + } + } + + return trim($yamlLine); + } } diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 8d278d91023e1..70878252b90df 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -2414,6 +2414,53 @@ public function testParseValueWithNegativeModifiers() $this->parser->parse($yaml) ); } + + /** + * Test that covers #37788 issue + * + * @dataProvider validMappingSequenceProvider + */ + public function testParametersAfterMappingSequence(string $yaml, array $parsed): void + { + self::assertSame($parsed, $this->parser->parse($yaml)); + } + + public function validMappingSequenceProvider(): iterable + { + $expected = [ + 'map' => [ + 'key' => 'value', + 'a' => 'b' + ], + 'param' => 'some' + ]; + + yield "multiline syntax" => [ + << [ + << [ + << Date: Tue, 18 Aug 2020 14:23:07 +0300 Subject: [PATCH 2/6] Fix coding standard issues --- src/Symfony/Component/Yaml/Parser.php | 2 +- .../Component/Yaml/Tests/ParserTest.php | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 225a60db15746..8e0bc86c8323a 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -1211,7 +1211,7 @@ private function lexInlineMapping(string $yaml): string } $this->openMappingCount = 0; $lines = [ - $this->calculateLineOpenMappings($yaml) + $this->calculateLineOpenMappings($yaml), ]; while (!$this->allMappingIsClosed() && $this->moveToNextLine()) { $lines[] = $this->calculateLineOpenMappings($this->currentLine); diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 70878252b90df..56a16e0b1f407 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -2416,7 +2416,7 @@ public function testParseValueWithNegativeModifiers() } /** - * Test that covers #37788 issue + * Test that covers #37788 issue. * * @dataProvider validMappingSequenceProvider */ @@ -2430,12 +2430,12 @@ public function validMappingSequenceProvider(): iterable $expected = [ 'map' => [ 'key' => 'value', - 'a' => 'b' + 'a' => 'b', ], - 'param' => 'some' + 'param' => 'some', ]; - yield "multiline syntax" => [ + yield 'multiline syntax' => [ << [ + yield 'inline syntax' => [ << [ + yield 'mixed syntax' => [ << Date: Tue, 18 Aug 2020 14:23:52 +0300 Subject: [PATCH 3/6] Fix Yaml component parser exception messages format --- src/Symfony/Component/Yaml/Parser.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 8e0bc86c8323a..42e84ff79f24d 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -218,7 +218,7 @@ private function doParse(string $value, int $flags) } if (!\is_string($key) && !\is_int($key)) { - throw new ParseException(sprintf('%s keys are not supported. Quote your evaluable mapping keys instead.', is_numeric($key) ? 'Numeric' : 'Non-string'), $this->getRealCurrentLineNb() + 1, $this->currentLine); + throw new ParseException(sprintf('"%s" keys are not supported. Quote your evaluable mapping keys instead.', is_numeric($key) ? 'Numeric' : 'Non-string'), $this->getRealCurrentLineNb() + 1, $this->currentLine); } // Convert float keys to strings, to avoid being converted to integers by PHP @@ -233,7 +233,7 @@ private function doParse(string $value, int $flags) $refName = substr(rtrim($values['value']), 1); if (!\array_key_exists($refName, $this->refs)) { if (false !== $pos = array_search($refName, $this->refsBeingParsed, true)) { - throw new ParseException(sprintf('Circular reference [%s, %s] detected for reference "%s".', implode(', ', \array_slice($this->refsBeingParsed, $pos)), $refName, $refName), $this->currentLineNb + 1, $this->currentLine, $this->filename); + throw new ParseException(sprintf('Circular reference [%s, "%s"] detected for reference "%s".', implode(', ', \array_slice($this->refsBeingParsed, $pos)), $refName, $refName), $this->currentLineNb + 1, $this->currentLine, $this->filename); } throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); @@ -709,7 +709,7 @@ private function parseValue(string $value, int $flags, string $context) if (!\array_key_exists($value, $this->refs)) { if (false !== $pos = array_search($value, $this->refsBeingParsed, true)) { - throw new ParseException(sprintf('Circular reference [%s, %s] detected for reference "%s".', implode(', ', \array_slice($this->refsBeingParsed, $pos)), $value, $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); + throw new ParseException(sprintf('Circular reference [%s, "%s"] detected for reference "%s".', implode(', ', \array_slice($this->refsBeingParsed, $pos)), $value, $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); } throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); From 9708c1ac9afcfcc083d8b03b4275aef40dc21800 Mon Sep 17 00:00:00 2001 From: RevZer0 <6ke6rn6@gmail.com> Date: Tue, 18 Aug 2020 14:29:13 +0300 Subject: [PATCH 4/6] Fix tests after exceptions format update --- src/Symfony/Component/Yaml/Tests/ParserTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 56a16e0b1f407..7fb1f8817fda1 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -1060,7 +1060,7 @@ public function testYamlDirective() public function testFloatKeys() { $this->expectException('Symfony\Component\Yaml\Exception\ParseException'); - $this->expectExceptionMessage('Numeric keys are not supported. Quote your evaluable mapping keys instead'); + $this->expectExceptionMessage('"Numeric" keys are not supported. Quote your evaluable mapping keys instead'); $yaml = <<<'EOF' foo: 1.2: "bar" @@ -1073,7 +1073,7 @@ public function testFloatKeys() public function testBooleanKeys() { $this->expectException('Symfony\Component\Yaml\Exception\ParseException'); - $this->expectExceptionMessage('Non-string keys are not supported. Quote your evaluable mapping keys instead'); + $this->expectExceptionMessage('"Non-string" keys are not supported. Quote your evaluable mapping keys instead'); $yaml = <<<'EOF' true: foo false: bar @@ -2251,7 +2251,7 @@ public function testEvalRefException() public function testDetectCircularReferences($yaml) { $this->expectException('Symfony\Component\Yaml\Exception\ParseException'); - $this->expectExceptionMessage('Circular reference [foo, bar, foo] detected'); + $this->expectExceptionMessage('Circular reference [foo, bar, "foo"] detected'); $this->parser->parse($yaml, Yaml::PARSE_CUSTOM_TAGS); } From 2670518cf6fe0480f0d36060bdc1752b74bf35eb Mon Sep 17 00:00:00 2001 From: RevZer0 <6ke6rn6@gmail.com> Date: Tue, 18 Aug 2020 14:34:02 +0300 Subject: [PATCH 5/6] Rollback exception formatting --- src/Symfony/Component/Yaml/Parser.php | 6 +++--- src/Symfony/Component/Yaml/Tests/ParserTest.php | 8 +++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 42e84ff79f24d..8e0bc86c8323a 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -218,7 +218,7 @@ private function doParse(string $value, int $flags) } if (!\is_string($key) && !\is_int($key)) { - throw new ParseException(sprintf('"%s" keys are not supported. Quote your evaluable mapping keys instead.', is_numeric($key) ? 'Numeric' : 'Non-string'), $this->getRealCurrentLineNb() + 1, $this->currentLine); + throw new ParseException(sprintf('%s keys are not supported. Quote your evaluable mapping keys instead.', is_numeric($key) ? 'Numeric' : 'Non-string'), $this->getRealCurrentLineNb() + 1, $this->currentLine); } // Convert float keys to strings, to avoid being converted to integers by PHP @@ -233,7 +233,7 @@ private function doParse(string $value, int $flags) $refName = substr(rtrim($values['value']), 1); if (!\array_key_exists($refName, $this->refs)) { if (false !== $pos = array_search($refName, $this->refsBeingParsed, true)) { - throw new ParseException(sprintf('Circular reference [%s, "%s"] detected for reference "%s".', implode(', ', \array_slice($this->refsBeingParsed, $pos)), $refName, $refName), $this->currentLineNb + 1, $this->currentLine, $this->filename); + throw new ParseException(sprintf('Circular reference [%s, %s] detected for reference "%s".', implode(', ', \array_slice($this->refsBeingParsed, $pos)), $refName, $refName), $this->currentLineNb + 1, $this->currentLine, $this->filename); } throw new ParseException(sprintf('Reference "%s" does not exist.', $refName), $this->getRealCurrentLineNb() + 1, $this->currentLine, $this->filename); @@ -709,7 +709,7 @@ private function parseValue(string $value, int $flags, string $context) if (!\array_key_exists($value, $this->refs)) { if (false !== $pos = array_search($value, $this->refsBeingParsed, true)) { - throw new ParseException(sprintf('Circular reference [%s, "%s"] detected for reference "%s".', implode(', ', \array_slice($this->refsBeingParsed, $pos)), $value, $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); + throw new ParseException(sprintf('Circular reference [%s, %s] detected for reference "%s".', implode(', ', \array_slice($this->refsBeingParsed, $pos)), $value, $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); } throw new ParseException(sprintf('Reference "%s" does not exist.', $value), $this->currentLineNb + 1, $this->currentLine, $this->filename); diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 7fb1f8817fda1..49e655a34162c 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -1060,7 +1060,7 @@ public function testYamlDirective() public function testFloatKeys() { $this->expectException('Symfony\Component\Yaml\Exception\ParseException'); - $this->expectExceptionMessage('"Numeric" keys are not supported. Quote your evaluable mapping keys instead'); + $this->expectExceptionMessage('Numeric keys are not supported. Quote your evaluable mapping keys instead'); $yaml = <<<'EOF' foo: 1.2: "bar" @@ -1073,7 +1073,7 @@ public function testFloatKeys() public function testBooleanKeys() { $this->expectException('Symfony\Component\Yaml\Exception\ParseException'); - $this->expectExceptionMessage('"Non-string" keys are not supported. Quote your evaluable mapping keys instead'); + $this->expectExceptionMessage('Non-string keys are not supported. Quote your evaluable mapping keys instead'); $yaml = <<<'EOF' true: foo false: bar @@ -2251,7 +2251,7 @@ public function testEvalRefException() public function testDetectCircularReferences($yaml) { $this->expectException('Symfony\Component\Yaml\Exception\ParseException'); - $this->expectExceptionMessage('Circular reference [foo, bar, "foo"] detected'); + $this->expectExceptionMessage('Circular reference [foo, bar, foo] detected'); $this->parser->parse($yaml, Yaml::PARSE_CUSTOM_TAGS); } @@ -2416,8 +2416,6 @@ public function testParseValueWithNegativeModifiers() } /** - * Test that covers #37788 issue. - * * @dataProvider validMappingSequenceProvider */ public function testParametersAfterMappingSequence(string $yaml, array $parsed): void From 80445f82f14d165058ec7942a730f1a73da34e83 Mon Sep 17 00:00:00 2001 From: RevZer0 <6ke6rn6@gmail.com> Date: Tue, 18 Aug 2020 15:22:04 +0300 Subject: [PATCH 6/6] Update tests so HEREDOC syntax became suitable on PHP < 7.3 versions --- .../Component/Yaml/Tests/ParserTest.php | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 49e655a34162c..b83c685321ce3 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -2432,32 +2432,27 @@ public function validMappingSequenceProvider(): iterable ], 'param' => 'some', ]; - - yield 'multiline syntax' => [ - << [ - << [$yaml, $expected]; + + $yaml = << [ - << [$yaml, $expected]; + + $yaml = << [$yaml, $expected]; } }