fix lexing strings containing escaped quotation characters
This commit is contained in:
parent
b60bb6e2d6
commit
673b8e941a
@ -750,54 +750,54 @@ class Parser
|
|||||||
return Inline::parse($this->lexInlineSequence($cursor), $flags, $this->refs);
|
return Inline::parse($this->lexInlineSequence($cursor), $flags, $this->refs);
|
||||||
}
|
}
|
||||||
|
|
||||||
$quotation = '' !== $value && ('"' === $value[0] || "'" === $value[0]) ? $value[0] : null;
|
switch ($value[0] ?? '') {
|
||||||
|
case '"':
|
||||||
|
case "'":
|
||||||
|
$cursor = \strlen($this->currentLine) - \strlen($value);
|
||||||
|
$parsedValue = Inline::parse($this->lexInlineQuotedString($cursor), $flags, $this->refs);
|
||||||
|
|
||||||
// do not take following lines into account when the current line is a quoted single line value
|
if (isset($this->currentLine[$cursor]) && preg_replace('/\s*#.*$/A', '', substr($this->currentLine, $cursor))) {
|
||||||
if (null !== $quotation && self::preg_match('/^'.$quotation.'.*'.$quotation.'(\s*#.*)?$/', $value)) {
|
throw new ParseException(sprintf('Unexpected characters near "%s".', substr($this->currentLine, $cursor)));
|
||||||
return Inline::parse($value, $flags, $this->refs);
|
}
|
||||||
|
|
||||||
|
return $parsedValue;
|
||||||
|
default:
|
||||||
|
$lines = [];
|
||||||
|
|
||||||
|
while ($this->moveToNextLine()) {
|
||||||
|
// unquoted strings end before the first unindented line
|
||||||
|
if (0 === $this->getCurrentLineIndentation()) {
|
||||||
|
$this->moveToPreviousLine();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$lines[] = trim($this->currentLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($i = 0, $linesCount = \count($lines), $previousLineBlank = false; $i < $linesCount; ++$i) {
|
||||||
|
if ('' === $lines[$i]) {
|
||||||
|
$value .= "\n";
|
||||||
|
$previousLineBlank = true;
|
||||||
|
} elseif ($previousLineBlank) {
|
||||||
|
$value .= $lines[$i];
|
||||||
|
$previousLineBlank = false;
|
||||||
|
} else {
|
||||||
|
$value .= ' '.$lines[$i];
|
||||||
|
$previousLineBlank = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Inline::$parsedLineNumber = $this->getRealCurrentLineNb();
|
||||||
|
|
||||||
|
$parsedValue = Inline::parse($value, $flags, $this->refs);
|
||||||
|
|
||||||
|
if ('mapping' === $context && \is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) {
|
||||||
|
throw new ParseException('A colon cannot be used in an unquoted mapping value.', $this->getRealCurrentLineNb() + 1, $value, $this->filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $parsedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$lines = [];
|
|
||||||
|
|
||||||
while ($this->moveToNextLine()) {
|
|
||||||
// unquoted strings end before the first unindented line
|
|
||||||
if (null === $quotation && 0 === $this->getCurrentLineIndentation()) {
|
|
||||||
$this->moveToPreviousLine();
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$lines[] = trim($this->currentLine);
|
|
||||||
|
|
||||||
// quoted string values end with a line that is terminated with the quotation character
|
|
||||||
$escapedLine = str_replace(['\\\\', '\\"'], '', $this->currentLine);
|
|
||||||
if ('' !== $escapedLine && substr($escapedLine, -1) === $quotation) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for ($i = 0, $linesCount = \count($lines), $previousLineBlank = false; $i < $linesCount; ++$i) {
|
|
||||||
if ('' === $lines[$i]) {
|
|
||||||
$value .= "\n";
|
|
||||||
$previousLineBlank = true;
|
|
||||||
} elseif ($previousLineBlank) {
|
|
||||||
$value .= $lines[$i];
|
|
||||||
$previousLineBlank = false;
|
|
||||||
} else {
|
|
||||||
$value .= ' '.$lines[$i];
|
|
||||||
$previousLineBlank = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Inline::$parsedLineNumber = $this->getRealCurrentLineNb();
|
|
||||||
|
|
||||||
$parsedValue = Inline::parse($value, $flags, $this->refs);
|
|
||||||
|
|
||||||
if ('mapping' === $context && \is_string($parsedValue) && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) {
|
|
||||||
throw new ParseException('A colon cannot be used in an unquoted mapping value.', $this->getRealCurrentLineNb() + 1, $value, $this->filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $parsedValue;
|
|
||||||
} catch (ParseException $e) {
|
} catch (ParseException $e) {
|
||||||
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
|
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
|
||||||
$e->setSnippet($this->currentLine);
|
$e->setSnippet($this->currentLine);
|
||||||
@ -1154,8 +1154,13 @@ class Parser
|
|||||||
|
|
||||||
$previousLineWasNewline = true;
|
$previousLineWasNewline = true;
|
||||||
$previousLineWasTerminatedWithBackslash = false;
|
$previousLineWasTerminatedWithBackslash = false;
|
||||||
|
$lineNumber = 0;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
if (++$lineNumber > 1) {
|
||||||
|
$cursor += strspn($this->currentLine, ' ', $cursor);
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->isCurrentLineBlank()) {
|
if ($this->isCurrentLineBlank()) {
|
||||||
$value .= "\n";
|
$value .= "\n";
|
||||||
} elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) {
|
} elseif (!$previousLineWasNewline && !$previousLineWasTerminatedWithBackslash) {
|
||||||
|
@ -1570,6 +1570,54 @@ EOT;
|
|||||||
$this->assertSame(['foo' => 'bar baz foobar foo', 'bar' => 'baz'], $this->parser->parse($yaml));
|
$this->assertSame(['foo' => 'bar baz foobar foo', 'bar' => 'baz'], $this->parser->parse($yaml));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider escapedQuotationCharactersInQuotedStrings
|
||||||
|
*/
|
||||||
|
public function testParseQuotedStringContainingEscapedQuotationCharacters(string $yaml, array $expected)
|
||||||
|
{
|
||||||
|
$this->assertSame($expected, $this->parser->parse($yaml));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function escapedQuotationCharactersInQuotedStrings()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'single quoted string' => [
|
||||||
|
<<<YAML
|
||||||
|
entries:
|
||||||
|
- message: 'No emails received before timeout - Address: ''test@testemail.company.com''
|
||||||
|
Keyword: ''Your Order confirmation'' ttl: 50'
|
||||||
|
outcome: failed
|
||||||
|
YAML
|
||||||
|
,
|
||||||
|
[
|
||||||
|
'entries' => [
|
||||||
|
[
|
||||||
|
'message' => 'No emails received before timeout - Address: \'test@testemail.company.com\' Keyword: \'Your Order confirmation\' ttl: 50',
|
||||||
|
'outcome' => 'failed',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'double quoted string' => [
|
||||||
|
<<<YAML
|
||||||
|
entries:
|
||||||
|
- message: "No emails received before timeout - Address: \"test@testemail.company.com\"
|
||||||
|
Keyword: \"Your Order confirmation\" ttl: 50"
|
||||||
|
outcome: failed
|
||||||
|
YAML
|
||||||
|
,
|
||||||
|
[
|
||||||
|
'entries' => [
|
||||||
|
[
|
||||||
|
'message' => 'No emails received before timeout - Address: "test@testemail.company.com" Keyword: "Your Order confirmation" ttl: 50',
|
||||||
|
'outcome' => 'failed',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
public function testParseMultiLineString()
|
public function testParseMultiLineString()
|
||||||
{
|
{
|
||||||
$this->assertEquals("foo bar\nbaz", $this->parser->parse("foo\nbar\n\nbaz"));
|
$this->assertEquals("foo bar\nbaz", $this->parser->parse("foo\nbar\n\nbaz"));
|
||||||
|
Reference in New Issue
Block a user