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

Skip to content

Commit ab88a58

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

File tree

5 files changed

+397
-142
lines changed

5 files changed

+397
-142
lines changed
Lines changed: 21 additions & 0 d E29B eletions
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: 94 additions & 119 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.
@@ -91,18 +93,16 @@ public static function parse($value, $flags = 0, $references = array())
9193
mb_internal_encoding('ASCII');
9294
}
9395

96+
$reader = new StringReader($value);
9497
$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);
98+
if ($reader->eat('[')) {
99+
$result = self::parseSequence($value, $flags, $i, $references);
100+
++$i;
101+
} elseif ($reader->eat('{')) {
102+
$result = self::parseMapping($value, $flags, $i, $references);
103+
++$i;
104+
} else {
105+
$result = self::parseScalar($value, $flags, null, $i, true, $references);
106106
}
107107

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

294303
if (null !== $delimiters) {
295-
$tmp = ltrim(substr($scalar, $i), ' ');
296-
if (!in_array($tmp[0], $delimiters)) {
304+
$reader->eatSpan(' ');
305+
if (null === $reader->eatAny($delimiters)) {
297306
throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)));
298307
}
299308
}
300309
} else {
301310
// "normal" string
302-
if (!$delimiters) {
311+
if (null === $delimiters) {
303312
$output = substr($scalar, $i);
304-
$i += strlen($output);
313+
$i += $reader->getRemainingByteCount();
305314

306315
// remove comments
307316
if (preg_match('/[ \t]+#/', $output, $match, PREG_OFFSET_CAPTURE)) {
317+
$reader = new StringReader($output, 0, $match[0][1]);
308318
$output = substr($output, 0, $match[0][1]);
309319
}
310-
} elseif (preg_match('/^(.+?)('.implode('|', $delimiters).')/', substr($scalar, $i), $match)) {
311-
$output = $match[1];
320+
} elseif ('' !== $output = $reader->eatCSpan(implode('', $delimiters))) {
321+
$reader = new StringReader($output);
312322
$i += strlen($output);
313323
} else {
314324
throw new ParseException(sprintf('Malformed inline YAML string (%s).', $scalar));
315325
}
316326

317327
// 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]));
328+
if ($indicator = $reader->eatAny(array('@', '`', '|', '>'))) {
329+
throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $indicator));
320330
}
321331

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);
332+
if ($reader->eat('%')) {
333+
@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);
324334
}
325335

326336
if ($evaluate) {
@@ -331,36 +341,6 @@ public static function parseScalar($scalar, $flags = 0, $delimiters = null, $str
331341
return $output;
332342
}
333343

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

402382
// the value can be an array if a reference has been resolved to an array var
403383
if (is_string($value) && !$isQuoted && false !== strpos($value, ': ')) {
@@ -455,7 +435,7 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = ar
455435
}
456436

457437
// key
458-
$key = self::parseScalar($mapping, $flags, array(':', ' '), array('"', "'"), $i, false);
438+
$key = self::parseScalar($mapping, $flags, array(':', ' '), $i, false);
459439
$i = strpos($mapping, ':', $i);
460440

461441
if (!isset($mapping[$i + 1]) || ' ' !== $mapping[$i + 1]) {
@@ -497,7 +477,7 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = ar
497477
case ' ':
498478
break;
499479
default:
500-
$value = self::parseScalar($mapping, $flags, array(',', '}'), array('"', "'"), $i, true, $references);
480+
$value = self::parseScalar($mapping, $flags, array(',', '}'), $i, true, $references);
501481
// Spec: Keys MUST be unique; first one wins.
502482
// Parser cannot abort this mapping earlier, since lines
503483
// are processed sequentially.
@@ -535,17 +515,12 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = ar
535515
private static function evaluateScalar($scalar, $flags, $references = array())
536516
{
537517
$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-
}
518+
$reader = new StringReader($scalar);
519+
if ($reader->eat('*')) {
520+
$value = $reader->eatCSpan(' #');
546521

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

@@ -556,59 +531,63 @@ private static function evaluateScalar($scalar, $flags, $references = array())
556531
return $references[$value];
557532
}
558533

534+
if ($reader->eat('!')) {
535+
if ($reader->eat('str')) {
536+
$reader->skip(1, true);
537+
538+
return $reader->readToFullConsumption();
539+
} elseif ($reader->eat(' ')) {
540+
return (int) self::parseScalar($reader->readToFullConsumption(), $flags);
541+
} elseif ($tag = $reader->eatAny(array('php/object:', '!php/object:'))) {
542+
if (self::$objectSupport) {
543+
if ('!php/object:' === $tag) {
544+
@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);
545+
}
546+
547+
return unserialize($reader->readToFullConsumption());
548+
}
549+
550+
if (self::$exceptionOnInvalidType) {
551+
throw new ParseException('Object support when parsing a YAML file has been disabled.');
552+
}
553+
554+
return;
555+
} elseif ($reader->eat('php/const:')) {
556+
if (self::$constantSupport) {
557+
$constant = $reader->readToFullConsumption();
558+
if (defined($constant)) {
559+
return constant($constant);
560+
}
561+
562+
throw new ParseException(sprintf('The constant "%s" is not defined.', $constant));
563+
}
564+
if (self::$exceptionOnInvalidType) {
565+
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));
566+
}
567+
568+
return;
569+
} elseif ($reader->eat('!float ')) {
570+
return (float) $reader->readToFullConsumption();
571+
} elseif ($reader->eat('!binary')) {
572+
$reader->skip(1);
573+
574+
return self::evaluateBinaryScalar($reader->readToFullConsumption());
575+
}
576+
}
577+
578+
$scalarLower = strtolower($scalar);
559579
switch (true) {
560580
case 'null' === $scalarLower:
561-
case '' === $scalar:
562581
case '~' === $scalar:
582+
case '' === $scalar:
563583
return;
564584
case 'true' === $scalarLower:
565585
return true;
566586
case 'false' === $scalarLower:
567587
return false;
568588
// Optimise for returning strings.
569-
case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || $scalar[0] === '!' || is_numeric($scalar[0]):
589+
case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || is_numeric($scalar[0]):
570590
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 sup 37CA port 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);
612591
case preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar):
613592
$scalar = str_replace('_', '', (string) $scalar);
614593
// omitting the break / return as integers are handled in the next case
@@ -632,8 +611,6 @@ private static function evaluateScalar($scalar, $flags, $references = array())
632611
return -log(0);
633612
case '-.inf' === $scalarLower:
634613
return log(0);
635-
case 0 === strpos($scalar, '!!binary '):
636-
return self::evaluateBinaryScalar(substr($scalar, 9));
637614
case preg_match('/^(-|\+)?[0-9][0-9,]*(\.[0-9_]+)?$/', $scalar):
638615
case preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar):
639616
if (false !== strpos($scalar, ',')) {
@@ -662,8 +639,6 @@ private static function evaluateScalar($scalar, $flags, $references = array())
662639
* @param string $scalar
663640
*
664641
* @return string
665-
*
666-
* @internal
667642
*/
668643
public static function evaluateBinaryScalar($scalar)
669644
{

0 commit comments

Comments
 (0)
0