Version 1
[yaffs-website] / vendor / consolidation / output-formatters / src / Transformations / DomToArraySimplifier.php
1 <?php
2 namespace Consolidation\OutputFormatters\Transformations;
3
4 use Consolidation\OutputFormatters\Options\FormatterOptions;
5 use Consolidation\OutputFormatters\StructuredData\Xml\DomDataInterface;
6 use Consolidation\OutputFormatters\StructuredData\Xml\XmlSchema;
7
8 /**
9  * Simplify a DOMDocument to an array.
10  */
11 class DomToArraySimplifier implements SimplifyToArrayInterface
12 {
13     public function __construct()
14     {
15     }
16
17     /**
18      * @param ReflectionClass $dataType
19      */
20     public function canSimplify(\ReflectionClass $dataType)
21     {
22         return
23             $dataType->isSubclassOf('\Consolidation\OutputFormatters\StructuredData\Xml\DomDataInterface') ||
24             $dataType->isSubclassOf('DOMDocument') ||
25             ($dataType->getName() == 'DOMDocument');
26     }
27
28     public function simplifyToArray($structuredData, FormatterOptions $options)
29     {
30         if ($structuredData instanceof DomDataInterface) {
31             $structuredData = $structuredData->getDomData();
32         }
33         if ($structuredData instanceof \DOMDocument) {
34             // $schema = $options->getXmlSchema();
35             $simplified = $this->elementToArray($structuredData);
36             $structuredData = array_shift($simplified);
37         }
38         return $structuredData;
39     }
40
41     /**
42      * Recursively convert the provided DOM element into a php array.
43      *
44      * @param \DOMNode $element
45      * @return array
46      */
47     protected function elementToArray(\DOMNode $element)
48     {
49         if ($element->nodeType == XML_TEXT_NODE) {
50             return $element->nodeValue;
51         }
52         $attributes = $this->getNodeAttributes($element);
53         $children = $this->getNodeChildren($element);
54
55         return array_merge($attributes, $children);
56     }
57
58     /**
59      * Get all of the attributes of the provided element.
60      *
61      * @param \DOMNode $element
62      * @return array
63      */
64     protected function getNodeAttributes($element)
65     {
66         if (empty($element->attributes)) {
67             return [];
68         }
69         $attributes = [];
70         foreach ($element->attributes as $key => $attribute) {
71             $attributes[$key] = $attribute->nodeValue;
72         }
73         return $attributes;
74     }
75
76     /**
77      * Get all of the children of the provided element, with simplification.
78      *
79      * @param \DOMNode $element
80      * @return array
81      */
82     protected function getNodeChildren($element)
83     {
84         if (empty($element->childNodes)) {
85             return [];
86         }
87         $uniformChildrenName = $this->hasUniformChildren($element);
88         if ("{$uniformChildrenName}s" == $element->nodeName) {
89             $result = $this->getUniformChildren($element->nodeName, $element);
90         } else {
91             $result = $this->getUniqueChildren($element->nodeName, $element);
92         }
93         return array_filter($result);
94     }
95
96     /**
97      * Get the data from the children of the provided node in preliminary
98      * form.
99      *
100      * @param \DOMNode $element
101      * @return array
102      */
103     protected function getNodeChildrenData($element)
104     {
105         $children = [];
106         foreach ($element->childNodes as $key => $value) {
107             $children[$key] = $this->elementToArray($value);
108         }
109         return $children;
110     }
111
112     /**
113      * Determine whether the children of the provided element are uniform.
114      * @see getUniformChildren(), below.
115      *
116      * @param \DOMNode $element
117      * @return boolean
118      */
119     protected function hasUniformChildren($element)
120     {
121         $last = false;
122         foreach ($element->childNodes as $key => $value) {
123             $name = $value->nodeName;
124             if (!$name) {
125                 return false;
126             }
127             if ($last && ($name != $last)) {
128                 return false;
129             }
130             $last = $name;
131         }
132         return $last;
133     }
134
135     /**
136      * Convert the children of the provided DOM element into an array.
137      * Here, 'uniform' means that all of the element names of the children
138      * are identical, and further, the element name of the parent is the
139      * plural form of the child names.  When the children are uniform in
140      * this way, then the parent element name will be used as the key to
141      * store the children in, and the child list will be returned as a
142      * simple list with their (duplicate) element names omitted.
143      *
144      * @param string $parentKey
145      * @param \DOMNode $element
146      * @return array
147      */
148     protected function getUniformChildren($parentKey, $element)
149     {
150         $children = $this->getNodeChildrenData($element);
151         $simplifiedChildren = [];
152         foreach ($children as $key => $value) {
153             if ($this->valueCanBeSimplified($value)) {
154                 $value = array_shift($value);
155             }
156             $id = $this->getIdOfValue($value);
157             if ($id) {
158                 $simplifiedChildren[$parentKey][$id] = $value;
159             } else {
160                 $simplifiedChildren[$parentKey][] = $value;
161             }
162         }
163         return $simplifiedChildren;
164     }
165
166     /**
167      * Determine whether the provided value has additional unnecessary
168      * nesting.  {"color": "red"} is converted to "red". No other
169      * simplification is done.
170      *
171      * @param \DOMNode $value
172      * @return boolean
173      */
174     protected function valueCanBeSimplified($value)
175     {
176         if (!is_array($value)) {
177             return false;
178         }
179         if (count($value) != 1) {
180             return false;
181         }
182         $data = array_shift($value);
183         return is_string($data);
184     }
185
186     /**
187      * If the object has an 'id' or 'name' element, then use that
188      * as the array key when storing this value in its parent.
189      * @param mixed $value
190      * @return string
191      */
192     protected function getIdOfValue($value)
193     {
194         if (!is_array($value)) {
195             return false;
196         }
197         if (array_key_exists('id', $value)) {
198             return trim($value['id'], '-');
199         }
200         if (array_key_exists('name', $value)) {
201             return trim($value['name'], '-');
202         }
203     }
204
205     /**
206      * Convert the children of the provided DOM element into an array.
207      * Here, 'unique' means that all of the element names of the children are
208      * different.  Since the element names will become the key of the
209      * associative array that is returned, so duplicates are not supported.
210      * If there are any duplicates, then an exception will be thrown.
211      *
212      * @param string $parentKey
213      * @param \DOMNode $element
214      * @return array
215      */
216     protected function getUniqueChildren($parentKey, $element)
217     {
218         $children = $this->getNodeChildrenData($element);
219         if ((count($children) == 1) && (is_string($children[0]))) {
220             return [$element->nodeName => $children[0]];
221         }
222         $simplifiedChildren = [];
223         foreach ($children as $key => $value) {
224             if (is_numeric($key) && is_array($value) && (count($value) == 1)) {
225                 $valueKeys = array_keys($value);
226                 $key = $valueKeys[0];
227                 $value = array_shift($value);
228             }
229             if (array_key_exists($key, $simplifiedChildren)) {
230                 throw new \Exception("Cannot convert data from a DOM document to an array, because <$key> appears more than once, and is not wrapped in a <{$key}s> element.");
231             }
232             $simplifiedChildren[$key] = $value;
233         }
234         return $simplifiedChildren;
235     }
236 }