From ba6e3159c0eeb3b6e21db32fce8fa2535cb3aa77 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Thu, 17 Jan 2013 09:34:45 +0100 Subject: [PATCH] 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); --- .../Bridge/Twig/Extension/YamlExtension.php | 10 ++-- src/Symfony/Component/Yaml/Dumper.php | 16 +++--- src/Symfony/Component/Yaml/Inline.php | 56 +++++++++++++++---- src/Symfony/Component/Yaml/Parser.php | 26 +++++---- src/Symfony/Component/Yaml/Yaml.php | 16 +++--- .../Tests/Component/Yaml/DumperTest.php | 21 ++++++- .../Tests/Component/Yaml/ParserTest.php | 28 ++++++++-- 7 files changed, 122 insertions(+), 51 deletions(-) diff --git a/src/Symfony/Bridge/Twig/Extension/YamlExtension.php b/src/Symfony/Bridge/Twig/Extension/YamlExtension.php index 130575cb6860..f88829ee34d2 100644 --- a/src/Symfony/Bridge/Twig/Extension/YamlExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/YamlExtension.php @@ -31,7 +31,7 @@ public function getFilters() ); } - public function encode($input, $inline = 0) + public function encode($input, $inline = 0, $dumpObjects = false) { static $dumper; @@ -39,20 +39,20 @@ public function encode($input, $inline = 0) $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); } /** diff --git a/src/Symfony/Component/Yaml/Dumper.php b/src/Symfony/Component/Yaml/Dumper.php index ec74cfc478e5..9829a49c9347 100644 --- a/src/Symfony/Component/Yaml/Dumper.php +++ b/src/Symfony/Component/Yaml/Dumper.php @@ -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 @@ 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" : ''); } } diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index 02968d15f987..2f7855d131f0 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -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 @@ 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: @@ -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); @@ -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)); @@ -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)); @@ -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); diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index 0bc42506095b..1c0fdd828a34 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -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 = ''; @@ -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'] @@ -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'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P.+?))?\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 @@ 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)) { @@ -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); @@ -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, '#')) { @@ -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); diff --git a/src/Symfony/Component/Yaml/Yaml.php b/src/Symfony/Component/Yaml/Yaml.php index f5e3a0e9b45b..ae175f1b0e32 100644 --- a/src/Symfony/Component/Yaml/Yaml.php +++ b/src/Symfony/Component/Yaml/Yaml.php @@ -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 @@ 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 = ''; @@ -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); @@ -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); } } diff --git a/tests/Symfony/Tests/Component/Yaml/DumperTest.php b/tests/Symfony/Tests/Component/Yaml/DumperTest.php index 33b397868376..83e08ca8cbde 100644 --- a/tests/Symfony/Tests/Component/Yaml/DumperTest.php +++ b/tests/Symfony/Tests/Component/Yaml/DumperTest.php @@ -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); } } diff --git a/tests/Symfony/Tests/Component/Yaml/ParserTest.php b/tests/Symfony/Tests/Component/Yaml/ParserTest.php index 6a684ed99f2e..4b3f3bf273fa 100644 --- a/tests/Symfony/Tests/Component/Yaml/ParserTest.php +++ b/tests/Symfony/Tests/Component/Yaml/ParserTest.php @@ -13,7 +13,6 @@ 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 @@ public function testEndOfTheDocumentMarker() $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(<<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 = <<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()