diff --git a/src/Symfony/Bridge/Twig/Extension/YamlExtension.php b/src/Symfony/Bridge/Twig/Extension/YamlExtension.php index 130575cb68..f88829ee34 100644 --- a/src/Symfony/Bridge/Twig/Extension/YamlExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/YamlExtension.php @@ -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); } /** diff --git a/src/Symfony/Component/Console/Input/ArgvInput.php b/src/Symfony/Component/Console/Input/ArgvInput.php index 3ca9b0521e..9addf1483d 100644 --- a/src/Symfony/Component/Console/Input/ArgvInput.php +++ b/src/Symfony/Component/Console/Input/ArgvInput.php @@ -215,12 +215,14 @@ class ArgvInput extends Input $option = $this->definition->getOption($name); - if (null === $value && $option->acceptValue()) { + if (null === $value && $option->acceptValue() && count($this->parsed)) { // if option accepts an optional or mandatory argument // let's see if there is one provided $next = array_shift($this->parsed); - if ('-' !== $next[0]) { + if (isset($next[0]) && '-' !== $next[0]) { $value = $next; + } elseif (empty($next)) { + $value = ''; } else { array_unshift($this->parsed, $next); } diff --git a/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php b/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php index cf63177005..fc7ab21834 100644 --- a/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php +++ b/src/Symfony/Component/Console/Tests/Input/ArgvInputTest.php @@ -71,6 +71,18 @@ class ArgvInputTest extends \PHPUnit_Framework_TestCase $input->bind(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_REQUIRED)))); $this->assertEquals(array('foo' => 'bar'), $input->getOptions(), '->parse() parses short options with a required value (with a space separator)'); + $input = new ArgvInput(array('cli.php', '-f', '')); + $input->bind(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)))); + $this->assertEquals(array('foo' => ''), $input->getOptions(), '->parse() parses short options with an optional empty value'); + + $input = new ArgvInput(array('cli.php', '-f', '', 'foo')); + $input->bind(new InputDefinition(array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL)))); + $this->assertEquals(array('foo' => ''), $input->getOptions(), '->parse() parses short options with an optional empty value followed by an argument'); + + $input = new ArgvInput(array('cli.php', '-f', '', '-b')); + $input->bind(new InputDefinition(array(new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')))); + $this->assertEquals(array('foo' => '', 'bar' => true), $input->getOptions(), '->parse() parses short options with an optional empty value followed by an option'); + $input = new ArgvInput(array('cli.php', '-f', '-b', 'foo')); $input->bind(new InputDefinition(array(new InputArgument('name'), new InputOption('foo', 'f', InputOption::VALUE_OPTIONAL), new InputOption('bar', 'b')))); $this->assertEquals(array('foo' => null, 'bar' => true), $input->getOptions(), '->parse() parses short options with an optional value which is not present'); diff --git a/src/Symfony/Component/Yaml/Dumper.php b/src/Symfony/Component/Yaml/Dumper.php index 220cb39742..fa1cd6a3e0 100644 --- a/src/Symfony/Component/Yaml/Dumper.php +++ b/src/Symfony/Component/Yaml/Dumper.php @@ -38,19 +38,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); @@ -59,9 +61,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 + $this->indentation) + $this->dump($value, $inline - 1, $exceptionOnInvalidType, $objectSupport, $willBeInlined ? 0 : $indent + $this->indentation) ).($willBeInlined ? "\n" : ''); } } diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index df8928577c..92bfb52726 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)) { @@ -69,21 +77,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: @@ -121,11 +143,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); @@ -134,7 +158,7 @@ class Inline ) { $output = array(); foreach ($value as $val) { - $output[] = self::dump($val); + $output[] = self::dump($val, $exceptionOnInvalidType, $objectSupport); } return sprintf('[%s]', implode(', ', $output)); @@ -143,7 +167,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)); @@ -373,7 +397,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); diff --git a/src/Symfony/Component/Yaml/Parser.php b/src/Symfony/Component/Yaml/Parser.php index d09227b279..1d87d94a19 100644 --- a/src/Symfony/Component/Yaml/Parser.php +++ b/src/Symfony/Component/Yaml/Parser.php @@ -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 = ''; @@ -88,7 +90,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'] @@ -104,9 +106,9 @@ 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'.Inline::REGEX_QUOTED_STRING.'|[^ \'"\[\{].*?) *\:(\s+(?P.+?))?\s*$#u', $this->currentLine, $values)) { @@ -115,6 +117,8 @@ class Parser } $context = 'mapping'; + // force correct settings + Inline::parse(null, $exceptionOnInvalidType, $objectSupport); try { $key = Inline::parseScalar($values['key']); } catch (ParseException $e) { @@ -139,7 +143,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)) { @@ -176,20 +180,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); @@ -366,7 +370,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, '#')) { @@ -389,7 +393,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); diff --git a/src/Symfony/Component/Yaml/Tests/DumperTest.php b/src/Symfony/Component/Yaml/Tests/DumperTest.php index c662eadb8d..89e6924add 100644 --- a/src/Symfony/Component/Yaml/Tests/DumperTest.php +++ b/src/Symfony/Component/Yaml/Tests/DumperTest.php @@ -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\Component\Yaml\Tests\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\Component\Yaml\Tests\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/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index e27482a694..6980523018 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -13,7 +13,6 @@ namespace Symfony\Component\Yaml\Tests; 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(<<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() diff --git a/src/Symfony/Component/Yaml/Yaml.php b/src/Symfony/Component/Yaml/Yaml.php index 85cdf3db66..ed340b4152 100644 --- a/src/Symfony/Component/Yaml/Yaml.php +++ b/src/Symfony/Component/Yaml/Yaml.php @@ -22,13 +22,53 @@ use Symfony\Component\Yaml\Exception\ParseException; */ class Yaml { + /** + * Be warned that PHP support will be removed in Symfony 2.3. + * + * @deprecated Deprecated since version 2.0, to be removed in 2.3. + */ public static $enablePhpParsing = false; + /** + * Enables PHP support when parsing YAML files. + * + * Be warned that PHP support will be removed in Symfony 2.3. + * + * @deprecated Deprecated since version 2.0, to be removed in 2.3. + */ public static function enablePhpParsing() { self::$enablePhpParsing = true; } + /** + * Sets the PHP support flag when parsing YAML files. + * + * Be warned that PHP support will be removed in Symfony 2.3. + * + * @param Boolean $boolean true if PHP parsing support is enabled, false otherwise + * + * @deprecated Deprecated since version 2.0, to be removed in 2.3. + */ + public static function setPhpParsing($boolean) + { + self::$enablePhpParsing = (Boolean) $boolean; + } + + /** + * Checks if PHP support is enabled when parsing YAML files. + * + * Be warned that PHP support will be removed in Symfony 2.3. + * + * @return Boolean true if PHP parsing support is enabled, false otherwise + * + * @deprecated Deprecated since version 2.0, to be removed in 2.3. + */ + public static function supportsPhpParsing() + { + return self::$enablePhpParsing; + } + /** * Parses YAML into a PHP array. * @@ -49,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 = ''; @@ -79,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); @@ -95,19 +135,21 @@ 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 integer $indent The amount of spaces to use for indentation of nested nodes. + * @param array $array PHP array + * @param integer $inline The level where you switch to inline YAML + * @param integer $indent The amount of spaces to use for indentation of nested nodes. + * @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, $indent = 4) + public static function dump($array, $inline = 2, $indent = 4, $exceptionOnInvalidType = false, $objectSupport = false) { $yaml = new Dumper(); $yaml->setIndentation($indent); - return $yaml->dump($array, $inline); + return $yaml->dump($array, $inline, $exceptionOnInvalidType, $objectSupport); } }