added a way to enable/disable object support when parsing/dumping

By default, object support is disabled, and instead of throwing an
exception when an object is handled, null is returned.

If you do need object support, enable it via:

    Yaml::dump($data, false, true);

If you want an exception to be thrown in case an invalid type is handled
(a PHP resource or a PHP object), pass true as the second argument:

    Yaml::dump($data, true, true);

The same can be done when parsing:

    Yaml::parse($data, 2, false, true);
This commit is contained in:
Fabien Potencier 2013-01-17 09:34:45 +01:00
parent ac756bf39e
commit ba6e3159c0
7 changed files with 122 additions and 51 deletions

View File

@ -31,7 +31,7 @@ class YamlExtension extends \Twig_Extension
);
}
public function encode($input, $inline = 0)
public function encode($input, $inline = 0, $dumpObjects = false)
{
static $dumper;
@ -39,20 +39,20 @@ class YamlExtension extends \Twig_Extension
$dumper = new YamlDumper();
}
return $dumper->dump($input, $inline);
return $dumper->dump($input, $inline, false, $dumpObjects);
}
public function dump($value)
public function dump($value, $inline = 0, $dumpObjects = false)
{
if (is_resource($value)) {
return '%Resource%';
}
if (is_array($value) || is_object($value)) {
return '%'.gettype($value).'% '.$this->encode($value);
return '%'.gettype($value).'% '.$this->encode($value, $inline, $dumpObjects);
}
return $this->encode($value);
return $this->encode($value, $inline, $dumpObjects);
}
/**

View File

@ -21,19 +21,21 @@ class Dumper
/**
* Dumps a PHP value to YAML.
*
* @param mixed $input The PHP value
* @param integer $inline The level where you switch to inline YAML
* @param integer $indent The level of indentation (used internally)
* @param mixed $input The PHP value
* @param integer $inline The level where you switch to inline YAML
* @param Boolean $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
* @param Boolean $objectSupport true if object support is enabled, false otherwise
* @param integer $indent The level of indentation (used internally)
*
* @return string The YAML representation of the PHP value
*/
public function dump($input, $inline = 0, $indent = 0)
public function dump($input, $inline = 0, $exceptionOnInvalidType = false, $objectSupport = false, $indent = 0)
{
$output = '';
$prefix = $indent ? str_repeat(' ', $indent) : '';
if ($inline <= 0 || !is_array($input) || empty($input)) {
$output .= $prefix.Inline::dump($input);
$output .= $prefix.Inline::dump($input, $exceptionOnInvalidType, $objectSupport);
} else {
$isAHash = array_keys($input) !== range(0, count($input) - 1);
@ -42,9 +44,9 @@ class Dumper
$output .= sprintf('%s%s%s%s',
$prefix,
$isAHash ? Inline::dump($key).':' : '-',
$isAHash ? Inline::dump($key, $exceptionOnInvalidType, $objectSupport).':' : '-',
$willBeInlined ? ' ' : "\n",
$this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + 2)
$this->dump($value, $inline - 1, $exceptionOnInvalidType, $objectSupport, $willBeInlined ? 0 : $indent + 2)
).($willBeInlined ? "\n" : '');
}
}

View File

@ -22,15 +22,23 @@ class Inline
{
const REGEX_QUOTED_STRING = '(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\']*(?:\'\'[^\']*)*)\')';
private static $exceptionOnInvalidType = false;
private static $objectSupport = false;
/**
* Converts a YAML string to a PHP array.
*
* @param string $value A YAML string
* @param string $value A YAML string
* @param Boolean $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
* @param Boolean $objectSupport true if object support is enabled, false otherwise
*
* @return array A PHP array representing the YAML string
*/
public static function parse($value)
public static function parse($value, $exceptionOnInvalidType = false, $objectSupport = false)
{
self::$exceptionOnInvalidType = $exceptionOnInvalidType;
self::$objectSupport = $objectSupport;
$value = trim($value);
if (0 == strlen($value)) {
@ -63,21 +71,35 @@ class Inline
/**
* Dumps a given PHP variable to a YAML string.
*
* @param mixed $value The PHP variable to convert
* @param mixed $value The PHP variable to convert
* @param Boolean $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
* @param Boolean $objectSupport true if object support is enabled, false otherwise
*
* @return string The YAML string representing the PHP array
*
* @throws DumpException When trying to dump PHP resource
*/
public static function dump($value)
public static function dump($value, $exceptionOnInvalidType = false, $objectSupport = false)
{
switch (true) {
case is_resource($value):
throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value)));
if ($exceptionOnInvalidType) {
throw new DumpException(sprintf('Unable to dump PHP resources in a YAML file ("%s").', get_resource_type($value)));
}
return 'null';
case is_object($value):
return '!!php/object:'.serialize($value);
if ($objectSupport) {
return '!!php/object:'.serialize($value);
}
if ($exceptionOnInvalidType) {
throw new DumpException('Object support when dumping a YAML file has been disabled.');
}
return 'null';
case is_array($value):
return self::dumpArray($value);
return self::dumpArray($value, $exceptionOnInvalidType, $objectSupport);
case null === $value:
return 'null';
case true === $value:
@ -115,11 +137,13 @@ class Inline
/**
* Dumps a PHP array to a YAML string.
*
* @param array $value The PHP array to dump
* @param array $value The PHP array to dump
* @param Boolean $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
* @param Boolean $objectSupport true if object support is enabled, false otherwise
*
* @return string The YAML string representing the PHP array
*/
private static function dumpArray($value)
private static function dumpArray($value, $exceptionOnInvalidType, $objectSupport)
{
// array
$keys = array_keys($value);
@ -128,7 +152,7 @@ class Inline
) {
$output = array();
foreach ($value as $val) {
$output[] = self::dump($val);
$output[] = self::dump($val, $exceptionOnInvalidType, $objectSupport);
}
return sprintf('[%s]', implode(', ', $output));
@ -137,7 +161,7 @@ class Inline
// mapping
$output = array();
foreach ($value as $key => $val) {
$output[] = sprintf('%s: %s', self::dump($key), self::dump($val));
$output[] = sprintf('%s: %s', self::dump($key, $exceptionOnInvalidType, $objectSupport), self::dump($val, $exceptionOnInvalidType, $objectSupport));
}
return sprintf('{ %s }', implode(', ', $output));
@ -355,7 +379,15 @@ class Inline
case 0 === strpos($scalar, '! '):
return intval(self::parseScalar(substr($scalar, 2)));
case 0 === strpos($scalar, '!!php/object:'):
return unserialize(substr($scalar, 13));
if (self::$objectSupport) {
return unserialize(substr($scalar, 13));
}
if (self::$exceptionOnInvalidType) {
throw new ParseException('Object support when parsing a YAML file has been disabled.');
}
return null;
case ctype_digit($scalar):
$raw = $scalar;
$cast = intval($scalar);

View File

@ -38,13 +38,15 @@ class Parser
/**
* Parses a YAML string to a PHP value.
*
* @param string $value A YAML string
* @param string $value A YAML string
* @param Boolean $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
* @param Boolean $objectSupport true if object support is enabled, false otherwise
*
* @return mixed A PHP value
*
* @throws ParseException If the YAML is not valid
*/
public function parse($value)
public function parse($value, $exceptionOnInvalidType = false, $objectSupport = false)
{
$this->currentLineNb = -1;
$this->currentLine = '';
@ -82,7 +84,7 @@ class Parser
$c = $this->getRealCurrentLineNb() + 1;
$parser = new Parser($c);
$parser->refs =& $this->refs;
$data[] = $parser->parse($this->getNextEmbedBlock());
$data[] = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport);
} else {
if (isset($values['leadspaces'])
&& ' ' == $values['leadspaces']
@ -98,12 +100,14 @@ class Parser
$block .= "\n".$this->getNextEmbedBlock($this->getCurrentLineIndentation() + 2);
}
$data[] = $parser->parse($block);
$data[] = $parser->parse($block, $exceptionOnInvalidType, $objectSupport);
} else {
$data[] = $this->parseValue($values['value']);
$data[] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport);
}
}
} elseif (preg_match('#^(?P<key>'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P<value>.+?))?\s*$#u', $this->currentLine, $values)) {
// force correct settings
Inline::parse(null, $exceptionOnInvalidType, $objectSupport);
try {
$key = Inline::parseScalar($values['key']);
} catch (ParseException $e) {
@ -128,7 +132,7 @@ class Parser
$c = $this->getRealCurrentLineNb() + 1;
$parser = new Parser($c);
$parser->refs =& $this->refs;
$parsed = $parser->parse($value);
$parsed = $parser->parse($value, $exceptionOnInvalidType, $objectSupport);
$merged = array();
if (!is_array($parsed)) {
@ -165,20 +169,20 @@ class Parser
$c = $this->getRealCurrentLineNb() + 1;
$parser = new Parser($c);
$parser->refs =& $this->refs;
$data[$key] = $parser->parse($this->getNextEmbedBlock());
$data[$key] = $parser->parse($this->getNextEmbedBlock(), $exceptionOnInvalidType, $objectSupport);
}
} else {
if ($isInPlace) {
$data = $this->refs[$isInPlace];
} else {
$data[$key] = $this->parseValue($values['value']);
$data[$key] = $this->parseValue($values['value'], $exceptionOnInvalidType, $objectSupport);
}
}
} else {
// 1-liner followed by newline
if (2 == count($this->lines) && empty($this->lines[1])) {
try {
$value = Inline::parse($this->lines[0]);
$value = Inline::parse($this->lines[0], $exceptionOnInvalidType, $objectSupport);
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);
@ -345,7 +349,7 @@ class Parser
*
* @throws ParseException When reference does not exist
*/
private function parseValue($value)
private function parseValue($value, $exceptionOnInvalidType, $objectSupport)
{
if (0 === strpos($value, '*')) {
if (false !== $pos = strpos($value, '#')) {
@ -368,7 +372,7 @@ class Parser
}
try {
return Inline::parse($value);
return Inline::parse($value, $exceptionOnInvalidType, $objectSupport);
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);

View File

@ -27,7 +27,7 @@ class Yaml
*
* @deprecated Deprecated since version 2.0, to be removed in 2.3.
*/
static public $enablePhpParsing = true;
public static $enablePhpParsing = true;
/**
* Enables PHP support when parsing YAML files.
@ -89,7 +89,7 @@ class Yaml
*
* @api
*/
public static function parse($input)
public static function parse($input, $exceptionOnInvalidType = false, $objectSupport = false)
{
// if input is a file, process it
$file = '';
@ -119,7 +119,7 @@ class Yaml
$yaml = new Parser();
try {
return $yaml->parse($input);
return $yaml->parse($input, $exceptionOnInvalidType, $objectSupport);
} catch (ParseException $e) {
if ($file) {
$e->setParsedFile($file);
@ -135,17 +135,19 @@ class Yaml
* The dump method, when supplied with an array, will do its best
* to convert the array into friendly YAML.
*
* @param array $array PHP array
* @param integer $inline The level where you switch to inline YAML
* @param array $array PHP array
* @param integer $inline The level where you switch to inline YAML
* @param Boolean $exceptionOnInvalidType true if an exception must be thrown on invalid types (a PHP resource or object), false otherwise
* @param Boolean $objectSupport true if object support is enabled, false otherwise
*
* @return string A YAML string representing the original PHP array
*
* @api
*/
public static function dump($array, $inline = 2)
public static function dump($array, $inline = 2, $exceptionOnInvalidType = false, $objectSupport = false)
{
$yaml = new Dumper();
return $yaml->dump($array, $inline);
return $yaml->dump($array, $inline, $exceptionOnInvalidType, $objectSupport);
}
}

View File

@ -152,11 +152,26 @@ EOF;
$this->assertEquals($expected, $this->dumper->dump($array, 10), '->dump() takes an inline level argument');
}
public function testObjectsSupport()
public function testObjectSupportEnabled()
{
$a = array('foo' => new A(), 'bar' => 1);
$dump = $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, false, true);
$this->assertEquals('{ foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\A":1:{s:1:"a";s:3:"foo";}, bar: 1 }', $this->dumper->dump($a), '->dump() is able to dump objects');
$this->assertEquals('{ foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\A":1:{s:1:"a";s:3:"foo";}, bar: 1 }', $dump, '->dump() is able to dump objects');
}
public function testObjectSupportDisabledButNoExceptions()
{
$dump = $this->dumper->dump(array('foo' => new A(), 'bar' => 1));
$this->assertEquals('{ foo: null, bar: 1 }', $dump, '->dump() does not dump objects when disabled');
}
/**
* @expectedException \Symfony\Component\Yaml\Exception\DumpException
*/
public function testObjectSupportDisabledWithExceptions()
{
$this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, true, false);
}
}

View File

@ -13,7 +13,6 @@ namespace Symfony\Tests\Component\Yaml;
use Symfony\Component\Yaml\Yaml;
use Symfony\Component\Yaml\Parser;
use Symfony\Component\Yaml\Exception\ParseException;
class ParserTest extends \PHPUnit_Framework_TestCase
{
@ -106,14 +105,31 @@ EOF;
$this->assertEquals('foo', $this->parser->parse($yaml));
}
public function testObjectsSupport()
public function testObjectSupportEnabled()
{
$b = array('foo' => new B(), 'bar' => 1);
$this->assertEquals($this->parser->parse(<<<EOF
$input = <<<EOF
foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}
bar: 1
EOF
), $b, '->parse() is able to dump objects');
EOF;
$this->assertEquals(array('foo' => new B(), 'bar' => 1), $this->parser->parse($input, false, true), '->parse() is able to parse objects');
}
public function testObjectSupportDisabledButNoExceptions()
{
$input = <<<EOF
foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}
bar: 1
EOF;
$this->assertEquals(array('foo' => null, 'bar' => 1), $this->parser->parse($input), '->parse() does not parse objects');
}
/**
* @expectedException \Symfony\Component\Yaml\Exception\ParseException
*/
public function testObjectsSupportDisabledWithExceptions()
{
$this->parser->parse('foo: !!php/object:O:30:"Symfony\Tests\Component\Yaml\B":1:{s:1:"b";s:3:"foo";}', true, false);
}
public function testNonUtf8Exception()