diff --git a/CHANGELOG.md b/CHANGELOG.md index 05b23cd7..364bf66d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,17 @@ CHANGELOG ========= +7.3 +--- + + * Add compact nested mapping support by using the `Yaml::DUMP_COMPACT_NESTED_MAPPING` flag + * Add the `Yaml::DUMP_FORCE_DOUBLE_QUOTES_ON_VALUES` flag to enforce double quotes around string values + 7.2 --- * Deprecate parsing duplicate mapping keys whose value is `null` + * Add support for dumping `null` as an empty value by using the `Yaml::DUMP_NULL_AS_EMPTY` flag 7.1 --- diff --git a/Dumper.php b/Dumper.php index f8ea205a..cd5a1f6c 100644 --- a/Dumper.php +++ b/Dumper.php @@ -41,6 +41,15 @@ public function __construct(private int $indentation = 4) * @param int-mask-of $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string */ public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags = 0): string + { + if ($flags & Yaml::DUMP_NULL_AS_EMPTY && $flags & Yaml::DUMP_NULL_AS_TILDE) { + throw new \InvalidArgumentException('The Yaml::DUMP_NULL_AS_EMPTY and Yaml::DUMP_NULL_AS_TILDE flags cannot be used together.'); + } + + return $this->doDump($input, $inline, $indent, $flags); + } + + private function doDump(mixed $input, int $inline = 0, int $indent = 0, int $flags = 0, int $nestingLevel = 0): string { $output = ''; $prefix = $indent ? str_repeat(' ', $indent) : ''; @@ -51,11 +60,12 @@ public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags } if ($inline <= 0 || (!\is_array($input) && !$input instanceof TaggedValue && $dumpObjectAsInlineMap) || !$input) { - $output .= $prefix.Inline::dump($input, $flags); + $output .= $prefix.Inline::dump($input, $flags, 0 === $nestingLevel); } elseif ($input instanceof TaggedValue) { - $output .= $this->dumpTaggedValue($input, $inline, $indent, $flags, $prefix); + $output .= $this->dumpTaggedValue($input, $inline, $indent, $flags, $prefix, $nestingLevel); } else { $dumpAsMap = Inline::isHash($input); + $compactNestedMapping = Yaml::DUMP_COMPACT_NESTED_MAPPING & $flags && !$dumpAsMap; foreach ($input as $key => $value) { if ('' !== $output && "\n" !== $output[-1]) { @@ -105,10 +115,10 @@ public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags } if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) { - $output .= ' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n"; + $output .= ' '.$this->doDump($value->getValue(), $inline - 1, 0, $flags, $nestingLevel + 1)."\n"; } else { $output .= "\n"; - $output .= $this->dump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : $indent + 2, $flags); + $output .= $this->doDump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : $indent + 2, $flags, $nestingLevel + 1); } continue; @@ -125,8 +135,8 @@ public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags $output .= \sprintf('%s%s%s%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', - $willBeInlined ? ' ' : "\n", - $this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags) + $willBeInlined || ($compactNestedMapping && \is_array($value) && Inline::isHash($value)) ? ' ' : "\n", + $compactNestedMapping && \is_array($value) && Inline::isHash($value) ? substr($this->doDump($value, $inline - 1, $indent + 2, $flags, $nestingLevel + 1), $indent + 2) : $this->doDump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags, $nestingLevel + 1) ).($willBeInlined ? "\n" : ''); } } @@ -134,7 +144,7 @@ public function dump(mixed $input, int $inline = 0, int $indent = 0, int $flags return $output; } - private function dumpTaggedValue(TaggedValue $value, int $inline, int $indent, int $flags, string $prefix): string + private function dumpTaggedValue(TaggedValue $value, int $inline, int $indent, int $flags, string $prefix, int $nestingLevel): string { $output = \sprintf('%s!%s', $prefix ? $prefix.' ' : '', $value->getTag()); @@ -150,10 +160,10 @@ private function dumpTaggedValue(TaggedValue $value, int $inline, int $indent, i } if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) { - return $output.' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n"; + return $output.' '.$this->doDump($value->getValue(), $inline - 1, 0, $flags, $nestingLevel + 1)."\n"; } - return $output."\n".$this->dump($value->getValue(), $inline - 1, $indent, $flags); + return $output."\n".$this->doDump($value->getValue(), $inline - 1, $indent, $flags, $nestingLevel + 1); } private function getBlockIndentationIndicator(string $value): string diff --git a/Escaper.php b/Escaper.php index e42034aa..8cc492c5 100644 --- a/Escaper.php +++ b/Escaper.php @@ -28,22 +28,24 @@ class Escaper // first to ensure proper escaping because str_replace operates iteratively // on the input arrays. This ordering of the characters avoids the use of strtr, // which performs more slowly. - private const ESCAPEES = ['\\', '\\\\', '\\"', '"', - "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", - "\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f", - "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", - "\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f", - "\x7f", - "\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9", - ]; - private const ESCAPED = ['\\\\', '\\"', '\\\\', '\\"', - '\\0', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\a', - '\\b', '\\t', '\\n', '\\v', '\\f', '\\r', '\\x0e', '\\x0f', - '\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17', - '\\x18', '\\x19', '\\x1a', '\\e', '\\x1c', '\\x1d', '\\x1e', '\\x1f', - '\\x7f', - '\\N', '\\_', '\\L', '\\P', - ]; + private const ESCAPEES = [ + '\\', '\\\\', '\\"', '"', + "\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07", + "\x08", "\x09", "\x0a", "\x0b", "\x0c", "\x0d", "\x0e", "\x0f", + "\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17", + "\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f", + "\x7f", + "\xc2\x85", "\xc2\xa0", "\xe2\x80\xa8", "\xe2\x80\xa9", + ]; + private const ESCAPED = [ + '\\\\', '\\"', '\\\\', '\\"', + '\\0', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\a', + '\\b', '\\t', '\\n', '\\v', '\\f', '\\r', '\\x0e', '\\x0f', + '\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17', + '\\x18', '\\x19', '\\x1a', '\\e', '\\x1c', '\\x1d', '\\x1e', '\\x1f', + '\\x7f', + '\\N', '\\_', '\\L', '\\P', + ]; /** * Determines if a PHP value would require double quoting in YAML. diff --git a/Inline.php b/Inline.php index 34ef66e6..bfa910c7 100644 --- a/Inline.php +++ b/Inline.php @@ -100,7 +100,7 @@ public static function parse(string $value, int $flags = 0, array &$references = * * @throws DumpException When trying to dump PHP resource */ - public static function dump(mixed $value, int $flags = 0): string + public static function dump(mixed $value, int $flags = 0, bool $rootLevel = false): string { switch (true) { case \is_resource($value): @@ -138,7 +138,7 @@ public static function dump(mixed $value, int $flags = 0): string case \is_array($value): return self::dumpArray($value, $flags); case null === $value: - return self::dumpNull($flags); + return self::dumpNull($flags, $rootLevel); case true === $value: return 'true'; case false === $value: @@ -173,6 +173,7 @@ public static function dump(mixed $value, int $flags = 0): string case self::isBinaryString($value): return '!!binary '.base64_encode($value); case Escaper::requiresDoubleQuoting($value): + case Yaml::DUMP_FORCE_DOUBLE_QUOTES_ON_VALUES & $flags: return Escaper::escapeWithDoubleQuotes($value); case Escaper::requiresSingleQuoting($value): $singleQuoted = Escaper::escapeWithSingleQuotes($value); @@ -242,23 +243,28 @@ private static function dumpArray(array $value, int $flags): string private static function dumpHashArray(array|\ArrayObject|\stdClass $value, int $flags): string { $output = []; + $keyFlags = $flags &~ Yaml::DUMP_FORCE_DOUBLE_QUOTES_ON_VALUES; foreach ($value as $key => $val) { if (\is_int($key) && Yaml::DUMP_NUMERIC_KEY_AS_STRING & $flags) { $key = (string) $key; } - $output[] = \sprintf('%s: %s', self::dump($key, $flags), self::dump($val, $flags)); + $output[] = \sprintf('%s: %s', self::dump($key, $keyFlags), self::dump($val, $flags)); } return \sprintf('{ %s }', implode(', ', $output)); } - private static function dumpNull(int $flags): string + private static function dumpNull(int $flags, bool $rootLevel = false): string { if (Yaml::DUMP_NULL_AS_TILDE & $flags) { return '~'; } + if (Yaml::DUMP_NULL_AS_EMPTY & $flags && !$rootLevel) { + return ''; + } + return 'null'; } diff --git a/Parser.php b/Parser.php index f759a64c..be589082 100644 --- a/Parser.php +++ b/Parser.php @@ -1166,8 +1166,8 @@ private function lexUnquotedString(int &$cursor): string { $offset = $cursor; - while ($cursor < strlen($this->currentLine)) { - if (in_array($this->currentLine[$cursor], ['[', ']', '{', '}', ',', ':'], true)) { + while ($cursor < \strlen($this->currentLine)) { + if (\in_array($this->currentLine[$cursor], ['[', ']', '{', '}', ',', ':'], true)) { break; } @@ -1224,8 +1224,8 @@ private function lexInlineStructure(int &$cursor, string $closingTag, bool $cons $value .= $this->currentLine[$cursor]; ++$cursor; - if ($consumeUntilEol && isset($this->currentLine[$cursor]) && ($whitespaces = strspn($this->currentLine, ' ', $cursor) + $cursor) < strlen($this->currentLine) && '#' !== $this->currentLine[$whitespaces]) { - throw new ParseException(sprintf('Unexpected token "%s".', trim(substr($this->currentLine, $cursor)))); + if ($consumeUntilEol && isset($this->currentLine[$cursor]) && ($whitespaces = strspn($this->currentLine, ' ', $cursor) + $cursor) < \strlen($this->currentLine) && '#' !== $this->currentLine[$whitespaces]) { + throw new ParseException(\sprintf('Unexpected token "%s".', trim(substr($this->currentLine, $cursor)))); } return $value; diff --git a/Tests/DumperTest.php b/Tests/DumperTest.php index 24758b81..e937336c 100644 --- a/Tests/DumperTest.php +++ b/Tests/DumperTest.php @@ -216,6 +216,63 @@ public function testObjectSupportDisabledWithExceptions() $this->dumper->dump(['foo' => new A(), 'bar' => 1], 0, 0, Yaml::DUMP_EXCEPTION_ON_INVALID_TYPE); } + public function testDumpWithMultipleNullFlagsFormatsThrows() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('The Yaml::DUMP_NULL_AS_EMPTY and Yaml::DUMP_NULL_AS_TILDE flags cannot be used together.'); + + $this->dumper->dump(['foo' => 'bar'], 0, 0, Yaml::DUMP_NULL_AS_EMPTY | Yaml::DUMP_NULL_AS_TILDE); + } + + public function testDumpNullAsEmptyInExpandedMapping() + { + $expected = "qux:\n foo: bar\n baz: \n"; + + $this->assertSame($expected, $this->dumper->dump(['qux' => ['foo' => 'bar', 'baz' => null]], 2, flags: Yaml::DUMP_NULL_AS_EMPTY)); + } + + public function testDumpNullAsEmptyWithObject() + { + $class = new \stdClass(); + $class->foo = 'bar'; + $class->baz = null; + + $this->assertSame("foo: bar\nbaz: \n", $this->dumper->dump($class, 2, flags: Yaml::DUMP_NULL_AS_EMPTY | Yaml::DUMP_OBJECT_AS_MAP)); + } + + public function testDumpNullAsEmptyDumpsWhenInInlineMapping() + { + $expected = "foo: \nqux: { foo: bar, baz: }\n"; + + $this->assertSame($expected, $this->dumper->dump(['foo' => null, 'qux' => ['foo' => 'bar', 'baz' => null]], 1, flags: Yaml::DUMP_NULL_AS_EMPTY)); + } + + public function testDumpNullAsEmptyDumpsNestedMaps() + { + $expected = "foo: \nqux:\n foo: bar\n baz: \n"; + + $this->assertSame($expected, $this->dumper->dump(['foo' => null, 'qux' => ['foo' => 'bar', 'baz' => null]], 10, flags: Yaml::DUMP_NULL_AS_EMPTY)); + } + + public function testDumpNullAsEmptyInExpandedSequence() + { + $expected = "qux:\n - foo\n - \n - bar\n"; + + $this->assertSame($expected, $this->dumper->dump(['qux' => ['foo', null, 'bar']], 2, flags: Yaml::DUMP_NULL_AS_EMPTY)); + } + + public function testDumpNullAsEmptyWhenInInlineSequence() + { + $expected = "foo: \nqux: [foo, , bar]\n"; + + $this->assertSame($expected, $this->dumper->dump(['foo' => null, 'qux' => ['foo', null, 'bar']], 1, flags: Yaml::DUMP_NULL_AS_EMPTY)); + } + + public function testDumpNullAsEmptyAtRoot() + { + $this->assertSame('null', $this->dumper->dump(null, 2, flags: Yaml::DUMP_NULL_AS_EMPTY)); + } + /** * @dataProvider getEscapeSequences */ @@ -853,6 +910,87 @@ public function testDumpNullAsTilde() $this->assertSame('{ foo: ~ }', $this->dumper->dump(['foo' => null], 0, 0, Yaml::DUMP_NULL_AS_TILDE)); } + /** + * @dataProvider getForceQuotesOnValuesData + */ + public function testCanForceQuotesOnValues(array $input, string $expected) + { + $this->assertSame($expected, $this->dumper->dump($input, 0, 0, Yaml::DUMP_FORCE_DOUBLE_QUOTES_ON_VALUES)); + } + + public static function getForceQuotesOnValuesData(): iterable + { + yield 'empty string' => [ + ['foo' => ''], + '{ foo: \'\' }', + ]; + + yield 'double quote' => [ + ['foo' => '"'], + '{ foo: "\"" }', + ]; + + yield 'single quote' => [ + ['foo' => "'"], + '{ foo: "\'" }', + ]; + + yield 'line break' => [ + ['foo' => "line\nbreak"], + '{ foo: "line\nbreak" }', + ]; + + yield 'tab character' => [ + ['foo' => "tab\tcharacter"], + '{ foo: "tab\tcharacter" }', + ]; + + yield 'backslash' => [ + ['foo' => "back\\slash"], + '{ foo: "back\\\\slash" }', + ]; + + yield 'colon' => [ + ['foo' => 'colon: value'], + '{ foo: "colon: value" }', + ]; + + yield 'dash' => [ + ['foo' => '- dash'], + '{ foo: "- dash" }', + ]; + + yield 'numeric' => [ + ['foo' => 23], + '{ foo: 23 }', + ]; + + yield 'boolean' => [ + ['foo' => true], + '{ foo: true }', + ]; + + yield 'null' => [ + ['foo' => null], + '{ foo: null }', + ]; + + yield 'nested' => [ + ['foo' => ['bar' => 'bat', 'baz' => 23]], + '{ foo: { bar: "bat", baz: 23 } }', + ]; + + yield 'mix of values' => [ + ['foo' => 'bat', 'bar' => 23, 'baz' => true, 'qux' => "line\nbreak"], + '{ foo: "bat", bar: 23, baz: true, qux: "line\nbreak" }', + ]; + + yield 'special YAML characters' => [ + ['foo' => 'colon: value', 'bar' => '- dash', 'baz' => '? question', 'qux' => '# hash'], + '{ foo: "colon: value", bar: "- dash", baz: "? question", qux: "# hash" }', + ]; + } + /** * @dataProvider getNumericKeyData */ @@ -1004,6 +1142,177 @@ public static function getDateTimeData() ]; } + public static function getDumpCompactNestedMapping() + { + $data = [ + 'planets' => [ + [ + 'name' => 'Mercury', + 'distance' => 57910000, + 'properties' => [ + ['name' => 'size', 'value' => 4879], + ['name' => 'moons', 'value' => 0], + [[[]]], + ], + ], + [ + 'name' => 'Jupiter', + 'distance' => 778500000, + 'properties' => [ + ['name' => 'size', 'value' => 139820], + ['name' => 'moons', 'value' => 79], + [[]], + ], + ], + ], + ]; + + yield 'Compact nested mapping 1' => [ + $data, + << [ + $data, + << [ + $data, + << [ + $data, + << [ + $data, + <<dump($data, $inline, 0, Yaml::DUMP_COMPACT_NESTED_MAPPING); + $this->assertSame($expected, $actual); + $this->assertSameData($data, $this->parser->parse($actual)); + } + private function assertSameData($expected, $actual) { $this->assertEquals($expected, $actual); diff --git a/Tests/ParserTest.php b/Tests/ParserTest.php index cdd37ed4..836ec23f 100644 --- a/Tests/ParserTest.php +++ b/Tests/ParserTest.php @@ -52,6 +52,33 @@ public function testTopLevelNull() $this->assertSameData($expected, $data); } + public function testEmptyValueInExpandedMappingIsSupported() + { + $yml = <<<'YAML' +foo: + bar: + baz: qux +YAML; + + $data = $this->parser->parse($yml); + $expected = ['foo' => ['bar' => null, 'baz' => 'qux']]; + $this->assertSameData($expected, $data); + } + + public function testEmptyValueInExpandedSequenceIsSupported() + { + $yml = <<<'YAML' +foo: + - bar + - + - baz +YAML; + + $data = $this->parser->parse($yml); + $expected = ['foo' => ['bar', null, 'baz']]; + $this->assertSameData($expected, $data); + } + public function testTaggedValueTopLevelNumber() { $yml = '!number 5'; @@ -1155,7 +1182,7 @@ public function testNestedFoldedStringBlockWithComments() footer # comment3 -EOT +EOT, ]], Yaml::parse(<<<'EOF' - title: some title @@ -1495,13 +1522,13 @@ public static function getBinaryData() <<<'EOT' data: !!binary | SGVsbG8gd29ybGQ= -EOT +EOT, ], 'containing spaces in block scalar' => [ <<<'EOT' data: !!binary | SGVs bG8gd 29ybGQ= -EOT +EOT, ], ]; } @@ -1602,7 +1629,7 @@ public static function parserThrowsExceptionWithCorrectLineNumberProvider() - # bar bar: "123", -YAML +YAML, ], [ 5, @@ -1612,7 +1639,7 @@ public static function parserThrowsExceptionWithCorrectLineNumberProvider() # bar # bar bar: "123", -YAML +YAML, ], [ 8, @@ -1625,7 +1652,7 @@ public static function parserThrowsExceptionWithCorrectLineNumberProvider() - # bar bar: "123", -YAML +YAML, ], [ 10, @@ -1640,7 +1667,7 @@ public static function parserThrowsExceptionWithCorrectLineNumberProvider() # bar # bar bar: "123", -YAML +YAML, ], ]; } @@ -1744,14 +1771,14 @@ public static function wrappedUnquotedStringsProvider() [ 'foo' => 'bar bar', 'fiz' => 'cat cat', - ] + ], ], 'sequence' => [ '[ bar bar, cat cat ]', [ 'bar bar', 'cat cat', - ] + ], ], ]; } @@ -1777,7 +1804,7 @@ public function testParseMultiLineUnquotedStringWithTrailingComment(string $yaml $this->assertSame($expected, $this->parser->parse($yaml)); } - public function unquotedStringWithTrailingComment() + public static function unquotedStringWithTrailingComment() { return [ 'comment after comma' => [ @@ -2031,7 +2058,7 @@ public static function inlineNotationSpanningMultipleLinesProvider(): array << [ [ @@ -2043,7 +2070,7 @@ public static function inlineNotationSpanningMultipleLinesProvider(): array ['entry1', {}], ['entry2'] ] -YAML +YAML, ], 'sequence nested in mapping' => [ ['foo' => ['bar', 'foobar'], 'bar' => ['baz']], @@ -2085,7 +2112,7 @@ public static function inlineNotationSpanningMultipleLinesProvider(): array bar, ] bar: baz -YAML +YAML, ], 'nested sequence nested in mapping starting on the same line' => [ [ @@ -2212,7 +2239,7 @@ public static function inlineNotationSpanningMultipleLinesProvider(): array foo: 'bar baz' -YAML +YAML, ], 'mixed mapping with inline notation having separated lines' => [ [ @@ -2228,7 +2255,7 @@ public static function inlineNotationSpanningMultipleLinesProvider(): array a: "b" } param: "some" -YAML +YAML, ], 'mixed mapping with inline notation on one line' => [ [ @@ -2241,7 +2268,7 @@ public static function inlineNotationSpanningMultipleLinesProvider(): array << [ [ @@ -2254,7 +2281,7 @@ public static function inlineNotationSpanningMultipleLinesProvider(): array << [ [ @@ -2268,7 +2295,7 @@ public static function inlineNotationSpanningMultipleLinesProvider(): array map: {key: "value", a: "b"} param: "some" -YAML +YAML, ], 'nested collections containing strings with bracket chars' => [ [ @@ -2308,7 +2335,7 @@ public static function inlineNotationSpanningMultipleLinesProvider(): array foo: 'bar}' } ] -YAML +YAML, ], 'escaped characters in quoted strings' => [ [ @@ -2329,7 +2356,7 @@ public static function inlineNotationSpanningMultipleLinesProvider(): array ['te''st'], ["te\"st]"] ] -YAML +YAML, ], ]; } @@ -2404,7 +2431,7 @@ public static function taggedValuesProvider() quz: !long > this is a long text -YAML +YAML, ], 'sequences' => [ [new TaggedValue('foo', ['yaml']), new TaggedValue('quz', ['bar'])], @@ -2412,7 +2439,7 @@ public static function taggedValuesProvider() - !foo - yaml - !quz [bar] -YAML +YAML, ], 'mappings' => [ new TaggedValue('foo', ['foo' => new TaggedValue('quz', ['bar']), 'quz' => new TaggedValue('foo', ['quz' => 'bar'])]), @@ -2421,14 +2448,14 @@ public static function taggedValuesProvider() foo: !quz [bar] quz: !foo quz: bar -YAML +YAML, ], 'inline' => [ [new TaggedValue('foo', ['foo', 'bar']), new TaggedValue('quz', ['foo' => 'bar', 'quz' => new TaggedValue('bar', ['one' => 'bar'])])], << [ [new TaggedValue('foo', 'bar')], @@ -2444,7 +2471,7 @@ public static function taggedValuesProvider() baz #bar ]] -YAML +YAML, ], 'with-comments-trailing-comma' => [ [ @@ -2456,7 +2483,7 @@ public static function taggedValuesProvider() baz, #bar ]] -YAML +YAML, ], ]; } diff --git a/Yaml.php b/Yaml.php index 36b45198..57625a53 100644 --- a/Yaml.php +++ b/Yaml.php @@ -35,6 +35,9 @@ class Yaml public const DUMP_EMPTY_ARRAY_AS_SEQUENCE = 1024; public const DUMP_NULL_AS_TILDE = 2048; public const DUMP_NUMERIC_KEY_AS_STRING = 4096; + public const DUMP_NULL_AS_EMPTY = 8192; + public const DUMP_COMPACT_NESTED_MAPPING = 16384; + public const DUMP_FORCE_DOUBLE_QUOTES_ON_VALUES = 32768; /** * Parses a YAML file into a PHP value.