1<?php 2 3/* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Symfony\Component\Yaml; 13 14use Symfony\Component\Yaml\Tag\TaggedValue; 15 16/** 17 * Dumper dumps PHP variables to YAML strings. 18 * 19 * @author Fabien Potencier <fabien@symfony.com> 20 * 21 * @final 22 */ 23class Dumper 24{ 25 /** 26 * The amount of spaces to use for indentation of nested nodes. 27 * 28 * @var int 29 */ 30 protected $indentation; 31 32 public function __construct(int $indentation = 4) 33 { 34 if ($indentation < 1) { 35 throw new \InvalidArgumentException('The indentation must be greater than zero.'); 36 } 37 38 $this->indentation = $indentation; 39 } 40 41 /** 42 * Dumps a PHP value to YAML. 43 * 44 * @param mixed $input The PHP value 45 * @param int $inline The level where you switch to inline YAML 46 * @param int $indent The level of indentation (used internally) 47 * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string 48 */ 49 public function dump($input, int $inline = 0, int $indent = 0, int $flags = 0): string 50 { 51 $output = ''; 52 $prefix = $indent ? str_repeat(' ', $indent) : ''; 53 $dumpObjectAsInlineMap = true; 54 55 if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($input instanceof \ArrayObject || $input instanceof \stdClass)) { 56 $dumpObjectAsInlineMap = empty((array) $input); 57 } 58 59 if ($inline <= 0 || (!\is_array($input) && !$input instanceof TaggedValue && $dumpObjectAsInlineMap) || empty($input)) { 60 $output .= $prefix.Inline::dump($input, $flags); 61 } elseif ($input instanceof TaggedValue) { 62 $output .= $this->dumpTaggedValue($input, $inline, $indent, $flags, $prefix); 63 } else { 64 $dumpAsMap = Inline::isHash($input); 65 66 foreach ($input as $key => $value) { 67 if ('' !== $output && "\n" !== $output[-1]) { 68 $output .= "\n"; 69 } 70 71 if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value) && false !== strpos($value, "\n") && false === strpos($value, "\r")) { 72 // If the first line starts with a space character, the spec requires a blockIndicationIndicator 73 // http://www.yaml.org/spec/1.2/spec.html#id2793979 74 $blockIndentationIndicator = (' ' === substr($value, 0, 1)) ? (string) $this->indentation : ''; 75 76 if (isset($value[-2]) && "\n" === $value[-2] && "\n" === $value[-1]) { 77 $blockChompingIndicator = '+'; 78 } elseif ("\n" === $value[-1]) { 79 $blockChompingIndicator = ''; 80 } else { 81 $blockChompingIndicator = '-'; 82 } 83 84 $output .= sprintf('%s%s%s |%s%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', '', $blockIndentationIndicator, $blockChompingIndicator); 85 86 foreach (explode("\n", $value) as $row) { 87 if ('' === $row) { 88 $output .= "\n"; 89 } else { 90 $output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row); 91 } 92 } 93 94 continue; 95 } 96 97 if ($value instanceof TaggedValue) { 98 $output .= sprintf('%s%s !%s', $prefix, $dumpAsMap ? Inline::dump($key, $flags).':' : '-', $value->getTag()); 99 100 if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value->getValue()) && false !== strpos($value->getValue(), "\n") && false === strpos($value->getValue(), "\r\n")) { 101 // If the first line starts with a space character, the spec requires a blockIndicationIndicator 102 // http://www.yaml.org/spec/1.2/spec.html#id2793979 103 $blockIndentationIndicator = (' ' === substr($value->getValue(), 0, 1)) ? (string) $this->indentation : ''; 104 $output .= sprintf(' |%s', $blockIndentationIndicator); 105 106 foreach (explode("\n", $value->getValue()) as $row) { 107 $output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row); 108 } 109 110 continue; 111 } 112 113 if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) { 114 $output .= ' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n"; 115 } else { 116 $output .= "\n"; 117 $output .= $this->dump($value->getValue(), $inline - 1, $dumpAsMap ? $indent + $this->indentation : $indent + 2, $flags); 118 } 119 120 continue; 121 } 122 123 $dumpObjectAsInlineMap = true; 124 125 if (Yaml::DUMP_OBJECT_AS_MAP & $flags && ($value instanceof \ArrayObject || $value instanceof \stdClass)) { 126 $dumpObjectAsInlineMap = empty((array) $value); 127 } 128 129 $willBeInlined = $inline - 1 <= 0 || !\is_array($value) && $dumpObjectAsInlineMap || empty($value); 130 131 $output .= sprintf('%s%s%s%s', 132 $prefix, 133 $dumpAsMap ? Inline::dump($key, $flags).':' : '-', 134 $willBeInlined ? ' ' : "\n", 135 $this->dump($value, $inline - 1, $willBeInlined ? 0 : $indent + $this->indentation, $flags) 136 ).($willBeInlined ? "\n" : ''); 137 } 138 } 139 140 return $output; 141 } 142 143 private function dumpTaggedValue(TaggedValue $value, int $inline, int $indent, int $flags, string $prefix): string 144 { 145 $output = sprintf('%s!%s', $prefix ? $prefix.' ' : '', $value->getTag()); 146 147 if (Yaml::DUMP_MULTI_LINE_LITERAL_BLOCK & $flags && \is_string($value->getValue()) && false !== strpos($value->getValue(), "\n") && false === strpos($value->getValue(), "\r\n")) { 148 // If the first line starts with a space character, the spec requires a blockIndicationIndicator 149 // http://www.yaml.org/spec/1.2/spec.html#id2793979 150 $blockIndentationIndicator = (' ' === substr($value->getValue(), 0, 1)) ? (string) $this->indentation : ''; 151 $output .= sprintf(' |%s', $blockIndentationIndicator); 152 153 foreach (explode("\n", $value->getValue()) as $row) { 154 $output .= sprintf("\n%s%s%s", $prefix, str_repeat(' ', $this->indentation), $row); 155 } 156 157 return $output; 158 } 159 160 if ($inline - 1 <= 0 || null === $value->getValue() || \is_scalar($value->getValue())) { 161 return $output.' '.$this->dump($value->getValue(), $inline - 1, 0, $flags)."\n"; 162 } 163 164 return $output."\n".$this->dump($value->getValue(), $inline - 1, $indent, $flags); 165 } 166} 167