Yaffs site version 1.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         // Check for plurals.
89         if (in_array($element->nodeName, ["{$uniformChildrenName}s", "{$uniformChildrenName}es"])) {
90             $result = $this->getUniformChildren($element->nodeName, $element);
91         } else {
92             $result = $this->getUniqueChildren($element->nodeName, $element);
93         }
94         return array_filter($result);
95     }
96
97     /**
98      * Get the data from the children of the provided node in preliminary
99      * form.
100      *
101      * @param \DOMNode $element
102      * @return array
103      */
104     protected function getNodeChildrenData($element)
105     {
106         $children = [];
107         foreach ($element->childNodes as $key => $value) {
108             $children[$key] = $this->elementToArray($value);
109         }
110         return $children;
111     }
112
113     /**
114      * Determine whether the children of the provided element are uniform.
115      * @see getUniformChildren(), below.
116      *
117      * @param \DOMNode $element
118      * @return boolean
119      */
120     protected function hasUniformChildren($element)
121     {
122         $last = false;
123         foreach ($element->childNodes as $key => $value) {
124             $name = $value->nodeName;
125             if (!$name) {
126                 return false;
127             }
128             if ($last && ($name != $last)) {
129                 return false;
130             }
131             $last = $name;
132         }
133         return $last;
134     }
135
136     /**
137      * Convert the children of the provided DOM element into an array.
138      * Here, 'uniform' means that all of the element names of the children
139      * are identical, and further, the element name of the parent is the
140      * plural form of the child names.  When the children are uniform in
141      * this way, then the parent element name will be used as the key to
142      * store the children in, and the child list will be returned as a
143      * simple list with their (duplicate) element names omitted.
144      *
145      * @param string $parentKey
146      * @param \DOMNode $element
147      * @return array
148      */
149     protected function getUniformChildren($parentKey, $element)
150     {
151         $children = $this->getNodeChildrenData($element);
152         $simplifiedChildren = [];
153         foreach ($children as $key => $value) {
154             if ($this->valueCanBeSimplified($value)) {
155                 $value = array_shift($value);
156             }
157             $id = $this->getIdOfValue($value);
158             if ($id) {
159                 $simplifiedChildren[$parentKey][$id] = $value;
160             } else {
161                 $simplifiedChildren[$parentKey][] = $value;
162             }
163         }
164         return $simplifiedChildren;
165     }
166
167     /**
168      * Determine whether the provided value has additional unnecessary
169      * nesting.  {"color": "red"} is converted to "red". No other
170      * simplification is done.
171      *
172      * @param \DOMNode $value
173      * @return boolean
174      */
175     protected function valueCanBeSimplified($value)
176     {
177         if (!is_array($value)) {
178             return false;
179         }
180         if (count($value) != 1) {
181             return false;
182         }
183         $data = array_shift($value);
184         return is_string($data);
185     }
186
187     /**
188      * If the object has an 'id' or 'name' element, then use that
189      * as the array key when storing this value in its parent.
190      * @param mixed $value
191      * @return string
192      */
193     protected function getIdOfValue($value)
194     {
195         if (!is_array($value)) {
196             return false;
197         }
198         if (array_key_exists('id', $value)) {
199             return trim($value['id'], '-');
200         }
201         if (array_key_exists('name', $value)) {
202             return trim($value['name'], '-');
203         }
204     }
205
206     /**
207      * Convert the children of the provided DOM element into an array.
208      * Here, 'unique' means that all of the element names of the children are
209      * different.  Since the element names will become the key of the
210      * associative array that is returned, so duplicates are not supported.
211      * If there are any duplicates, then an exception will be thrown.
212      *
213      * @param string $parentKey
214      * @param \DOMNode $element
215      * @return array
216      */
217     protected function getUniqueChildren($parentKey, $element)
218     {
219         $children = $this->getNodeChildrenData($element);
220         if ((count($children) == 1) && (is_string($children[0]))) {
221             return [$element->nodeName => $children[0]];
222         }
223         $simplifiedChildren = [];
224         foreach ($children as $key => $value) {
225             if (is_numeric($key) && is_array($value) && (count($value) == 1)) {
226                 $valueKeys = array_keys($value);
227                 $key = $valueKeys[0];
228                 $value = array_shift($value);
229             }
230             if (array_key_exists($key, $simplifiedChildren)) {
231                 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.");
232             }
233             $simplifiedChildren[$key] = $value;
234         }
235         return $simplifiedChildren;
236     }
237 }