Version 1
[yaffs-website] / vendor / symfony / config / Definition / Dumper / XmlReferenceDumper.php
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
12 namespace Symfony\Component\Config\Definition\Dumper;
13
14 use Symfony\Component\Config\Definition\ConfigurationInterface;
15 use Symfony\Component\Config\Definition\NodeInterface;
16 use Symfony\Component\Config\Definition\ArrayNode;
17 use Symfony\Component\Config\Definition\EnumNode;
18 use Symfony\Component\Config\Definition\PrototypedArrayNode;
19
20 /**
21  * Dumps a XML reference configuration for the given configuration/node instance.
22  *
23  * @author Wouter J <waldio.webdesign@gmail.com>
24  */
25 class XmlReferenceDumper
26 {
27     private $reference;
28
29     public function dump(ConfigurationInterface $configuration, $namespace = null)
30     {
31         return $this->dumpNode($configuration->getConfigTreeBuilder()->buildTree(), $namespace);
32     }
33
34     public function dumpNode(NodeInterface $node, $namespace = null)
35     {
36         $this->reference = '';
37         $this->writeNode($node, 0, true, $namespace);
38         $ref = $this->reference;
39         $this->reference = null;
40
41         return $ref;
42     }
43
44     /**
45      * @param NodeInterface $node
46      * @param int           $depth
47      * @param bool          $root      If the node is the root node
48      * @param string        $namespace The namespace of the node
49      */
50     private function writeNode(NodeInterface $node, $depth = 0, $root = false, $namespace = null)
51     {
52         $rootName = ($root ? 'config' : $node->getName());
53         $rootNamespace = ($namespace ?: ($root ? 'http://example.org/schema/dic/'.$node->getName() : null));
54
55         // xml remapping
56         if ($node->getParent()) {
57             $remapping = array_filter($node->getParent()->getXmlRemappings(), function ($mapping) use ($rootName) {
58                 return $rootName === $mapping[1];
59             });
60
61             if (count($remapping)) {
62                 list($singular) = current($remapping);
63                 $rootName = $singular;
64             }
65         }
66         $rootName = str_replace('_', '-', $rootName);
67
68         $rootAttributes = array();
69         $rootAttributeComments = array();
70         $rootChildren = array();
71         $rootComments = array();
72
73         if ($node instanceof ArrayNode) {
74             $children = $node->getChildren();
75
76             // comments about the root node
77             if ($rootInfo = $node->getInfo()) {
78                 $rootComments[] = $rootInfo;
79             }
80
81             if ($rootNamespace) {
82                 $rootComments[] = 'Namespace: '.$rootNamespace;
83             }
84
85             // render prototyped nodes
86             if ($node instanceof PrototypedArrayNode) {
87                 $prototype = $node->getPrototype();
88
89                 $info = 'prototype';
90                 if (null !== $prototype->getInfo()) {
91                     $info .= ': '.$prototype->getInfo();
92                 }
93                 array_unshift($rootComments, $info);
94
95                 if ($key = $node->getKeyAttribute()) {
96                     $rootAttributes[$key] = str_replace('-', ' ', $rootName).' '.$key;
97                 }
98
99                 if ($prototype instanceof ArrayNode) {
100                     $children = $prototype->getChildren();
101                 } else {
102                     if ($prototype->hasDefaultValue()) {
103                         $prototypeValue = $prototype->getDefaultValue();
104                     } else {
105                         switch (get_class($prototype)) {
106                             case 'Symfony\Component\Config\Definition\ScalarNode':
107                                 $prototypeValue = 'scalar value';
108                                 break;
109
110                             case 'Symfony\Component\Config\Definition\FloatNode':
111                             case 'Symfony\Component\Config\Definition\IntegerNode':
112                                 $prototypeValue = 'numeric value';
113                                 break;
114
115                             case 'Symfony\Component\Config\Definition\BooleanNode':
116                                 $prototypeValue = 'true|false';
117                                 break;
118
119                             case 'Symfony\Component\Config\Definition\EnumNode':
120                                 $prototypeValue = implode('|', array_map('json_encode', $prototype->getValues()));
121                                 break;
122
123                             default:
124                                 $prototypeValue = 'value';
125                         }
126                     }
127                 }
128             }
129
130             // get attributes and elements
131             foreach ($children as $child) {
132                 if (!$child instanceof ArrayNode) {
133                     // get attributes
134
135                     // metadata
136                     $name = str_replace('_', '-', $child->getName());
137                     $value = '%%%%not_defined%%%%'; // use a string which isn't used in the normal world
138
139                     // comments
140                     $comments = array();
141                     if ($info = $child->getInfo()) {
142                         $comments[] = $info;
143                     }
144
145                     if ($example = $child->getExample()) {
146                         $comments[] = 'Example: '.$example;
147                     }
148
149                     if ($child->isRequired()) {
150                         $comments[] = 'Required';
151                     }
152
153                     if ($child instanceof EnumNode) {
154                         $comments[] = 'One of '.implode('; ', array_map('json_encode', $child->getValues()));
155                     }
156
157                     if (count($comments)) {
158                         $rootAttributeComments[$name] = implode(";\n", $comments);
159                     }
160
161                     // default values
162                     if ($child->hasDefaultValue()) {
163                         $value = $child->getDefaultValue();
164                     }
165
166                     // append attribute
167                     $rootAttributes[$name] = $value;
168                 } else {
169                     // get elements
170                     $rootChildren[] = $child;
171                 }
172             }
173         }
174
175         // render comments
176
177         // root node comment
178         if (count($rootComments)) {
179             foreach ($rootComments as $comment) {
180                 $this->writeLine('<!-- '.$comment.' -->', $depth);
181             }
182         }
183
184         // attribute comments
185         if (count($rootAttributeComments)) {
186             foreach ($rootAttributeComments as $attrName => $comment) {
187                 $commentDepth = $depth + 4 + strlen($attrName) + 2;
188                 $commentLines = explode("\n", $comment);
189                 $multiline = (count($commentLines) > 1);
190                 $comment = implode(PHP_EOL.str_repeat(' ', $commentDepth), $commentLines);
191
192                 if ($multiline) {
193                     $this->writeLine('<!--', $depth);
194                     $this->writeLine($attrName.': '.$comment, $depth + 4);
195                     $this->writeLine('-->', $depth);
196                 } else {
197                     $this->writeLine('<!-- '.$attrName.': '.$comment.' -->', $depth);
198                 }
199             }
200         }
201
202         // render start tag + attributes
203         $rootIsVariablePrototype = isset($prototypeValue);
204         $rootIsEmptyTag = (0 === count($rootChildren) && !$rootIsVariablePrototype);
205         $rootOpenTag = '<'.$rootName;
206         if (1 >= ($attributesCount = count($rootAttributes))) {
207             if (1 === $attributesCount) {
208                 $rootOpenTag .= sprintf(' %s="%s"', current(array_keys($rootAttributes)), $this->writeValue(current($rootAttributes)));
209             }
210
211             $rootOpenTag .= $rootIsEmptyTag ? ' />' : '>';
212
213             if ($rootIsVariablePrototype) {
214                 $rootOpenTag .= $prototypeValue.'</'.$rootName.'>';
215             }
216
217             $this->writeLine($rootOpenTag, $depth);
218         } else {
219             $this->writeLine($rootOpenTag, $depth);
220
221             $i = 1;
222
223             foreach ($rootAttributes as $attrName => $attrValue) {
224                 $attr = sprintf('%s="%s"', $attrName, $this->writeValue($attrValue));
225
226                 $this->writeLine($attr, $depth + 4);
227
228                 if ($attributesCount === $i++) {
229                     $this->writeLine($rootIsEmptyTag ? '/>' : '>', $depth);
230
231                     if ($rootIsVariablePrototype) {
232                         $rootOpenTag .= $prototypeValue.'</'.$rootName.'>';
233                     }
234                 }
235             }
236         }
237
238         // render children tags
239         foreach ($rootChildren as $child) {
240             $this->writeLine('');
241             $this->writeNode($child, $depth + 4);
242         }
243
244         // render end tag
245         if (!$rootIsEmptyTag && !$rootIsVariablePrototype) {
246             $this->writeLine('');
247
248             $rootEndTag = '</'.$rootName.'>';
249             $this->writeLine($rootEndTag, $depth);
250         }
251     }
252
253     /**
254      * Outputs a single config reference line.
255      *
256      * @param string $text
257      * @param int    $indent
258      */
259     private function writeLine($text, $indent = 0)
260     {
261         $indent = strlen($text) + $indent;
262         $format = '%'.$indent.'s';
263
264         $this->reference .= sprintf($format, $text).PHP_EOL;
265     }
266
267     /**
268      * Renders the string conversion of the value.
269      *
270      * @param mixed $value
271      *
272      * @return string
273      */
274     private function writeValue($value)
275     {
276         if ('%%%%not_defined%%%%' === $value) {
277             return '';
278         }
279
280         if (is_string($value) || is_numeric($value)) {
281             return $value;
282         }
283
284         if (false === $value) {
285             return 'false';
286         }
287
288         if (true === $value) {
289             return 'true';
290         }
291
292         if (null === $value) {
293             return 'null';
294         }
295
296         if (empty($value)) {
297             return '';
298         }
299
300         if (is_array($value)) {
301             return implode(',', $value);
302         }
303     }
304 }