8000 [Yaml] introduce flags to customize the parser behavior by xabbuh · Pull Request #17730 · symfony/symfony · GitHub
[go: up one dir, main page]

Skip to content

[Yaml] introduce flags to customize the parser behavior #17730

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions UPGRADE-3.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,51 @@ Serializer
Yaml
----

* Deprecated support for passing `true`/`false` as the second argument to the
`parse()` method to trigger exceptions when an invalid type was passed.

Before:

```php
Yaml::parse('{ "foo": "bar", "fiz": "cat" }', true);
```

After:

```php
Yaml::parse('{ "foo": "bar", "fiz": "cat" }', YAML::PARSE_EXCEPTION_ON_INVALID_TYPE);
```

* Deprecated support for passing `true`/`false` as the third argument to the
`parse()` method to toggle object support.

Before:

```php
Yaml::parse('{ "foo": "bar", "fiz": "cat" }', false, true);
```

After:

```php
Yaml::parse('{ "foo": "bar", "fiz": "cat" }', Yaml::PARSE_OBJECT);
```

* Deprecated support for passing `true`/`false` as the fourth argument to the
`parse()` method to parse objects as maps.

Before:

```php
Yaml::parse('{ "foo": "bar", "fiz": "cat" }', false, false, true);
```

After:

```php
Yaml::parse('{ "foo": "bar", "fiz": "cat" }', Yaml::PARSE_OBJECT_FOR_MAP);
```

* Deprecated support for passing `true`/`false` as the third argument to the `dump()` methods to toggle object support.

Before:
Expand Down
45 changes: 45 additions & 0 deletions UPGRADE-4.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,51 @@ Serializer
Yaml
----

* Removed support for passing `true`/`false` as the second argument to the
`parse()` method to trigger exceptions when an invalid type was passed.

Before:

```php
Yaml::parse('{ "foo": "bar", "fiz": "cat" }', true);
```

After:

```php
Yaml::parse('{ "foo": "bar", "fiz": "cat" }', YAML::PARSE_EXCEPTION_ON_INVALID_TYPE);
```

* Removed support for passing `true`/`false` as the third argument to the
`parse()` method to toggle object support.

Before:

```php
Yaml::parse('{ "foo": "bar", "fiz": "cat" }', false, true);
```

After:

```php
Yaml::parse('{ "foo": "bar", "fiz": "cat" }', Yaml::PARSE_OBJECT);
```

* Removed support for passing `true`/`false` as the fourth argument to the
`parse()` method to parse objects as maps.

Before:

```php
Yaml::parse('{ "foo": "bar", "fiz": "cat" }', false, false, true);
```

After:

```php
Yaml::parse('{ "foo": "bar", "fiz": "cat" }', Yaml::PARSE_OBJECT_FOR_MAP);
```

* Removed support for passing `true`/`false` as the third argument to the `dump()` methods to toggle object support.

Before:
Expand Down
6 changes: 6 additions & 0 deletions src/Symfony/Component/Yaml/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ CHANGELOG
3.1.0
-----

* Added support for customizing the YAML parser behavior through an optional bit field:

```php
Yaml::parse('{ "foo": "bar", "fiz": "cat" }', Yaml::EXCEPTION_ON_INVALID_TYPE | Yaml::PARSE_OBJECT | Yaml::PARSE_OBJECT_FOR_MAP);
```

* Added support for customizing the dumped YAML string through an optional bit field:

```php
Expand Down
48 changes: 39 additions & 9 deletions src/Symfony/Component/Yaml/Inline.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,51 @@ class Inline
/**
* Converts a YAML string to a PHP array.
*
* @param string $value A YAML string
* @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
* @param bool $objectSupport true if object support is enabled, false otherwise
* @param bool $objectForMap true if maps should return a stdClass instead of array()
* @param array $references Mapping of variable names to values
* @param string $value A YAML string
* @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
* @param array $references Mapping of variable names to values
*
* @return array A PHP array representing the YAML string
*
* @throws ParseException
*/
public static function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false, $references = array())
public static function parse($value, $flags = 0, $references = array())
{
self::$exceptionOnInvalidType = $exceptionOnInvalidType;
self::$objectSupport = $objectSupport;
self::$objectForMap = $objectForMap;
if (is_bool($flags)) {
@trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);

if ($flags) {
$flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE;
} else {
$flags = 0;
}
}

if (func_num_args() >= 3 && !is_array($references)) {
@trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED);

if ($references) {
$flags |= Yaml::PARSE_OBJECT;
}

if (func_num_args() >= 4) {
@trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED);

if (func_get_arg(3)) {
$flags |= Yaml::PARSE_OBJECT_FOR_MAP;
}
}

if (func_num_args() >= 5) {
$references = func_get_arg(4);
} else {
$references = array();
}
}

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);

$value = trim($value);

Expand Down
64 changes: 43 additions & 21 deletions src/Symfony/Component/Yaml/Parser.php
F438
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,41 @@ public function __construct($offset = 0)
/**
* Parses a YAML string to a PHP value.
*
* @param string $value A YAML string
* @param bool $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
* @param bool $objectSupport true if object support is enabled, false otherwise
* @param bool $objectForMap true if maps should return a stdClass instead of array()
* @param string $value A YAML string
* @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
*
* @return mixed A PHP value
*
* @throws ParseException If the YAML is not valid
*/
public function parse($value, $exceptionOnInvalidType = false, $objectSupport = false, $objectForMap = false)
public function parse($value, $flags = 0)
{
if (is_bool($flags)) {
@trigger_error('Passing a boolean flag to toggle exception handling is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE flag instead.', E_USER_DEPRECATED);

if ($flags) {
$flags = Yaml::PARSE_EXCEPTION_ON_INVALID_TYPE;
} else {
$flags = 0;
}
}

if (func_num_args() >= 3) {
@trigger_error('Passing a boolean flag to toggle object support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT flag instead.', E_USER_DEPRECATED);

if (func_get_arg(2)) {
$flags |= Yaml::PARSE_OBJECT;
}
}

if (func_num_args() >= 4) {
@trigger_error('Passing a boolean flag to toggle object for map support is deprecated since version 3.1 and will be removed in 4.0. Use the Yaml::PARSE_OBJECT_FOR_MAP flag instead.', E_USER_DEPRECATED);

if (func_get_arg(3)) {
$flags |= Yaml::PARSE_OBJECT_FOR_MAP;
}
}

if (!preg_match('//u', $value)) {
throw new ParseException('The YAML value does not appear to be valid UTF-8.');
}
Expand Down Expand Up @@ -95,7 +119,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
$c = $this->getRealCurrentLineNb() + 1;
$parser = new self($c);
$parser->refs = &$this->refs;
$data[] = $parser->parse($this->getNextEmbedBlock(null, true), $exceptionOnInvalidType, $objectSupport, $objectForMap);
$data[] = $parser->parse($this->getNextEmbedBlock(null, true), $flags);
} else {
if (isset($values['leadspaces'])
&& preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\{\[].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $values['value'], $matches)
Expand All @@ -110,9 +134,9 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
$block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + strlen($values['leadspaces']) + 1);
}

$data[] = $parser->parse($block, $exceptionOnInvalidType, $objectSupport, $objectForMap);
$data[] = $parser->parse($block, $flags);
} else {
$data[] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport, $objectForMap, $context);
$data[] = $this->parseValue($values['value'], $flags, $context);
}
}
if ($isRef) {
Expand All @@ -125,7 +149,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
$context = 'mapping';

// force correct settings
Inline::parse(null, $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs);
Inline::parse(null, $flags, $this->refs);
try {
$key = Inline::parseScalar($values['key']);
} catch (ParseException $e) {
Expand Down Expand Up @@ -169,7 +193,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
$c = $this->getRealCurrentLineNb() + 1;
$parser = new self($c);
$parser->refs = &$this->refs;
$parsed = $parser->parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap);
$parsed = $parser->parse($value, $flags);

if (!is_array($parsed)) {
throw new ParseException('YAML merge keys used with a scalar value instead of an array.', $this->getRealCurrentLineNb() + 1, $this->currentLine);
Expand Down Expand Up @@ -220,15 +244,15 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
$c = $this->getRealCurrentLineNb() + 1;
$parser = new self($c);
$parser->refs = &$this->refs;
$value = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport, $objectForMap);
$value = $parser->parse($this->getNextEmbedBlock(), $flags);
// Spec: Keys MUST be unique; first one wins.
// But overwriting is allowed when a merge node is used in current block.
if ($allowOverwrite || !isset($data[$key])) {
$data[$key] = $value;
}
}
} else {
$value = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport, $objectForMap, $context);
$value = $this->parseValue($values['value'], $flags, $context);
// Spec: Keys MUST be unique; first one wins.
// But overwriting is allowed when a merge node is used in current block.
if ($allowOverwrite || !isset($data[$key])) {
Expand All @@ -247,7 +271,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
// 1-liner optionally followed by newline(s)
if (is_string($value) && $this->lines[0] === trim($value)) {
try {
$value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs);
$value = Inline::parse($this->lines[0], $flags, $this->refs);
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);
Expand Down Expand Up @@ -301,7 +325,7 @@ public function parse($value, $exceptionOnInvalidType = false, $objectSupport =
mb_internal_encoding($mbEncoding);
}

if ($objectForMap && !is_object($data)) {
if (Yaml::PARSE_OBJECT_FOR_MAP & $flags && !is_object($data)) {
$data = (object) $data;
}

Expand Down Expand Up @@ -462,17 +486,15 @@ private function moveToPreviousLine()
/**
* Parses a YAML value.
*
* @param string $value A YAML value
* @param bool $exceptionOnInvalidType True if an exception must be thrown on invalid types false otherwise
* @param bool $objectSupport True if object support is enabled, false otherwise
* @param bool $objectForMap true if maps should return a stdClass instead of array()
* @param string $context The parser context (either sequence or mapping)
* @param string $value A YAML value
* @param int $flags A bit field of PARSE_* constants to customize the YAML parser behavior
* @param string $context The parser context (either sequence or mapping)
*
* @return mixed A PHP value
*
* @throws ParseException When reference does not exist
*/
private function parseValue($value, $exceptionOnInvalidType, $objectSupport, $objectForMap, $context)
private function parseValue($value, $flags, $context)
{
if (0 === strpos($value, '*')) {
if (false !== $pos = strpos($value, '#')) {
Expand All @@ -495,7 +517,7 @@ private function parseValue($value, $exceptionOnInvalidType, $objectSupport, $ob
}

try {
$parsedValue = Inline::parse($value, $exceptionOnInvalidType, $objectSupport, $objectForMap, $this->refs);
$parsedValue = Inline::parse($value, $flags, $this->refs);

if ('mapping' === $context && '"' !== $value[0] && "'" !== $value[0] && '[' !== $value[0] && '{' !== $value[0] && '!' !== $value[0] && false !== strpos($parsedValue, ': ')) {
throw new ParseException('A colon cannot be used in an unquoted mapping value.');
Expand Down
34 changes: 34 additions & 0 deletions src/Symfony/Component/Yaml/Tests/InlineTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Symfony\Component\Yaml\Tests;

use Symfony\Component\Yaml\Inline;
use Symfony\Component\Yaml\Yaml;

class InlineTest extends \PHPUnit_Framework_TestCase
{
Expand All @@ -27,6 +28,17 @@ public function testParse($yaml, $value)
* @dataProvider getTestsForParseWithMapObjects
*/
public function testParseWithMapObjects($yaml, $value)
{
$actual = Inline::parse($yaml, Yaml::PARSE_OBJECT_FOR_MAP);

$this->assertSame(serialize($value), serialize($actual));
}

/**
* @group legacy
* @dataProvider getTestsForParseWithMapObjects
*/
public function testParseWithMapObjectsPassingTrue($yaml, $value)
{
$actual = Inline::parse($yaml, false, false, true);

Expand Down Expand Up @@ -142,6 +154,15 @@ public function testParseScalarWithCorrectlyQuotedStringShouldReturnString()
* @dataProvider getDataForParseReferences
*/
public function testParseReferences($yaml, $expected)
{
$this->assertSame($expected, Inline::parse($yaml, 0, array('var' => 'var-value')));
}

/**
* @group legacy
* @dataProvider getDataForParseReferences
*/
public function testParseReferencesAsFifthArgument($yaml, $expected)
{
$this->assertSame($expected, Inline::parse($yaml, false, false, false, array('var' => 'var-value')));
}
Expand All @@ -161,6 +182,19 @@ public function getDataForParseReferences()
}

public function testParseMapReferenceInSequence()
{
$foo = array(
'a' => 'Steve',
'b' => 'Clark',
'c' => 'Brian',
);
$this->assertSame(array($foo), Inline::parse('[*foo]', 0, array('foo' => $foo)));
}

/**
* @group legacy
*/
public function testParseMapReferenceInSequenceAsFifthArgument()
{
$foo = array(
'a' => 'Steve',
Expand Down
Loading
0