Skip to content

Navigation Menu

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 da15553

Browse filesBrowse files
committed
feature #58243 [Yaml] Add support for dumping null as an empty value by using the Yaml::DUMP_NULL_AS_EMPTY flag (alexandre-daubois)
This PR was merged into the 7.3 branch. Discussion ---------- [Yaml] Add support for dumping `null` as an empty value by using the `Yaml::DUMP_NULL_AS_EMPTY` flag | Q | A | ------------- | --- | Branch? | 7.2 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | Fix #58155 | License | MIT Another try after #58232. I went for the second solution from `@stof` (#58232 (comment)), namely dumping empty value as null just in expanded sequences and mappings. When the inline limit of the dumper is reached, it fallbacks on using `null` (examples in tests). I'm not 100% happy about the name of the constant, `DUMP_NULL_AS_EMPTY_IN_EXPANDED_NOTATION`. It's verbose. I'm open to suggestions! Commits ------- 1fc1379 [Yaml] Add support for dumping `null` as an empty value by using the `Yaml::DUMP_NULL_AS_EMPTY` flag
2 parents 4078cb1 + 1fc1379 commit da15553
Copy full SHA for da15553

File tree

6 files changed

+110
-11
lines changed
Filter options

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
+1
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-8
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ public function __construct(private int $indentation = 4)
4141
* @param int-mask-of<Yaml::DUMP_*> $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
4242
*/
4343
public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags = 0): string
44+
{
45+
if ($flags & Yaml::DUMP_NULL_AS_EMPTY && $flags & Yaml::DUMP_NULL_AS_TILDE) {
46+
throw new \InvalidArgumentException('The Yaml::DUMP_NULL_AS_EMPTY and Yaml::DUMP_NULL_AS_TILDE flags cannot be used together.');
47+
}
48+
49+
return $this->doDump($input, $inline, $indent, $flags);
50+
}
51+
52+
private function doDump(mixed $input, int $inline = 0, int $indent = 0, int $flags = 0, int $nestingLevel = 0): string
4453
{
4554
$output = '';
4655
$prefix = $indent ? str_repeat(' ', $indent) : '';
@@ -51,9 +60,9 @@ public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags
5160
}
5261

5362
if ($inline <= 0 || (!\is_array($input) && !$input instanceof TaggedValue && $dumpObjectAsInlineMap) || !$input) {
54-
$output .= $prefix.Inline::dump($input, $flags);
63+
$output .= $prefix.Inline::dump($input, $flags, 0 === $nestingLevel);
5564
} elseif ($input instanceof TaggedValue) {
56-
$output .= $this->dumpTaggedValue($input, $inline, $indent, $flags, $prefix);
65+
$output .= $this->dumpTaggedValue($input, $inline, $indent, $flags, $prefix, $nestingLevel);
5766
} else {
5867
$dumpAsMap = Inline::isHash($input);
5968

@@ -105,10 +114,10 @@ public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags
105114
}
106115

107116
if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) {
108-
$output .= ' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n";
117+
$output .= ' '.$this->doDump($value->getValue(), $inline - 1, 0, $flags, $nestingLevel + 1)."\n";
109118
} else {
110119
$output .= "\n";
111-
$output .= $this->dump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : $indent + 2, $flags);
120+
$output .= $this->doDump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : $indent + 2, $flags, $nestingLevel + 1);
112121
}
113122

114123
continue;
@@ -126,15 +135,15 @@ public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags
126135
$prefix,
127136
$dumpAsMap ? Inline::dump($key, $flags).':' : '-',
128137
$willBeInlined ? ' ' : "\n",
129-
$this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags)
138+
$this->doDump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags, $nestingLevel + 1)
130139
).($willBeInlined ? "\n" : '');
131140
}
132141
}
133142

134143
return $output;
135144
}
136145

137-
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
138147
{
139148
$output = \sprintf('%s!%s', $prefix ? $prefix.' ' : '', $value->getTag());
140149

@@ -150,10 +159,10 @@ private function dumpTaggedValue(TaggedValue $value, int $inline, int $indent, i
150159
}
151160

152161
if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) {
153-
return $output.' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n";
162+
return $output.' '.$this->doDump($value->getValue(), $inline - 1, 0, $flags, $nestingLevel + 1)."\n";
154163
}
155164

156-
return $output."\n".$this->dump($value->getValue(), $inline - 1, $indent, $flags);
165+
return $output."\n".$this->doDump($value->getValue(), $inline - 1, $indent, $flags, $nestingLevel + 1);
157166
}
158167

159168
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-3
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
+57
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
+27
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
+1
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.