From 4f5c14979880ce0c07fbb98ebeaa20b601c22485 Mon Sep 17 00:00:00 2001 From: Asmir Mustafic Date: Wed, 12 Apr 2017 12:55:45 +0200 Subject: [PATCH 1/3] Test case for not in-lined map-objects --- .../Component/Yaml/Tests/DumperTest.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/Symfony/Component/Yaml/Tests/DumperTest.php b/src/Symfony/Component/Yaml/Tests/DumperTest.php index 844177dbcd107..4533c6050c35e 100644 --- a/src/Symfony/Component/Yaml/Tests/DumperTest.php +++ b/src/Symfony/Component/Yaml/Tests/DumperTest.php @@ -206,6 +206,25 @@ public function testInlineLevel() $this->assertEquals($expected, $this->dumper->dump($this->array, 10), '->dump() takes an inline level argument'); } + public function testArrayObjectAsMapNotInLined() + { + $deep = new \ArrayObject(array('deep1' => 'd', 'deep2' => 'e')); + $inner = new \ArrayObject(array('inner1' => 'b', 'inner2' => 'c', 'inner3' => $deep)); + $outer = new \ArrayObject(array('outer1' => 'a', 'outer1' => $inner)); + + $yaml = $this->dumper->dump($outer, 2, 0, Yaml::DUMP_OBJECT_AS_MAP); + + $expected = <<assertEquals($expected, $yaml); + } + public function testObjectSupportEnabled() { $dump = $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, Yaml::DUMP_OBJECT); From 44adf72860f4365efc1e6ebc0bb99a4204526c79 Mon Sep 17 00:00:00 2001 From: Christian Flothmann Date: Wed, 12 Apr 2017 21:41:43 +0200 Subject: [PATCH 2/3] respect inline level when dumping objects as maps --- src/Symfony/Component/Yaml/Dumper.php | 17 +++++ src/Symfony/Component/Yaml/Inline.php | 4 - .../Component/Yaml/Tests/DumperTest.php | 76 ++++++++++++++----- 3 files changed, 74 insertions(+), 23 deletions(-) diff --git a/src/Symfony/Component/Yaml/Dumper.php b/src/Symfony/Component/Yaml/Dumper.php index 98d82434c2388..d40be199ef0ea 100644 --- a/src/Symfony/Component/Yaml/Dumper.php +++ b/src/Symfony/Component/Yaml/Dumper.php @@ -82,6 +82,10 @@ public function dump($input, $inline = 0, $indent = 0, $flags = 0) $output = ''; $prefix = $indent ? str_repeat(' ', $indent) : ''; + if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($input instanceof \ArrayObject || $input instanceof \stdClass)) { + $input = $this->castObjectToArray($input); + } + if ($inline <= 0 || !is_array($input) || empty($input)) { $output .= $prefix.Inline::dump($input, $flags); } else { @@ -111,4 +115,17 @@ public function dump($input, $inline = 0, $indent = 0, $flags = 0) return $output; } + + private function castObjectToArray($object) + { + $array = (array) $object; + + foreach ($array as $key => $value) { + if ($value instanceof \ArrayObject || $value instanceof \stdClass) { + $array[$key] = $this->castObjectToArray($value); + } + } + + return $array; + } } diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index eb3ffed6f3690..d01f0ba349814 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -163,10 +163,6 @@ public static function dump($value, $flags = 0) return '!php/object:'.serialize($value); } - if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) { - return self::dumpArray((array) $value, $flags); - } - if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) { throw new DumpException('Object support when dumping a YAML file has been disabled.'); } diff --git a/src/Symfony/Component/Yaml/Tests/DumperTest.php b/src/Symfony/Component/Yaml/Tests/DumperTest.php index 4533c6050c35e..9c5bc2d66522e 100644 --- a/src/Symfony/Component/Yaml/Tests/DumperTest.php +++ b/src/Symfony/Component/Yaml/Tests/DumperTest.php @@ -206,25 +206,6 @@ public function testInlineLevel() $this->assertEquals($expected, $this->dumper->dump($this->array, 10), '->dump() takes an inline level argument'); } - public function testArrayObjectAsMapNotInLined() - { - $deep = new \ArrayObject(array('deep1' => 'd', 'deep2' => 'e')); - $inner = new \ArrayObject(array('inner1' => 'b', 'inner2' => 'c', 'inner3' => $deep)); - $outer = new \ArrayObject(array('outer1' => 'a', 'outer1' => $inner)); - - $yaml = $this->dumper->dump($outer, 2, 0, Yaml::DUMP_OBJECT_AS_MAP); - - $expected = <<assertEquals($expected, $yaml); - } - public function testObjectSupportEnabled() { $dump = $this->dumper->dump(array('foo' => new A(), 'bar' => 1), 0, 0, Yaml::DUMP_OBJECT); @@ -352,6 +333,63 @@ public function objectAsMapProvider() return $tests; } + public function testDumpEmptyArrayObjectInstanceAsMap() + { + $this->assertSame('{ }', $this->dumper->dump(new \ArrayObject(), 2, 0, Yaml::DUMP_OBJECT_AS_MAP)); + } + + public function testDumpingArrayObjectInstancesRespectsInlineLevel() + { + $deep = new \ArrayObject(array('deep1' => 'd', 'deep2' => 'e')); + $inner = new \ArrayObject(array('inner1' => 'b', 'inner2' => 'c', 'inner3' => $deep)); + $outer = new \ArrayObject(array('outer1' => 'a', 'outer2' => $inner)); + + $yaml = $this->dumper->dump($outer, 2, 0, Yaml::DUMP_OBJECT_AS_MAP); + + $expected = <<assertSame($expected, $yaml); + } + + public function testDumpEmptyStdClassInstanceAsMap() + { + $this->assertSame('{ }', $this->dumper->dump(new \stdClass(), 2, 0, Yaml::DUMP_OBJECT_AS_MAP)); + } + + public function testDumpingStdClassInstancesRespectsInlineLevel() + { + $deep = new \stdClass(); + $deep->deep1 = 'd'; + $deep->deep2 = 'e'; + + $inner = new \stdClass(); + $inner->inner1 = 'b'; + $inner->inner2 = 'c'; + $inner->inner3 = $deep; + + $outer = new \stdClass(); + $outer->outer1 = 'a'; + $outer->outer2 = $inner; + + $yaml = $this->dumper->dump($outer, 2, 0, Yaml::DUMP_OBJECT_AS_MAP); + + $expected = <<assertSame($expected, $yaml); + } + public function testDumpMultiLineStringAsScalarBlock() { $data = array( From d53fe491240668cfd8b459c2058537921edbbe33 Mon Sep 17 00:00:00 2001 From: Asmir Mustafic Date: Thu, 13 Apr 2017 13:14:06 +0200 Subject: [PATCH 3/3] Make sure that array/object maps are always dumped as maps and indentation is respected --- src/Symfony/Component/Yaml/Dumper.php | 32 ++++++++++------- src/Symfony/Component/Yaml/Inline.php | 17 ++++++++++ .../Component/Yaml/Tests/DumperTest.php | 34 ++++++++++++++++++- 3 files changed, 69 insertions(+), 14 deletions(-) diff --git a/src/Symfony/Component/Yaml/Dumper.php b/src/Symfony/Component/Yaml/Dumper.php index d40be199ef0ea..5e03d7d67d508 100644 --- a/src/Symfony/Component/Yaml/Dumper.php +++ b/src/Symfony/Component/Yaml/Dumper.php @@ -82,14 +82,16 @@ public function dump($input, $inline = 0, $indent = 0, $flags = 0) $output = ''; $prefix = $indent ? str_repeat(' ', $indent) : ''; - if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($input instanceof \ArrayObject || $input instanceof \stdClass)) { - $input = $this->castObjectToArray($input); - } + $isObjectMap = self::isNotEmptyMap($input, $flags); - if ($inline <= 0 || !is_array($input) || empty($input)) { + if ($inline <= 0 || (!is_array($input) && !$isObjectMap) || empty($input)) { $output .= $prefix.Inline::dump($input, $flags); } else { - $isAHash = Inline::isHash($input); + $isAHash = $isObjectMap || Inline::isHash($input, $flags); + + if ($isObjectMap) { + $input = (array) $input; + } foreach ($input as $key => $value) { if ($inline >= 1 && Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && is_string($value) && false !== strpos($value, "\n")) { @@ -102,7 +104,7 @@ public function dump($input, $inline = 0, $indent = 0, $flags = 0) continue; } - $willBeInlined = $inline - 1 <= 0 || !is_array($value) || empty($value); + $willBeInlined = $inline - 1 <= 0 || (!is_array($value) && !self::isNotEmptyMap($value, $flags)) || empty($value); $output .= sprintf('%s%s%s%s', $prefix, @@ -116,16 +118,20 @@ public function dump($input, $inline = 0, $indent = 0, $flags = 0) return $output; } - private function castObjectToArray($object) + private static function isNotEmptyMap($input, $flags) { - $array = (array) $object; + if ($flags & ~Yaml::DUMP_OBJECT_AS_MAP) { + return false; + } - foreach ($array as $key => $value) { - if ($value instanceof \ArrayObject || $value instanceof \stdClass) { - $array[$key] = $this->castObjectToArray($value); - } + if ($input instanceof \ArrayObject && 0 < count($input)) { + return true; + } + + if ($input instanceof \stdClass && 0 < count((array) $input)) { + return true; } - return $array; + return false; } } diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index d01f0ba349814..53178b635cf01 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -163,6 +163,10 @@ public static function dump($value, $flags = 0) return '!php/object:'.serialize($value); } + if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \stdClass || $value instanceof \ArrayObject)) { + return self::dumpMap((array) $value, $flags); + } + if (Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE & $flags) { throw new DumpException('Object support when dumping a YAML file has been disabled.'); } @@ -257,6 +261,19 @@ private static function dumpArray($value, $flags) return sprintf('[%s]', implode(', ', $output)); } + return self::dumpMap($value, $flags); + } + + /** + * Dumps a PHP array to a YAML string as map. + * + * @param array $value The PHP array to dump + * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string + * + * @return string The YAML string representing the PHP array as map + */ + private static function dumpMap($value, $flags) + { // hash $output = array(); foreach ($value as $key => $val) { diff --git a/src/Symfony/Component/Yaml/Tests/DumperTest.php b/src/Symfony/Component/Yaml/Tests/DumperTest.php index 9c5bc2d66522e..95002543023cc 100644 --- a/src/Symfony/Component/Yaml/Tests/DumperTest.php +++ b/src/Symfony/Component/Yaml/Tests/DumperTest.php @@ -12,8 +12,8 @@ namespace Symfony\Component\Yaml\Tests; use PHPUnit\Framework\TestCase; -use Symfony\Component\Yaml\Parser; use Symfony\Component\Yaml\Dumper; +use Symfony\Component\Yaml\Parser; use Symfony\Component\Yaml\Yaml; class DumperTest extends TestCase @@ -390,6 +390,38 @@ public function testDumpingStdClassInstancesRespectsInlineLevel() $this->assertSame($expected, $yaml); } + public function testDumpingArrayObjectInstancesWithNumericKeysRespectsInlineLevel() + { + $deep = new \ArrayObject(array('d', 'e')); + $inner = new \ArrayObject(array('b', 'c', $deep)); + $outer = new \ArrayObject(array('a', $inner)); + + $yaml = $this->dumper->dump($outer, 2, 0, Yaml::DUMP_OBJECT_AS_MAP); + + $expected = <<assertEquals($expected, $yaml); + } + + public function testDumpingArrayObjectInstancesWithNumericKeysInlined() + { + $deep = new \ArrayObject(array('d', 'e')); + $inner = new \ArrayObject(array('b', 'c', $deep)); + $outer = new \ArrayObject(array('a', $inner)); + + $yaml = $this->dumper->dump($outer, 0, 0, Yaml::DUMP_OBJECT_AS_MAP); + $expected = <<assertEquals($expected, $yaml); + } + public function testDumpMultiLineStringAsScalarBlock() { $data = array(