Skip to content

Commit 8947a4e

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

File tree

5 files changed

+403
-123
lines changed

5 files changed

+403
-123
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: 100 additions & 100 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) {
@@ -343,20 +353,15 @@ public static function parseScalar($scalar, $flags = 0, $delimiters = null, $str
343353
*/
344354
private static function parseQuotedScalar($scalar, &$i)
345355
{
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-
356+
$reader = new StringReader($scalar, $i);
352357
$unescaper = new Unescaper();
353-
if ('"' == $scalar[$i]) {
354-
$output = $unescaper->unescapeDoubleQuotedString($output);
355-
} else {
356-
$output = $unescaper->unescapeSingleQuotedString($output);
358+
if ($reader->eat('"')) {
359+
$output = $unescaper->unescapeDoubleQuotedString($reader);
360+
} elseif ($reader->eat('\'')) {
361+
$output = $unescaper->unescapeSingleQuotedString($reader);
357362
}
358363

359-
$i += strlen($match[0]);
364+
$i = $reader->getOffset();
360365

361366
return $output;
362367
}
@@ -397,7 +402,7 @@ private static function parseSequence($sequence, $flags, &$i = 0, $references =
397402
break;
398403
default:
399404
$isQuoted = in_array($sequence[$i], array('"', "'"));
400-
$value = self::parseScalar($sequence, $flags, array(',', ']'), array('"', "'"), $i, true, $references);
405+
$value = self::parseScalar($sequence, $flags, array(',', ']'), $i, true, $references);
401406

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

457462
// key
458-
$key = self::parseScalar($mapping, $flags, array(':', ' '), array('"', "'"), $i, false);
463+
$key = self::parseScalar($mapping, $flags, array(':', ' '), $i, false);
459464
$i = strpos($mapping, ':', $i);
460465

461466
if (!isset($mapping[$i + 1]) || ' ' !== $mapping[$i + 1]) {
@@ -497,7 +502,7 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = ar
497502
case ' ':
498503
break;
499504
default:
500-
$value = self::parseScalar($mapping, $flags, array(',', '}'), array('"', "'"), $i, true, $references);
505+
$value = self::parseScalar($mapping, $flags, array(',', '}'), $i, true, $references);
501506
// Spec: Keys MUST be unique; first one wins.
502507
// Parser cannot abort this mapping earlier, since lines
503508
// are processed sequentially.
@@ -535,17 +540,12 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = ar
535540
private static function evaluateScalar($scalar, $flags, $references = array())
536541
{
537542
$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-
}
543+
$reader = new StringReader($scalar);
544+
if ($reader->eat('*')) {
545+
$value = $reader->eatCSpan(' #');
546546

547547
// an unquoted *
548-
if (false === $value || '' === $value) {
548+
if ('' === $value) {
549549
throw new ParseException('A reference must contain at least one character.');
550550
}
551551

@@ -556,59 +556,63 @@ private static function evaluateScalar($scalar, $flags, $references = array())
556556
return $references[$value];
557557
}
558558

559+
if ($reader->eat('!')) {
560+
if ($reader->eat('str')) {
561+
$reader->skip(1, true);
562+
563+
return $reader->readToFullConsumption();
564+
} elseif ($reader->eat(' ')) {
565+
return (int) self::parseScalar($reader->readToFullConsumption(), $flags);
566+
} elseif ($tag = $reader->eatAny(array('php/object:', '!php/object:'))) {
567+
if (self::$objectSupport) {
568+
if ('!php/object:' === $tag) {
569+
@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);
570+
}
571+
572+
return unserialize($reader->readToFullConsumption());
573+
}
574+
575+
if (self::$exceptionOnInvalidType) {
576+
throw new ParseException('Object support when parsing a YAML file has been disabled.');
577+
}
578+
579+
return;
580+
} elseif ($reader->eat('php/const:')) {
581+
if (self::$constantSupport) {
582+
$constant = $reader->readToFullConsumption();
583+
if (defined($constant)) {
584+
return constant($constant);
585+
}
586+
587+
throw new ParseException(sprintf('The constant "%s" is not defined.', $constant));
588+
}
589+
if (self::$exceptionOnInvalidType) {
590+
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));
591+
}
592+
593+
return;
594+
} elseif ($reader->eat('!float ')) {
595+
return (float) $reader->readToFullConsumption();
596+
} elseif ($reader->eat('!binary')) {
597+
$reader->skip(1);
598+
599+
return self::evaluateBinaryScalar($reader->readToFullConsumption());
600+
}
601+
}
602+
603+
$scalarLower = strtolower($scalar);
559604
switch (true) {
560605
case 'null' === $scalarLower:
561-
case '' === $scalar:
562606
case '~' === $scalar:
607+
case '' === $scalar:
563608
return;
564609
case 'true' === $scalarLower:
565610
return true;
566611
case 'false' === $scalarLower:
567612
return false;
568613
// Optimise for returning strings.
569-
case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || $scalar[0] === '!' || is_numeric($scalar[0]):
614+
case $scalar[0] === '+' || $scalar[0] === '-' || $scalar[0] === '.' || is_numeric($scalar[0]):
570615
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);
612616
case preg_match('{^[+-]?[0-9][0-9_]*$}', $scalar):
613617
$scalar = str_replace('_', '', (string) $scalar);
614618
// omitting the break / return as integers are handled in the next case
@@ -632,8 +636,6 @@ private static function evaluateScalar($scalar, $flags, $references = array())
632636
return -log(0);
633637
case '-.inf' === $scalarLower:
634638
return log(0);
635-
case 0 === strpos($scalar, '!!binary '):
636-
return self::evaluateBinaryScalar(substr($scalar, 9));
637639
case preg_match('/^(-|\+)?[0-9][0-9,]*(\.[0-9_]+)?$/', $scalar):
638640
case preg_match('/^(-|\+)?[0-9][0-9_]*(\.[0-9_]+)?$/', $scalar):
639641
if (false !== strpos($scalar, ',')) {
@@ -662,8 +664,6 @@ private static function evaluateScalar($scalar, $flags, $references = array())
662664
* @param string $scalar
663665
*
664666
* @return string
665-
*
666-
* @internal
667667
*/
668668
public static function evaluateBinaryScalar($scalar)
669669
{

0 commit comments

Comments
 (0)