Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit c9a1c09

Browse filesBrowse files
RichardBradleyfabpot
authored andcommitted
#20411 fix Yaml parsing for very long quoted strings
1 parent 01a0250 commit c9a1c09
Copy full SHA for c9a1c09

File tree

3 files changed

+77
-39
lines changed
Filter options

3 files changed

+77
-39
lines changed

‎src/Symfony/Component/Yaml/Inline.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Yaml/Inline.php
+8-8Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@ public static function dump($value, $exceptionOnInvalidType = false, $objectSupp
149149
case Escaper::requiresDoubleQuoting($value):
150150
return Escaper::escapeWithDoubleQuotes($value);
151151
case Escaper::requiresSingleQuoting($value):
152-
case preg_match(self::getHexRegex(), $value):
153-
case preg_match(self::getTimestampRegex(), $value):
152+
case Parser::preg_match(self::getHexRegex(), $value):
153+
case Parser::preg_match(self::getTimestampRegex(), $value):
154154
return Escaper::escapeWithSingleQuotes($value);
155155
default:
156156
return $value;
@@ -242,10 +242,10 @@ public static function parseScalar($scalar, $delimiters = null, $stringDelimiter
242242
$i += strlen($output);
243243

244244
// remove comments
245-
if (preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) {
245+
if (Parser::preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) {
246246
$output = substr($output, 0, $match[0][1]);
247247
}
248-
} elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
248+
} elseif (Parser::preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
249249
$output = $match[1];
250250
$i += strlen($output);
251251
} else {
@@ -272,7 +272,7 @@ public static function parseScalar($scalar, $delimiters = null, $stringDelimiter
272272
*/
273273
private static function parseQuotedScalar($scalar, &$i)
274274
{
275-
if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
275+
if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
276276
throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i)));
277277
}
278278

@@ -520,16 +520,16 @@ private static function evaluateScalar($scalar, $references = array())
520520

521521
return '0' == $scalar[1] ? octdec($scalar) : (((string) $raw === (string) $cast) ? $cast : $raw);
522522
case is_numeric($scalar):
523-
case preg_match(self::getHexRegex(), $scalar):
523+
case Parser::preg_match(self::getHexRegex(), $scalar):
524524
return '0x' === $scalar[0].$scalar[1] ? hexdec($scalar) : (float) $scalar;
525525
case '.inf' === $scalarLower:
526526
case '.nan' === $scalarLower:
527527
return -log(0);
528528
case '-.inf' === $scalarLower:
529529
return log(0);
530-
case preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
530+
case Parser::preg_match('/^(-|\+)?[0-9,]+(\.[0-9]+)?$/', $scalar):
531531
return (float) str_replace(',', '', $scalar);
532-
case preg_match(self::getTimestampRegex(), $scalar):
532+
case Parser::preg_match(self::getTimestampRegex(), $scalar):
533533
$timeZone = date_default_timezone_get();
534534
date_default_timezone_set('UTC');
535535
$time = strtotime($scalar);

‎src/Symfony/Component/Yaml/Parser.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Yaml/Parser.php
+57-31Lines changed: 57 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public function __construct($offset = 0, $totalNumberOfLines = null, array $skip
6161
*/
6262
public function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false)
6363
{
64-
if (!preg_match('//u', $value)) {
64+
if (false === preg_match('//u', $value)) {
6565
throw new ParseException('The YAML value does not appear to be valid UTF-8.');
6666
}
6767
$this->currentLineNb = -1;
@@ -92,13 +92,13 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
9292
}
9393

9494
$isRef = $mergeNode = false;
95-
if (preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+?))?\s*$#u', $this->currentLine, $values)) {
95+
if (self::preg_match('#^\-((?P<leadspaces>\s+)(?P<value>.+))?$#u', rtrim($this->currentLine), $values)) {
9696
if ($context && 'mapping' == $context) {
9797
throw new ParseException('You cannot define a sequence item when in a mapping', $this->getRealCurrentLineNb() + 1, $this->currentLine);
9898
}
9999
$context = 'sequence';
100100

101-
if (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
101+
if (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
102102
$isRef = $matches['ref'];
103103
$values['value'] = $matches['value'];
104104
}
@@ -108,7 +108,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
108108
$data[] = $this->parseBlock($this->getRealCurrentLineNb() + 1, $this->getNextEmbedBlock(null, true), $exceptionOnInvalidType, $objectSupport, $objectForMap);
109109
} else {
110110
if (isset($values['leadspaces'])
111-
&& preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $values['value'], $matches)
111+
&& self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+))?$#u', rtrim($values['value']), $matches)
112112
) {
113113
// this is a compact notation element, add to next block and parse
114114
$block = $values['value'];
@@ -124,7 +124,10 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
124124
if ($isRef) {
125125
$this->refs[$isRef] = end($data);
126126
}
127-
} elseif (preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $this->currentLine, $values) && (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'")))) {
127+
} elseif (
128+
self::preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P<value>.+))?$#u', rtrim($this->currentLine), $values)
129+
&& (false === strpos($values['key'], ' #') || in_array($values['key'][0], array('"', "'")))
130+
) {
128131
if ($context && 'sequence' == $context) {
129132
throw new ParseException('You cannot define a mapping item when in a sequence', $this->currentLineNb + 1, $this->currentLine);
130133
}
@@ -203,7 +206,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
203206
}
204207
}
205208
}
206-
} elseif (isset($values['value']) && preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
209+
} elseif (isset($values['value']) && self::preg_match('#^&(?P<ref>[^ ]+) *(?P<value>.*)#u', $values['value'], $matches)) {
207210
$isRef = $matches['ref'];
208211
$values['value'] = $matches['value'];
209212
}
@@ -266,27 +269,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
266269
return $value;
267270
}
268271

269-
switch (preg_last_error()) {
270-
case PREG_INTERNAL_ERROR:
271-
$error = 'Internal PCRE error.';
272-
break;
273-
case PREG_BACKTRACK_LIMIT_ERROR:
274-
$error = 'pcre.backtrack_limit reached.';
275-
break;
276-
case PREG_RECURSION_LIMIT_ERROR:
277-
$error = 'pcre.recursion_limit reached.';
278-
break;
279-
case PREG_BAD_UTF8_ERROR:
280-
$error = 'Malformed UTF-8 data.';
281-
break;
282-
case PREG_BAD_UTF8_OFFSET_ERROR:
283-
$error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.';
284-
break;
285-
default:
286-
$error = 'Unable to parse.';
287-
}
288-
289-
throw new ParseException($error, $this->getRealCurrentLineNb() + 1, $this->currentLine);
272+
throw new ParseException('Unable to parse', $this->getRealCurrentLineNb() + 1, $this->currentLine);
290273
}
291274
}
292275

@@ -520,7 +503,7 @@ private function parseValue($value, $exceptionOnInvalidType, $objectSupport, $ob
520503
return $this->refs[$value];
521504
}
522505

523-
if (preg_match('/^'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) {
506+
if (self::preg_match('/^'.self::BLOCK_SCALAR_HEADER_PATTERN.'$/', $value, $matches)) {
524507
$modifiers = isset($matches['modifiers']) ? $matches['modifiers'] : '';
525508

526509
return $this->parseBlockScalar($matches['separator'], preg_replace('#\d+#', '', $modifiers), (int) abs($modifiers));
@@ -566,7 +549,7 @@ private function parseBlockScalar($style, $chomping = '', $indentation = 0)
566549

567550
// determine indentation if not specified
568551
if (0 === $indentation) {
569-
if (preg_match('/^ +/', $this->currentLine, $matches)) {
552+
if (self::preg_match('/^ +/', $this->currentLine, $matches)) {
570553
$indentation = strlen($matches[0]);
571554
}
572555
}
@@ -577,7 +560,7 @@ private function parseBlockScalar($style, $chomping = '', $indentation = 0)
577560
while (
578561
$notEOF && (
579562
$isCurrentLineBlank ||
580-
preg_match($pattern, $this->currentLine, $matches)
563+
self::preg_match($pattern, $this->currentLine, $matches)
581564
)
582565
) {
583566
if ($isCurrentLineBlank && strlen($this->currentLine) > $indentation) {
@@ -800,6 +783,49 @@ private function isStringUnIndentedCollectionItem()
800783
*/
801784
private function isBlockScalarHeader()
802785
{
803-
return (bool) preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine);
786+
return (bool) self::preg_match('~'.self::BLOCK_SCALAR_HEADER_PATTERN.'$~', $this->currentLine);
787+
}
788+
789+
/**
790+
* A local wrapper for `preg_match` which will throw a ParseException if there
791+
* is an internal error in the PCRE engine.
792+
*
793+
* This avoids us needing to check for "false" every time PCRE is used
794+
* in the YAML engine
795+
*
796+
* @throws ParseException on a PCRE internal error
797+
*
798+
* @see preg_last_error()
799+
*
800+
* @internal
801+
*/
802+
public static function preg_match($pattern, $subject, &$matches = null, $flags = 0, $offset = 0)
803+
{
804+
$ret = preg_match($pattern, $subject, $matches, $flags, $offset);
805+
if ($ret === false) {
806+
switch (preg_last_error()) {
807+
case PREG_INTERNAL_ERROR:
808+
$error = 'Internal PCRE error.';
809+
break;
810+
case PREG_BACKTRACK_LIMIT_ERROR:
811+
$error = 'pcre.backtrack_limit reached.';
812+
break;
813+
case PREG_RECURSION_LIMIT_ERROR:
814+
$error = 'pcre.recursion_limit reached.';
815+
break;
816+
case PREG_BAD_UTF8_ERROR:
817+
$error = 'Malformed UTF-8 data.';
818+
break;
819+
case PREG_BAD_UTF8_OFFSET_ERROR:
820+
$error = 'Offset doesn\'t correspond to the begin of a valid UTF-8 code point.';
821+
break;
822+
default:
823+
$error = 'Error.';
824+
}
825+
826+
throw new ParseException($error);
827+
}
828+
829+
return $ret;
804830
}
805831
}

‎src/Symfony/Component/Yaml/Tests/ParserTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Yaml/Tests/ParserTest.php
+12Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
class ParserTest extends \PHPUnit_Framework_TestCase
1818
{
19+
/** @var Parser */
1920
protected $parser;
2021

2122
protected function setUp()
@@ -1143,6 +1144,17 @@ public function parserThrowsExceptionWithCorrectLineNumberProvider()
11431144
),
11441145
);
11451146
}
1147+
1148+
public function testCanParseVeryLongValue()
1149+
{
1150+
$longStringWithSpaces = str_repeat('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ', 20000);
1151+
$trickyVal = array('x' => $longStringWithSpaces);
1152+
1153+
$yamlString = Yaml::dump($trickyVal);
1154+
$arrayFromYaml = $this->parser->parse($yamlString);
1155+
1156+
$this->assertEquals($trickyVal, $arrayFromYaml);
1157+
}
11461158
}
11471159

11481160
class B

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.