Skip to content

Commit

Permalink
feature #24253 [Yaml] support parsing files (xabbuh)
Browse files Browse the repository at this point in the history
This PR was merged into the 3.4 branch.

Discussion
----------

[Yaml] support parsing files

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #24191 (comment)
| License       | MIT
| Doc PR        | TODO

This PR adds a new flag `PARSE_FILE` which can be passed to the YAML parser. When given, the input will be interpreted as a filename whose contents will then be parsed. We already supported passing filenames in the past. Back then the features was not deterministic as it just relied on the string being an existing file or not. Now that we are able to control the behaviour of the parser by passing flags this can be done in a much cleaner way and allows to properly deal with errors (i.e. non existent or unreadable files).

This change will also allow to improve error/deprecation messages to include the filename being parsed and thus showing more descriptive error messages when people are using the YAML parser with a bunch of files (e.g. as part of the DependencyInjection or Routing component).

Commits
-------

9becb8a [Yaml] support parsing files
  • Loading branch information
xabbuh committed Sep 25, 2017
2 parents 8136fa5 + 9becb8a commit 1b4d2b2
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 50 deletions.
2 changes: 2 additions & 0 deletions src/Symfony/Component/Yaml/CHANGELOG.md
Expand Up @@ -4,6 +4,8 @@ CHANGELOG
3.4.0
-----

* added support for parsing YAML files using the `Yaml::parseFile()` or `Parser::parseFile()` method

* the `Dumper`, `Parser`, and `Yaml` classes are marked as final

* Deprecated the `!php/object:` tag which will be replaced by the
Expand Down
56 changes: 28 additions & 28 deletions src/Symfony/Component/Yaml/Inline.php
Expand Up @@ -26,27 +26,27 @@ class Inline
{
const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*+(?:\\\\.[^"\\\\]*+)*+)"|\'([^\']*+(?:\'\'[^\']*+)*+)\')';

public static $parsedLineNumber;
public static $parsedLineNumber = -1;
public static $parsedFilename;

private static $exceptionOnInvalidType = false;
private static $objectSupport = false;
private static $objectForMap = false;
private static $constantSupport = false;

/**
* @param int $flags
* @param int|null $parsedLineNumber
* @param int $flags
* @param int|null $parsedLineNumber
* @param string|null $parsedFilename
*/
public static function initialize($flags, $parsedLineNumber = null)
public static function initialize($flags, $parsedLineNumber = null, $parsedFilename = null)
{
self::$exceptionOnInvalidType = (bool) (Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE & $flags);
self::$objectSupport = (bool) (Yaml::PARSE_OBJECT & $flags);
self::$objectForMap = (bool) (Yaml::PARSE_OBJECT_FOR_MAP & $flags);
self::$constantSupport = (bool) (Yaml::PARSE_CONSTANT & $flags);

if (null !== $parsedLineNumber) {
self::$parsedLineNumber = $parsedLineNumber;
}
self::$parsedFilename = $parsedFilename;
self::$parsedLineNumber = null !== $parsedLineNumber ? $parsedLineNumber : -1;
}

/**
Expand Down Expand Up @@ -128,7 +128,7 @@ public static function parse($value, $flags = 0, $references = array())

// some comments are allowed at the end
if (preg_replace('/\s+#.*$/A', '', substr($value, $i))) {
throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)));
throw new ParseException(sprintf('Unexpected characters near "%s".', substr($value, $i)), self::$parsedLineNumber, $value, self::$parsedFilename);
}

if (isset($mbEncoding)) {
Expand Down Expand Up @@ -322,7 +322,7 @@ public static function parseScalar($scalar, $flags = 0, $delimiters = null, &$i
if (null !== $delimiters) {
$tmp = ltrim(substr($scalar, $i), ' ');
if (!in_array($tmp[0], $delimiters)) {
throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)));
throw new ParseException(sprintf('Unexpected characters (%s).', substr($scalar, $i)), self::$parsedLineNumber, $scalar, self::$parsedFilename);
}
}
} else {
Expand All @@ -339,12 +339,12 @@ public static function parseScalar($scalar, $flags = 0, $delimiters = null, &$i
$output = $match[1];
$i += strlen($output);
} else {
throw new ParseException(sprintf('Malformed inline YAML string: %s.', $scalar));
throw new ParseException(sprintf('Malformed inline YAML string: %s.', $scalar), self::$parsedLineNumber, null, self::$parsedFilename);
}

// a non-quoted string cannot start with @ or ` (reserved) nor with a scalar indicator (| or >)
if ($output && ('@' === $output[0] || '`' === $output[0] || '|' === $output[0] || '>' === $output[0])) {
throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]));
throw new ParseException(sprintf('The reserved indicator "%s" cannot start a plain scalar; you need to quote the scalar.', $output[0]), self::$parsedLineNumber, $output, self::$parsedFilename);
}

if ($output && '%' === $output[0]) {
Expand Down Expand Up @@ -372,7 +372,7 @@ public static function parseScalar($scalar, $flags = 0, $delimiters = null, &$i
private static function parseQuotedScalar($scalar, &$i)
{
if (!Parser::preg_match('/'.self::REGEX_QUOTED_STRING.'/Au', substr($scalar, $i), $match)) {
throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i)));
throw new ParseException(sprintf('Malformed inline YAML string: %s.', substr($scalar, $i)), self::$parsedLineNumber, $scalar, self::$parsedFilename);
}

$output = substr($match[0], 1, strlen($match[0]) - 2);
Expand Down Expand Up @@ -455,7 +455,7 @@ private static function parseSequence($sequence, $flags, &$i = 0, $references =
++$i;
}

throw new ParseException(sprintf('Malformed inline YAML string: %s.', $sequence));
throw new ParseException(sprintf('Malformed inline YAML string: %s.', $sequence), self::$parsedLineNumber, null, self::$parsedFilename);
}

/**
Expand Down Expand Up @@ -572,7 +572,7 @@ private static function parseMapping($mapping, $flags, &$i = 0, $references = ar
}
}

throw new ParseException(sprintf('Malformed inline YAML string: %s.', $mapping));
throw new ParseException(sprintf('Malformed inline YAML string: %s.', $mapping), self::$parsedLineNumber, null, self::$parsedFilename);
}

/**
Expand Down Expand Up @@ -600,11 +600,11 @@ private static function evaluateScalar($scalar, $flags, $references = array())

// an unquoted *
if (false === $value || '' === $value) {
throw new ParseException('A reference must contain at least one character.');
throw new ParseException('A reference must contain at least one character.', self::$parsedLineNumber, $value, self::$parsedFilename);
}

if (!array_key_exists($value, $references)) {
throw new ParseException(sprintf('Reference "%s" does not exist.', $value));
throw new ParseException(sprintf('Reference "%s" does not exist.', $value), self::$parsedLineNumber, $value, self::$parsedFilename);
}

return $references[$value];
Expand Down Expand Up @@ -639,7 +639,7 @@ private static function evaluateScalar($scalar, $flags, $references = array())
}

if (self::$exceptionOnInvalidType) {
throw new ParseException('Object support when parsing a YAML file has been disabled.');
throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber, $scalar, self::$parsedFilename);
}

return;
Expand All @@ -651,7 +651,7 @@ private static function evaluateScalar($scalar, $flags, $references = array())
}

if (self::$exceptionOnInvalidType) {
throw new ParseException('Object support when parsing a YAML file has been disabled.');
throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber, $scalar, self::$parsedFilename);
}

return;
Expand All @@ -661,7 +661,7 @@ private static function evaluateScalar($scalar, $flags, $references = array())
}

if (self::$exceptionOnInvalidType) {
throw new ParseException('Object support when parsing a YAML file has been disabled.');
throw new ParseException('Object support when parsing a YAML file has been disabled.', self::$parsedLineNumber, $scalar, self::$parsedFilename);
}

return;
Expand All @@ -673,10 +673,10 @@ private static function evaluateScalar($scalar, $flags, $references = array())
return constant($const);
}

throw new ParseException(sprintf('The constant "%s" is not defined.', $const));
throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber, $scalar, self::$parsedFilename);
}
if (self::$exceptionOnInvalidType) {
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));
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), self::$parsedLineNumber, $scalar, self::$parsedFilename);
}

return;
Expand All @@ -686,10 +686,10 @@ private static function evaluateScalar($scalar, $flags, $references = array())
return constant($const);
}

throw new ParseException(sprintf('The constant "%s" is not defined.', $const));
throw new ParseException(sprintf('The constant "%s" is not defined.', $const), self::$parsedLineNumber, $scalar, self::$parsedFilename);
}
if (self::$exceptionOnInvalidType) {
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));
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), self::$parsedLineNumber, $scalar, self::$parsedFilename);
}

return;
Expand Down Expand Up @@ -781,7 +781,7 @@ private static function parseTag($value, &$i, $flags)

// Built-in tags
if ($tag && '!' === $tag[0]) {
throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag));
throw new ParseException(sprintf('The built-in tag "!%s" is not implemented.', $tag), self::$parsedLineNumber, $value, self::$parsedFilename);
}

if (Yaml::PARSE_CUSTOM_TAGS & $flags) {
Expand All @@ -790,7 +790,7 @@ private static function parseTag($value, &$i, $flags)
return $tag;
}

throw new ParseException(sprintf('Tags support is not enabled. Enable the `Yaml::PARSE_CUSTOM_TAGS` flag to use "!%s".', $tag));
throw new ParseException(sprintf('Tags support is not enabled. Enable the `Yaml::PARSE_CUSTOM_TAGS` flag to use "!%s".', $tag), self::$parsedLineNumber, $value, self::$parsedFilename);
}

/**
Expand All @@ -805,11 +805,11 @@ public static function evaluateBinaryScalar($scalar)
$parsedBinaryData = self::parseScalar(preg_replace('/\s/', '', $scalar));

if (0 !== (strlen($parsedBinaryData) % 4)) {
throw new ParseException(sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', strlen($parsedBinaryData)));
throw new ParseException(sprintf('The normalized base64 encoded data (data without whitespace characters) length must be a multiple of four (%d bytes given).', strlen($parsedBinaryData)), self::$parsedLineNumber, $scalar, self::$parsedFilename);
}

if (!Parser::preg_match('#^[A-Z0-9+/]+={0,2}$#i', $parsedBinaryData)) {
throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData));
throw new ParseException(sprintf('The base64 encoded data (%s) contains invalid characters.', $parsedBinaryData), self::$parsedLineNumber, $scalar, self::$parsedFilename);
}

return base64_decode($parsedBinaryData, true);
Expand Down

0 comments on commit 1b4d2b2

Please sign in to comment.