From 1e7e7e0f9c751bf23504749edb2c294362e56a2c Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Thu, 18 Nov 2021 19:10:16 +0100 Subject: [PATCH] properly parse quoted strings tagged with !!str --- src/Symfony/Component/Yaml/Inline.php | 27 ++++++++++++------- .../Component/Yaml/Tests/InlineTest.php | 18 ++++++++++--- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index 22ca97b546528..cb68755404224 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -269,10 +269,11 @@ private static function dumpNull(int $flags): string * * @throws ParseException When malformed inline YAML string is parsed */ - public static function parseScalar(string $scalar, int $flags = 0, array $delimiters = null, int &$i = 0, bool $evaluate = true, array &$references = []) + public static function parseScalar(string $scalar, int $flags = 0, array $delimiters = null, int &$i = 0, bool $evaluate = true, array &$references = [], bool &$isQuoted = null) { if (\in_array($scalar[$i], ['"', "'"])) { // quoted scalar + $isQuoted = true; $output = self::parseQuotedScalar($scalar, $i); if (null !== $delimiters) { @@ -286,6 +287,8 @@ public static function parseScalar(string $scalar, int $flags = 0, array $delimi } } else { // "normal" string + $isQuoted = false; + if (!$delimiters) { $output = substr($scalar, $i); $i += \strlen($output); @@ -308,7 +311,7 @@ public static function parseScalar(string $scalar, int $flags = 0, array $delimi } if ($evaluate) { - $output = self::evaluateScalar($output, $flags, $references); + $output = self::evaluateScalar($output, $flags, $references, $isQuoted); } } @@ -320,7 +323,7 @@ public static function parseScalar(string $scalar, int $flags = 0, array $delimi * * @throws ParseException When malformed inline YAML string is parsed */ - private static function parseQuotedScalar(string $scalar, int &$i): string + private static function parseQuotedScalar(string $scalar, int &$i = 0): string { if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) { throw new ParseException(sprintf('Malformed inline YAML string: "%s".', substr($scalar, $i)), self::$parsedLineNumber + 1, $scalar, self::$parsedFilename); @@ -373,8 +376,7 @@ private static function parseSequence(string $sequence, int $flags, int &$i = 0, $value = self::parseMapping($sequence, $flags, $i, $references); break; default: - $isQuoted = \in_array($sequence[$i], ['"', "'"]); - $value = self::parseScalar($sequence, $flags, [',', ']'], $i, null === $tag, $references); + $value = self::parseScalar($sequence, $flags, [',', ']'], $i, null === $tag, $references, $isQuoted); // the value can be an array if a reference has been resolved to an array var if (\is_string($value) && !$isQuoted && false !== strpos($value, ': ')) { @@ -521,8 +523,7 @@ private static function parseMapping(string $mapping, int $flags, int &$i = 0, a } break; default: - $isValueQuoted = \in_array($mapping[$i], ['"', "'"]); - $value = self::parseScalar($mapping, $flags, [',', '}', "\n"], $i, null === $tag, $references); + $value = self::parseScalar($mapping, $flags, [',', '}', "\n"], $i, null === $tag, $references, $isValueQuoted); // Spec: Keys MUST be unique; first one wins. // Parser cannot abort this mapping earlier, since lines // are processed sequentially. @@ -561,8 +562,9 @@ private static function parseMapping(string $mapping, int $flags, int &$i = 0, a * * @throws ParseException when object parsing support was disabled and the parser detected a PHP object or when a reference could not be resolved */ - private static function evaluateScalar(string $scalar, int $flags, array &$references = []) + private static function evaluateScalar(string $scalar, int $flags, array &$references = [], bool &$isQuotedString = null) { + $isQuotedString = false; $scalar = trim($scalar); $scalarLower = strtolower($scalar); @@ -597,7 +599,14 @@ private static function evaluateScalar(string $scalar, int $flags, array &$refer case '!' === $scalar[0]: switch (true) { case 0 === strpos($scalar, '!!str '): - return (string) substr($scalar, 6); + $s = (string) substr($scalar, 6); + + if (\in_array($s[0] ?? '', ['"', "'"], true)) { + $isQuotedString = true; + $s = self::parseQuotedScalar($s); + } + + return $s; case 0 === strpos($scalar, '! '): return substr($scalar, 2); case 0 === strpos($scalar, '!php/object'): diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php index 2d09d678bc701..852d498b1772b 100644 --- a/src/Symfony/Component/Yaml/Tests/InlineTest.php +++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php @@ -906,21 +906,31 @@ public function ideographicSpaceProvider(): array ]; } + public function testParseSingleQuotedTaggedString() + { + $this->assertSame('foo', Inline::parse("!!str 'foo'")); + } + + public function testParseDoubleQuotedTaggedString() + { + $this->assertSame('foo', Inline::parse('!!str "foo"')); + } + public function testParseQuotedReferenceLikeStringsInMapping() { $yaml = <<assertSame(['foo' => '&foo', 'bar' => '&bar'], Inline::parse($yaml)); + $this->assertSame(['foo' => '&foo', 'bar' => '&bar', 'baz' => '&baz'], Inline::parse($yaml)); } public function testParseQuotedReferenceLikeStringsInSequence() { $yaml = <<assertSame(['&foo', '&bar'], Inline::parse($yaml)); + $this->assertSame(['&foo', '&bar', '&baz'], Inline::parse($yaml)); } }