8000 [Yaml] Add a StringReader · symfony/symfony@6c187fc · GitHub
[go: up one dir, main page]

Skip to content

Commit 6c187fc

Browse files
committed
[Yaml] Add a StringReader
1 parent 904279e commit 6c187fc

File tree

4 files changed

+279
-88
lines changed

4 files changed

+279
-88
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Yaml\Exception;
13+
14+
/**
15+
* Exception thrown when performing an invalid operation on an empty container, such as removing an element.
16+
*
17+
* @author Ener-Getick <egetick@gmail.com>
18+
*/
19+
class UnderflowException extends \UnderflowException implements ExceptionInterface
20+
{
21+
}

src/Symfony/Component/Yaml/Inline.php

Lines changed: 72 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\Yaml\Exception\ParseException;
1515
use Symfony\Component\Yaml\Exception\DumpException;
16+
use Symfony\Component\Yaml\Util\StringReader;
1617

1718
/**
1819
* Inline implements a YAML parser/dumper for the YAML inline syntax.
@@ -91,18 +92,16 @@ public static function parse($value, $flags = 0, $references = array())
9192
mb_internal_encoding('ASCII');
9293
}
9394

95+
$reader = new StringReader($value);
9496
$i = 0;
95-
switch ($value[0]) {
96-
case '[':
97-
$result = self::parseSequence($value, $flags, $i, $references);
98-
++$i;
99-
break;
100-
case '{':
101-
$result = self::parseMapping($value, $flags, $i, $references);
102-
++$i;
103-
break;
104-
default:
105-
$result = self::parseScalar($value, $flags, null, array('"', "'"), $i, true, $references);
97+
if ($reader->eat('[')) {
98+
$result = self::parseSequence($value, $flags, $i, $references);
99+
++$i;
100+
} elseif ($reader->eat('{')) {
101+
$result = self::parseMapping($value, $flags, $i, $references);
102+
++$i;
103+
} else {
104+
$result = self::parseScalar($value, $flags, null, array('"', "'"), $i, true, $references);
106105
}
107106

108107
// some comments are allowed at the end
@@ -287,7 +286,8 @@ private static function dumpArray($value, $flags)
287286
*/
288287
public static function parseScalar($scalar, $flags = 0, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array())
289288
{
290-
if (in_array($scalar[$i], $stringDelimiters)) {
289+
$reader = new StringReader($scalar, $i);
290+
if ($reader->eatAny($stringDelimiters)) {
291291
// quoted scalar
292292
$output = self::parseQuotedScalar($scalar, $i);
293293

@@ -299,28 +299,29 @@ public static function parseScalar($scalar, $flags = 0, $delimiters = null, $str
299299
}
300300
} else {
301301
// "normal" string
302-
if (!$delimiters) {
302+
if (null === $delimiters) {
303303
$output = substr($scalar, $i);
304-
$i += strlen($output);
304+
$i += $reader->getRemainingByteCount();
305305

306306
// remove comments
307307
if (preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) {
308+
$reader = new StringReader($output, 0, $match[0][1]);
308309
$output = substr($output, 0, $match[0][1]);
309310
}
310-
} elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
311-
$output = $match[1];
311+
} elseif ('' !== $output = $reader->eatCSpan(implode('', $delimiters))) {
312+
$reader = new StringReader($output);
312313
$i += strlen($output);
313314
} else {
314315
throw new ParseException(sprintf('Malformed inline YAML string (%s).', $scalar));
315316
}
316317

317318
// a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >)
318-
if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0])) {
319-
throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]));
319+
if ($indicator = $reader->eatAny(array('@', '`', '|', '>'))) {
320+
throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $indicator));
320321
}
321322

322-
if ($output && '%' === $output[0]) {
323-
@trigger_error(sprintf('Not quoting the scalar "%s" starting with the "%%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.' , $output), E_USER_DEPRECATED);
323+
if ($reader->eat('%')) {
324+
@trigger_error(sprintf('Not quoting the scalar "%s" starting with the "%%" indicator character is deprecated since Symfony 3.1 and will throw a ParseException in 4.0.', $output), E_USER_DEPRECATED);
324325
}
325326

326327
if ($evaluate) {
@@ -535,17 +536,12 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = ar
535536
private static function evaluateScalar($scalar, $flags, $references = array())
536537
{
537538
$scalar = trim($scalar);
538-
$scalarLower = strtolower($scalar);
539-
540-
if (0 === strpos($scalar, '*')) {
541-
if (false !== $pos = strpos($scalar, '#')) {
542-
$value = substr($scalar, 1, $pos - 2);
543-
} else {
544-
$value = substr($scalar, 1);
545-
}
539+
$reader = new StringReader($scalar);
540+
if ($reader->eat('*')) {
541+
$value = $reader->eatCSpan(' #');
546542

547543
// an unquoted *
548-
if (false === $value || '' === $value) {
544+
if ('' === $value) {
549545
throw new ParseException('A reference must contain at least one character.');
550546
}
551547

@@ -556,59 +552,63 @@ private static function evaluateScalar($scalar, $flags, $references = array())
556552
return $references[$value];
557553
}
558554

555+
if ($reader->eat('!')) {
556+
if ($reader->eat('str')) {
557+
$reader->skip(1, true);
558+
559+
return $reader->readToFullConsumption();
560+
} elseif ($reader->eat(' ')) {
561+
return (int) self::parseScalar($reader->readToFullConsumption(), $flags);
562+
} elseif ($tag = $reader->eatAny(array('php/object:', '!php/object:'))) {
563+
if (self::$objectSupport) {
564+
if ('!php/object:' === $tag) {
565+
@trigger_error('The !!php/object tag to indicate dumped PHP objects is deprecated since version 3.1 and will be removed in 4.0. Use the !php/object tag instead.', E_USER_DEPRECATED);
566+
}
567+
568+
return unserialize($reader->readToFullConsumption());
569+
}
570+
571+
if (self::$exceptionOnInvalidType) {
572+
throw new ParseException('Object support when parsing a YAML file has been disabled.');
573+
}
574+
575+
return;
576+
} elseif ($reader->eat('php/const:')) {
577+
if (self::$constantSupport) {
578+
$constant = $reader->readToFullConsumption();
579+
if (defined($constant)) {
580+
return constant($constant);
581+
}
582+
583+
throw new ParseException(sprintf('The constant "%s" is not defined.', $constant));
584+
}
585+
if (self::$exceptionOnInvalidType) {
586+
throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Have you forgotten to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar));
587+
}
588+
589+
return;
590+
} elseif ($reader->eat('!float ')) {
591+
return (float) $reader->readToFullConsumption();
592+
} elseif ($reader->eat('!binary')) {
593+
$reader->skip(1);
594+
595+
return self::evaluateBinaryScalar($reader->readToFullConsumption());
596+
}
597+
}
598+
599+
$scalarLower = strtolower($scalar);
559600
switch (true) {
560601
case 'null' === $scalarLower:
561-
case '' === $scalar:
562602
case '~' === $scalar:
603+
case '' === $scalar:
563604
return;
564605
case 'true' === $scalarLower:
565606
return true;
566607
case 'false' === $scalarLower:
567608
return false;
568609
// Optimise for returning strings.
569-
case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || $scalar[0] === '!' || is_numeric($scalar[0]):
610+
case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || is_numeric($scalar[0]):
570611
switch (true) {
571-
case 0 === strpos($scalar, '!str'):
572-
return (string) substr($scalar, 5);
573-
case 0 === strpos($scalar, '! '):
574-
return (int) self::parseScalar(substr($scalar, 2), $flags);
575-
case 0 === strpos($scalar, '!php/object:'):
576-
if (self::$objectSupport) {
577-
return unserialize(substr($scalar, 12));
578-
}
579-
580-
if (self::$exceptionOnInvalidType) {
581-
throw new ParseException('Object support when parsing a YAML file has been disabled.');
582-
}
583-
584-
return;
585-
case 0 === strpos($scalar, '!!php/object:'):
586-
if (self::$objectSupport) {
587-
@trigger_error('The !!php/object tag to indicate dumped PHP objects is deprecated since version 3.1 and will be removed in 4.0. Use the !php/object tag instead.', E_USER_DEPRECATED);
588-
589-
return unserialize(substr($scalar, 13));
590-
}
591-
592-
if (self::$exceptionOnInvalidType) {
593-
throw new ParseException('Object support when parsing a YAML file has been disabled.');
594-
}
595-
596-
return;
597-
case 0 === strpos($scalar, '!php/const:'):
598-
if (self::$constantSupport) {
599-
if (defined($const = substr($scalar, 11))) {
600-
return constant($const);
601-
}
602-
603-
throw new ParseException(sprintf('The constant "%s" is not defined.', $const));
604-
}
605-
if (self::$exceptionOnInvalidType) {
606-
throw new ParseException(sprintf('The string "%s" could not be parsed as a constant. Have you forgotten to pass the "Yaml::PARSE_CONSTANT" flag to the parser?', $scalar));
607-
}
608-
609-
return;
610-
case 0 === strpos($scalar, '!!float '):
611-
return (float) substr($scalar, 8);
612612
case preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar):
613613
$scalar = str_replace('_', '', (string) $scalar);
614614
// omitting the break / return as integers are handled in the next case

src/Symfony/Component/Yaml/Unescaper.php

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Yaml;
1313

1414
use Symfony\Component\Yaml\Exception\ParseException;
15+
use Symfony\Component\Yaml\Util\StringReader;
1516

1617
/**
1718
* Unescaper encapsulates unescaping rules for single and double-quoted
@@ -23,11 +24,6 @@
2324
*/
2425
class Unescaper
2526
{
26-
/**
27-
* Regex fragment that matches an escaped character in a double quoted string.
28-
*/
29-
const REGEX_ESCAPED_CHARACTER = '\\\\(x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|.)';
30-
3127
/**
3228
* Unescapes a single quoted string.
3329
*
@@ -49,12 +45,18 @@ public function unescapeSingleQuotedString($value)
4945
*/
5046
public function unescapeDoubleQuotedString($value)
5147
{
52-
$callback = function ($match) {
53-
return $this->unescapeCharacter($match[0]);
54-
};
48+
$reader = new StringReader($value);
49+
$unescapedValue = '';
50+
while (true) {
51+
$unescapedValue .= $reader->eatCSpan('\\');
52+
if ($reader->eat('\\')) {
53+
$unescapedValue .= $this->unescapeCharacter($reader);
54+
} else {
55+
break;
56+
}
57+
}
5558

56-
// evaluate the string
57-
return preg_replace_callback('/'.self::REGEX_ESCAPED_CHARACTER.'/u', $callback, $value);
59+
return $unescapedValue;
5860
}
5961

6062
/**
@@ -64,9 +66,10 @@ public function unescapeDoubleQuotedString($value)
6466
*
6567
* @return string The unescaped character
6668
*/
67-
private function unescapeCharacter($value)
69+
private function unescapeCharacter(StringReader $reader)
6870
{
69-
switch ($value[1]) {
71+
$character = $reader->read(1);
72+
switch ($character) {
7073
case '0':
7174
return "\x0";
7275
case 'a':
@@ -108,13 +111,13 @@ private function unescapeCharacter($value)
108111
// U+2029 PARAGRAPH SEPARATOR
109112
return "\xE2\x80\xA9";
110113
case 'x':
111-
return self::utf8chr(hexdec(substr($value, 2, 2)));
114+
return self::utf8chr(hexdec($reader->read(2)));
112115
case 'u':
113-
return self::utf8chr(hexdec(substr($value, 2, 4)));
116+
return self::utf8chr(hexdec($reader->read(4)));
114117
case 'U':
115-
return self::utf8chr(hexdec(substr($value, 2, 8)));
118+
return self::utf8chr(hexdec($reader->read(8)));
116119
default:
117-
throw new ParseException(sprintf('Found unknown escape character "%s".', $value));
120+
throw new ParseException(sprintf('Found unknown escape character "\\%s".', $character));
118121
}
119122
}
120123

0 commit comments

Comments
 (0)
0