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

Skip to content

Commit fe6901b

Browse files
committed
[Yaml] Add a StringReader
1 parent feb5413 commit fe6901b

File tree

6 files changed

+421
-166
lines changed

6 files changed

+421
-166
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: 93 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111

1212
namespace Symfony\Component\Yaml;
1313

14-
use Symfony\Component\Yaml\Exception\ParseException;
1514
use Symfony\Component\Yaml\Exception\DumpException;
15+
use Symfony\Component\Yaml\Exception\ParseException;
16+
use Symfony\Component\Yaml\Exception\UnderflowException;
17+
use Symfony\Component\Yaml\Util\StringReader;
1618

1719
/**
1820
* Inline implements a YAML parser/dumper for the YAML inline syntax.
@@ -93,18 + 23D3 95,16 @@ public static function parse($value, $flags = 0, $references = array())
9395
mb_internal_encoding('ASCII');
9496
}
9597

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

110110
// some comments are allowed at the end
@@ -222,8 +222,6 @@ public static function dump($value, $flags = 0)
222222
/**
223223
* Check if given array is hash or just normal indexed array.
224224
*
225-
* @internal
226-
*
227225
* @param array $value The PHP array to check
228226
*
229227
* @return bool true if value is hash array, false otherwise
@@ -284,44 +282,56 @@ private static function dumpArray($value, $flags)
284282
* @return string A YAML string
285283
*
286284
* @throws ParseException When malformed inline YAML string is parsed
287-
*
288-
* @internal
289285
*/
290-
public static function parseScalar($scalar, $flags = 0, $delimiters = null, $stringDelimiters = array('"', "'"), &$i = 0, $evaluate = true, $references = array())
286+
public static function parseScalar($scalar, $flags = 0, $delimiters = null, &$i = 0, $evaluate = true, $references = array())
291287
{
292-
if (in_array($scalar[$i], $stringDelimiters)) {
293-
// quoted scalar
294-
$output = self::parseQuotedScalar($scalar, $i);
288+
$reader = new StringReader($scalar, $i);
289+
if ($quote = $reader->eatAny(array('"', '\''))) {
290+
try {
291+
$output = $reader->transact(function () use ($reader, $quote) {
292+
$unescaper = new Unescaper();
293+
if ('"' === $quote) {
294+
return $unescaper->unescapeDoubleQuotedString($reader);
295+
} else {
296+
return $unescaper->unescapeSingleQuotedString($reader);
297+
}
298+
});
299+
} catch (UnderflowException $e) {
300+
throw new ParseException(sprintf('Malformed inline YAML string (%s).', $reader->readToFullConsumption()));
301+
}
302+
303+
$i = $reader->getOffset();
295304

296305
if (null !== $delimiters) {
297-
$tmp = ltrim(substr($scalar, $i), ' ');
298-
if (!in_array($tmp[0], $delimiters)) {
306+
$reader->eatSpan(' ');
307+
if (null === $reader->eatAny($delimiters)) {
299308
throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)));
300309
}
301310
}
302311
} else {
303312
// "normal" string
304-
if (!$delimiters) {
313+
if (null === $delimiters) {
305314
$output = substr($scalar, $i);
306-
$i += strlen($output);
315+
$i += $reader->getRemainingByteCount();
307316

308317
// remove comments
309318
if (preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) {
319+
$reader = new StringReader($output, 0, $match[0][1]);
310320
$output = substr($output, 0, $match[0][1]);
311321
}
312-
} elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
313-
$output = $match[1];
322+
} elseif ('' !== $output = $reader->eatCSpan(implode('', $delimiters))) {
323+
$reader = new StringReader($output);
314324
$i += strlen($output);
315325
} else {
316326
throw new ParseException(sprintf('Malformed inline YAML string (%s).', $scalar));
317327
}
318328

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

324-
if ($output && '%' === $output[0]) {
334+
if ($reader->eat('%')) {
325335
@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);
326336
}
327337

@@ -333,36 +343,6 @@ public static function parseScalar($scalar, $flags = 0, $delimiters = null, $str
333343
return $output;
334344
}
335345

336-
/**
337-
* Parses a quoted scalar to YAML.
338-
*
339-
* @param string $scalar
340-
* @param int &$i
341-
*
342-
* @return string A YAML string
343-
*
344-
* @throws ParseException When malformed inline YAML string is parsed
345-
*/
346-
private static function parseQuotedScalar($scalar, &$i)
347-
{
348-
if (!preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
349-
throw new ParseException(sprintf('Malformed inline YAML string (%s).', substr($scalar, $i)));
350-
}
351-
352-
$output = substr($match[0], 1, strlen($match[0]) - 2);
353-
354-
$unescaper = new Unescaper();
355-
if ('"' == $scalar[$i]) {
356-
$output = $unescaper->unescapeDoubleQuotedString($output);
357-
} else {
358-
$output = $unescaper->unescapeSingleQuotedString($output);
359-
}
360-
361-
$i += strlen($match[0]);
362-
363-
return $output;
364-
}
365-
366346
/**
367347
* Parses a sequence to a YAML string.
368348
*
@@ -399,7 +379,7 @@ private static function parseSequence($sequence, $flags, &$i = 0, $references =
399379
break;
400380
default:
401381
$isQuoted = in_array($sequence[$i], array('"', "'"));
402-
$value = self::parseScalar($sequence, $flags, array(',', ']'), array('"', "'"), $i, true, $references);
382+
$value = self::parseScalar($sequence, $flags, array(',', ']'), $i, true, $references);
403383

404384
// the value can be an array if a reference has been resolved to an array var
405385
if (is_string($value) && !$isQuoted && false !== strpos($value, ': ')) {
@@ -457,7 +437,7 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = ar
457437
}
458438

459439
// key
460-
$key = self::parseScalar($mapping, $flags, array(':', ' '), array('"', "'"), $i, false);
440+
$key = self::parseScalar($mapping, $flags, array(':', ' '), $i, false);
461441
$i = strpos($mapping, ':', $i);
462442

463443
if (!isset($mapping[$i + 1]) || ' ' !== $mapping[$i + 1]) {
@@ -499,7 +479,7 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = ar
499479
case ' ':
500480
break;
501481
default:
502-
$value = self::parseScalar($mapping, $flags, array(',', '}'), array('"', "'"), $i, true, $references);
482+
$value = self::parseScalar($mapping, $flags, array(',', '}'), $i, true, $references);
503483
// Spec: Keys MUST be unique; first one wins.
504484
// Parser cannot abort this mapping earlier, since lines
505485
// are processed sequentially.
@@ -537,17 +517,12 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = ar
537517
private static function evaluateScalar($scalar, $flags, $references = array())
538518
{
539519
$scalar = trim($scalar);
540-
$scalarLower = strtolower($scalar);
541-
542-
if (0 === strpos($scalar, '*')) {
543-
if (false !== $pos = strpos($scalar, '#')) {
544-
$value = substr($scalar, 1, $pos - 2);
545-
} else {
546-
$value = substr($scalar, 1);
547-
}
520+
$reader = new StringReader($scalar);
521+
if ($reader->eat('*')) {
522+
$value = $reader->eatCSpan(' #');
548523

549524
// an unquoted *
550-
if (false === $value || '' === $value) {
525+
if ('' === $value) {
551526
throw new ParseException('A reference must contain at least one character.');
552527
}
553528

@@ -558,59 +533,63 @@ private static function evaluateScalar($scalar, $flags, $references = array())
558533
return $references[$value];
559534
}
560535

536+
if ($reader->eat('!')) {
537+
if ($reader->eat('str')) {
538+
$reader->skip(1, true);
539+
540+
return $reader->readToFullConsumption();
541+
} elseif ($reader->eat(' ')) {
542+
return (int) self::parseScalar($reader->readToFullConsumption(), $flags);
543+
} elseif ($tag = $reader->eatAny(array('php/object:', '!php/object:'))) {
544+
if (self::$objectSupport) {
545+
if ('!php/object:' === $tag) {
546+
@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);
547+
}
548+
549+
return unserialize($reader->readToFullConsumption());
550+
}
551+
552+
if (self::$exceptionOnInvalidType) {
553+
throw new ParseException('Object support when parsing a YAML file has been disabled.');
554+
}
555+
556+
return;
557+
} elseif ($reader->eat('php/const:')) {
558+
if (self::$constantSupport) {
559+
$constant = $reader->readToFullConsumption();
560+
if (defined($constant)) {
561+
return constant($constant);
562+
}
563+
564+
throw new ParseException(sprintf('The constant "%s" is not defined.', $constant));
565+
}
566+
if (self::$exceptionOnInvalidType) {
567+
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));
568+
}
569+
570+
return;
571+
} elseif ($reader->eat('!float ')) {
572+
return (float) $reader->readToFullConsumption();
573+
} elseif ($reader->eat('!binary')) {
574+
$reader->skip(1);
575+
576+
return self::evaluateBinaryScalar($reader->readToFullConsumption());
577+
}
578+
}
579+
580+
$scalarLower = strtolower($scalar);
561581
switch (true) {
562582
case 'null' === $scalarLower:
563-
case '' === $scalar:
564583
case '~' === $scalar:
584+
case '' === $scalar:
565585
return;
566586
case 'true' === $scalarLower:
567587
return true;
568588
case 'false' === $scalarLower:
569589
return false;
570590
// Optimise for returning strings.
571-
case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || $scalar[0] === '!' || is_numeric($scalar[0]):
591+
case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || is_numeric($scalar[0]):
572592
switch (true) {
573-
case 0 === strpos($scalar, '!str'):
574-
return (string) substr($scalar, 5);
575-
case 0 === strpos($scalar, '! '):
576-
return (int) self::parseScalar(substr($scalar, 2), $flags);
577-
case 0 === strpos($scalar, '!php/object:'):
578-
if (self::$objectSupport) {
579-
return unserialize(substr($scalar, 12));
580-
}
581-
582-
if (self::$exceptionOnInvalidType) {
583-
throw new ParseException('Object support when parsing a YAML file has been disabled.');
584-
}
585-
586-
return;
587-
case 0 === strpos($scalar, '!!php/object:'):
588-
if (self::$objectSupport) {
589-
@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);
590-
591-
return unserialize(substr($scalar, 13));
592-
}
593-
594-
if (self::$exceptionOnInvalidType) {
595-
throw new ParseException('Object support when parsing a YAML file has been disabled.');
596-
}
597-
598-
return;
599-
case 0 === strpos($scalar, '!php/const:'):
600-
if (self::$constantSupport) {
601-
if (defined($const = substr($scalar, 11))) {
602-
return constant($const);
603-
}
604-
605-
throw new ParseException(sprintf('The constant "%s" is not defined.', $const));
606-
}
607-
if (self::$exceptionOnInvalidType) {
608-
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));
609-
}
610-
611-
return;
612-
case 0 === strpos($scalar, '!!float '):
613-
return (float) substr($scalar, 8);
614593
case preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar):
615594
$scalar = str_replace('_', '', (string) $scalar);
616595
// omitting the break / return as integers are handled in the next case
@@ -634,8 +613,6 @@ private static function evaluateScalar($scalar, $flags, $references = array())
634613
return -log(0);
635614
case '-.inf' === $scalarLower:
636615
return log(0);
637-
case 0 === strpos($scalar, '!!binary '):
638-
return self::evaluateBinaryScalar(substr($scalar, 9));
639616
case preg_match('/^(-|\+)?[0-9][0-9,]*(\.[0-9_]+)?$/', $scalar):
640617
case preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar):
641618
if (false !== strpos($scalar, ',')) {
@@ -664,8 +641,6 @@ private static function evaluateScalar($scalar, $flags, $references = array())
664641
* @param string $scalar
665642
*
666643
* @return string
667-
*
668-
* @internal
669644
*/
670645
public static function evaluateBinaryScalar($scalar)
671646
{

0 commit comments

Comments
 (0)
0