Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 6d7e910

Browse filesBrowse files
[Yaml] Add support for dumping null as an empty value by using the Yaml::DUMP_NULL_AS_EMPTY flag
1 parent f91514d commit 6d7e910
Copy full SHA for 6d7e910

File tree

Expand file treeCollapse file tree

6 files changed

+110
-11
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+110
-11
lines changed

‎src/Symfony/Component/Yaml/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/Yaml/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55
---
66

77
* Deprecate parsing duplicate mapping keys whose value is `null`
8+
* Add support for dumping `null` as an empty value by using the `Yaml::DUMP_NULL_AS_EMPTY` flag
89

910
7.1
1011
---

‎src/Symfony/Component/Yaml/Dumper.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Yaml/Dumper.php
+17-8Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ public function __construct(int $indentation = 4)
4545
* @param int-mask-of<Yaml::DUMP_*> $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
4646
*/
4747
public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags = 0): string
48+
{
49+
if ($flags & Yaml::DUMP_NULL_AS_EMPTY && $flags & Yaml::DUMP_NULL_AS_TILDE) {
50+
throw new \InvalidArgumentException('The Yaml::DUMP_NULL_AS_EMPTY and Yaml::DUMP_NULL_AS_TILDE flags cannot be used together.');
51+
}
52+
53+
return $this->doDump($input, $inline, $indent, $flags);
54+
}
55+
56+
private function doDump(mixed $input, int $inline = 0, int $indent = 0, int $flags = 0, int $nestingLevel = 0): string
4857
{
4958
$output = '';
5059
$prefix = $indent ? str_repeat(' ', $indent) : '';
@@ -55,9 +64,9 @@ public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags
5564
}
5665

5766
if ($inline <= 0 || (!\is_array($input) && !$input instanceof TaggedValue && $dumpObjectAsInlineMap) || !$input) {
58-
$output .= $prefix.Inline::dump($input, $flags);
67+
$output .= $prefix.Inline::dump($input, $flags, 0 === $nestingLevel);
5968
} elseif ($input instanceof TaggedValue) {
60-
$output .= $this->dumpTaggedValue($input, $inline, $indent, $flags, $prefix);
69+
$output .= $this->dumpTaggedValue($input, $inline, $indent, $flags, $prefix, $nestingLevel);
6170
} else {
6271
$dumpAsMap = Inline::isHash($input);
6372

@@ -109,10 +118,10 @@ public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags
109118
}
110119

111120
if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) {
112-
$output .= ' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n";
121+
$output .= ' '.$this->doDump($value->getValue(), $inline - 1, 0, $flags, $nestingLevel + 1)."\n";
113122
} else {
114123
$output .= "\n";
115-
$output .= $this->dump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : $indent + 2, $flags);
124+
$output .= $this->doDump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : $indent + 2, $flags, $nestingLevel + 1);
116125
}
117126

118127
continue;
@@ -130,15 +139,15 @@ public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags
130139
$prefix,
131140
$dumpAsMap ? Inline::dump($key, $flags).':' : '-',
132141
$willBeInlined ? ' ' : "\n",
133-
$this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags)
142+
$this->doDump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags, $nestingLevel + 1)
134143
).($willBeInlined ? "\n" : '');
135144
}
136145
}
137146

138147
return $output;
139148
}
140149

141-
private function dumpTaggedValue(TaggedValue $value, int $inline, int $indent, int $flags, string $prefix): string
150+
private function dumpTaggedValue(TaggedValue $value, int $inline, int $indent, int $flags, string $prefix, int $nestingLevel): string
142151
{
143152
$output = \sprintf('%s!%s', $prefix ? $prefix.' ' : '', $value->getTag());
144153

@@ -154,10 +163,10 @@ private function dumpTaggedValue(TaggedValue $value, int $inline, int $indent, i
154163
}
155164

156165
if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) {
157-
return $output.' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n";
166+
return $output.' '.$this->doDump($value->getValue(), $inline - 1, 0, $flags, $nestingLevel + 1)."\n";
158167
}
159168

160-
return $output."\n".$this->dump($value->getValue(), $inline - 1, $indent, $flags);
169+
return $output."\n".$this->doDump($value->getValue(), $inline - 1, $indent, $flags, $nestingLevel + 1);
161170
}
162171

163172
private function getBlockIndentationIndicator(string $value): string

‎src/Symfony/Component/Yaml/Inline.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Yaml/Inline.php
+7-3Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public static function parse(string $value, int $flags = 0, array &$references =
100100
*
101101
* @throws DumpException When trying to dump PHP resource
102102
*/
103-
public static function dump(mixed $value, int $flags = 0): string
103+
public static function dump(mixed $value, int $flags = 0, bool $rootLevel = false): string
104104
{
105105
switch (true) {
106106
case \is_resource($value):
@@ -138,7 +138,7 @@ public static function dump(mixed $value, int $flags = 0): string
138138
case \is_array($value):
139139
return self::dumpArray($value, $flags);
140140
case null === $value:
141-
return self::dumpNull($flags);
141+
return self::dumpNull($flags, $rootLevel);
142142
case true === $value:
143143
return 'true';
144144
case false === $value:
@@ -253,12 +253,16 @@ private static function dumpHashArray(array|\ArrayObject|\stdClass $value, int $
253253
return \sprintf('{ %s }', implode(', ', $output));
254254
}
255255

256-
private static function dumpNull(int $flags): string
256+
private static function dumpNull(int $flags, bool $rootLevel = false): string
257257
{
258258
if (Yaml::DUMP_NULL_AS_TILDE & $flags) {
259259
return '~';
260260
}
261261

262+
if (Yaml::DUMP_NULL_AS_EMPTY & $flags && !$rootLevel) {
263+
return '';
264+
}
265+
262266
return 'null';
263267
}
264268

‎src/Symfony/Component/Yaml/Tests/DumperTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Yaml/Tests/DumperTest.php
+57Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,63 @@ public function testObjectSupportDisabledWithExceptions()
216216
$this->dumper->dump(['foo' => new A(), 'bar' => 1], 0, 0, Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE);
217217
}
218218

219+
public function testDumpWithMultipleNullFlagsFormatsThrows()
220+
{
221+
$this->expectException(\InvalidArgumentException::class);
222+
$this->expectExceptionMessage('The Yaml::DUMP_NULL_AS_EMPTY and Yaml::DUMP_NULL_AS_TILDE flags cannot be used together.');
223+
224+
$this->dumper->dump(['foo' => 'bar'], 0, 0, Yaml::DUMP_NULL_AS_EMPTY | Yaml::DUMP_NULL_AS_TILDE);
225+
}
226+
227+
public function testDumpNullAsEmptyInExpandedMapping()
228+
{
229+
$expected = "qux:\n foo: bar\n baz: \n";
230+
231+
$this->assertSame($expected, $this->dumper->dump(['qux' => ['foo' => 'bar', 'baz' => null]], 2, flags: Yaml::DUMP_NULL_AS_EMPTY));
232+
}
233+
234+
public function testDumpNullAsEmptyWithObject()
235+
{
236+
$class = new \stdClass();
237+
$class->foo = 'bar';
238+
$class->baz = null;
239+
240+
$this->assertSame("foo: bar\nbaz: \n", $this->dumper->dump($class, 2, flags: Yaml::DUMP_NULL_AS_EMPTY | Yaml::DUMP_OBJECT_AS_MAP));
241+
}
242+
243+
public function testDumpNullAsEmptyDumpsWhenInInlineMapping()
244+
{
245+
$expected = "foo: \nqux: { foo: bar, baz: }\n";
246+
247+
$this->assertSame($expected, $this->dumper->dump(['foo' => null, 'qux' => ['foo' => 'bar', 'baz' => null]], 1, flags: Yaml::DUMP_NULL_AS_EMPTY));
248+
}
249+
250+
public function testDumpNullAsEmptyDumpsNestedMaps()
251+
{
252+
$expected = "foo: \nqux:\n foo: bar\n baz: \n";
253+
254+
$this->assertSame($expected, $this->dumper->dump(['foo' => null, 'qux' => ['foo' => 'bar', 'baz' => null]], 10, flags: Yaml::DUMP_NULL_AS_EMPTY));
255+
}
256+
257+
public function testDumpNullAsEmptyInExpandedSequence()
258+
{
259+
$expected = "qux:\n - foo\n - \n - bar\n";
260+
261+
$this->assertSame($expected, $this->dumper->dump(['qux' => ['foo', null, 'bar']], 2, flags: Yaml::DUMP_NULL_AS_EMPTY));
262+
}
263+
264+
public function testDumpNullAsEmptyWhenInInlineSequence()
265+
{
266+
$expected = "foo: \nqux: [foo, , bar]\n";
267+
268+
$this->assertSame($expected, $this->dumper->dump(['foo' => null, 'qux' => ['foo', null, 'bar']], 1, flags: Yaml::DUMP_NULL_AS_EMPTY));
269+
}
270+
271+
public function testDumpNullAsEmptyAtRoot()
272+
{
273+
$this->assertSame('null', $this->dumper->dump(null, 2, flags: Yaml::DUMP_NULL_AS_EMPTY));
274+
}
275+
219276
/**
220277
* @dataProvider getEscapeSequences
221278
*/

‎src/Symfony/Component/Yaml/Tests/ParserTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Yaml/Tests/ParserTest.php
+27Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,33 @@ public function testTopLevelNull()
5252
$this->assertSameData($expected, $data);
5353
}
5454

55+
public function testEmptyValueInExpandedMappingIsSupported()
56+
{
57+
$yml = <<<'YAML'
58+
foo:
59+
bar:
60+
baz: qux
61+
YAML;
62+
63+
$data = $this->parser->parse($yml);
64+
$expected = ['foo' => ['bar' => null, 'baz' => 'qux']];
65+
$this->assertSameData($expected, $data);
66+
}
67+
68+
public function testEmptyValueInExpandedSequenceIsSupported()
69+
{
70+
$yml = <<<'YAML'
71+
foo:
72+
- bar
73+
-
74+
- baz
75+
YAML;
76+
77+
$data = $this->parser->parse($yml);
78+
$expected = ['foo' => ['bar', null, 'baz']];
79+
$this->assertSameData($expected, $data);
80+
}
81+
5582
public function testTaggedValueTopLevelNumber()
5683
{
5784
$yml = '!number 5';

‎src/Symfony/Component/Yaml/Yaml.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Yaml/Yaml.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class Yaml
3535
public const DUMP_EMPTY_ARRAY_AS_SEQUENCE = 1024;
3636
public const DUMP_NULL_AS_TILDE = 2048;
3737
public const DUMP_NUMERIC_KEY_AS_STRING = 4096;
38+
public const DUMP_NULL_AS_EMPTY = 8192;
3839

3940
/**
4041
* Parses a YAML file into a PHP value.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.