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 0e0246b

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

File tree

6 files changed

+111
-16
lines changed
Filter options

6 files changed

+111
-16
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
+18-13Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,18 @@ public function __construct(int $indentation = 4)
3939
/**
4040
* Dumps a PHP value to YAML.
4141
*
42-
* @param mixed $input The PHP value
43-
* @param int $inline The level where you switch to inline YAML
44-
* @param int $indent The level of indentation (used internally)
45-
* @param int-mask-of<Yaml::DUMP_*> $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
42+
* @param mixed $input The PHP value
43+
* @param int $inline The level where you switch to inline YAML
44+
* @param int $indent The level of indentation (used internally)
45+
* @param int-mask-of<Yaml::DUMP_*> $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
46+
* @param int $nestingLevel The level of nesting (used internally)
4647
*/
47-
public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags = 0): string
48+
public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags = 0, int $nestingLevel = 0): string
4849
{
50+
if ($flags & Yaml::DUMP_NULL_AS_EMPTY && $flags & Yaml::DUMP_NULL_AS_TILDE) {
51+
throw new \InvalidArgumentException('The Yaml::DUMP_NULL_AS_EMPTY and Yaml::DUMP_NULL_AS_TILDE flags cannot be used together.');
52+
}
53+
4954
$output = '';
5055
$prefix = $indent ? str_repeat(' ', $indent) : '';
5156
$dumpObjectAsInlineMap = true;
@@ -55,9 +60,9 @@ public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags
5560
}
5661

5762
if ($inline <= 0 || (!\is_array($input) && !$input instanceof TaggedValue && $dumpObjectAsInlineMap) || !$input) {
58-
$output .= $prefix.Inline::dump($input, $flags);
63+
$output .= $prefix.Inline::dump($input, $flags, 0 === $nestingLevel);
5964
} elseif ($input instanceof TaggedValue) {
60-
$output .= $this->dumpTaggedValue($input, $inline, $indent, $flags, $prefix);
65+
$output .= $this->dumpTaggedValue($input, $inline, $indent, $flags, $prefix, $nestingLevel);
6166
} else {
6267
$dumpAsMap = Inline::isHash($input);
6368

@@ -109,10 +114,10 @@ public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags
109114
}
110115

111116
if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) {
112-
$output .= ' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n";
117+
$output .= ' '.$this->dump($value->getValue(), $inline - 1, 0, $flags, $nestingLevel + 1)."\n";
113118
} else {
114119
$output .= "\n";
115-
$output .= $this->dump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : $indent + 2, $flags);
120+
$output .= $this->dump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : $indent + 2, $flags, $nestingLevel + 1);
116121
}
117122

118123
continue;
@@ -130,15 +135,15 @@ public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags
130135
$prefix,
131136
$dumpAsMap ? Inline::dump($key, $flags).':' : '-',
132137
$willBeInlined ? ' ' : "\n",
133-
$this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags)
138+
$this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags, $nestingLevel + 1)
134139
).($willBeInlined ? "\n" : '');
135140
}
136141
}
137142

138143
return $output;
139144
}
140145

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

@@ -154,10 +159,10 @@ private function dumpTaggedValue(TaggedValue $value, int $inline, int $indent, i
154159
}
155160

156161
if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) {
157-
return $output.' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n";
162+
return $output.' '.$this->dump($value->getValue(), $inline - 1, 0, $flags, $nestingLevel + 1)."\n";
158163
}
159164

160-
return $output."\n".$this->dump($value->getValue(), $inline - 1, $indent, $flags);
165+
return $output."\n".$this->dump($value->getValue(), $inline - 1, $indent, $flags, $nestingLevel + 1);
161166
}
162167

163168
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.