Skip to content

Commit

Permalink
added a way to enable/disable object support when parsing/dumping
Browse files Browse the repository at this point in the history
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);
  • Loading branch information
fabpot committed Jan 17, 2013
1 parent ac756bf commit ba6e315
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 51 deletions.
10 changes: 5 additions & 5 deletions src/Symfony/Bridge/Twig/Extension/YamlExtension.php
Expand Up @@ -31,28 +31,28 @@ public function getFilters()
);
}

public function encode($input, $inline = 0)
public function encode($input, $inline = 0, $dumpObjects = false)
{
static $dumper;

if (null === $dumper) {
$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);
}

/**
Expand Down
16 changes: 9 additions & 7 deletions src/Symfony/Component/Yaml/Dumper.php
Expand Up @@ -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);

Expand All @@ -42,9 +44,9 @@ public function dump($input, $inline = 0, $indent = 0)

$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" : '');
}
}
Expand Down
56 changes: 44 additions & 12 deletions src/Symfony/Component/Yaml/Inline.php
Expand Up @@ -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)) {
Expand Down Expand Up @@ -63,21 +71,35 @@ public static function parse($value)
/**
* 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:
Expand Down Expand Up @@ -115,11 +137,13 @@ public static function dump($value)
/**
* 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);
Expand All @@ -128,7 +152,7 @@ private static function dumpArray($value)
) {
$output = array();
foreach ($value as $val) {
$output[] = self::dump($val);
$output[] = self::dump($val, $exceptionOnInvalidType, $objectSupport);
}

return sprintf('[%s]', implode(', ', $output));
Expand All @@ -137,7 +161,7 @@ private static function dumpArray($value)
// 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));
Expand Down Expand Up @@ -355,7 +379,15 @@ private static function evaluateScalar($scalar)
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);
Expand Down
26 changes: 15 additions & 11 deletions src/Symfony/Component/Yaml/Parser.php
Expand Up @@ -38,13 +38,15 @@ public function __construct($offset = 0)
/**
* 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 = '';
Expand Down Expand Up @@ -82,7 +84,7 @@ public function parse($value)
$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']
Expand All @@ -98,12 +100,14 @@ public function parse($value)
$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) {
Expand All @@ -128,7 +132,7 @@ public function parse($value)
$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)) {
Expand Down Expand Up @@ -165,20 +169,20 @@ public function parse($value)
$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);
Expand Down Expand Up @@ -345,7 +349,7 @@ private function moveToPreviousLine()
*
* @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, '#')) {
Expand All @@ -368,7 +372,7 @@ private function parseValue($value)
}

try {
return Inline::parse($value);
return Inline::parse($value, $exceptionOnInvalidType, $objectSupport);
} catch (ParseException $e) {
$e->setParsedLine($this->getRealCurrentLineNb() + 1);
$e->setSnippet($this->currentLine);
Expand Down
16 changes: 9 additions & 7 deletions src/Symfony/Component/Yaml/Yaml.php
Expand Up @@ -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.
Expand Down Expand Up @@ -89,7 +89,7 @@ public static function supportsPhpParsing()
*
* @api
*/
public static function parse($input)
public static function parse($input, $exceptionOnInvalidType = false, $objectSupport = false)
{
// if input is a file, process it
$file = '';
Expand Down Expand Up @@ -119,7 +119,7 @@ public static function parse($input)
$yaml = new Parser();

try {
return $yaml->parse($input);
return $yaml->parse($input, $exceptionOnInvalidType, $objectSupport);
} catch (ParseException $e) {
if ($file) {
$e->setParsedFile($file);
Expand All @@ -135,17 +135,19 @@ public static function parse($input)
* 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);
}
}
21 changes: 18 additions & 3 deletions tests/Symfony/Tests/Component/Yaml/DumperTest.php
Expand Up @@ -152,11 +152,26 @@ public function testInlineLevel()
$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);
}
}

Expand Down

0 comments on commit ba6e315

Please sign in to comment.