Completed
Pull Request — master (#25)
by Greg
02:58
created

DomToArraySimplifier::canSimplify()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 5
nc 3
nop 1
1
<?php
2
namespace Consolidation\OutputFormatters\Transformations;
3
4
use Consolidation\OutputFormatters\SimplifyToArrayInterface;
5
use Consolidation\OutputFormatters\FormatterOptions;
6
use Consolidation\OutputFormatters\StructuredData\Xml\DomDataInterface;
7
use Consolidation\OutputFormatters\StructuredData\Xml\XmlSchema;
8
9
/**
10
 * Simplify a DOMDocument to an array.
11
 */
12
class DomToArraySimplifier implements SimplifyToArrayInterface
13
{
14
    public function __construct()
15
    {
16
    }
17
18
    /**
19
     * @param ReflectionClass $dataType
20
     */
21
    public function canSimplify(\ReflectionClass $dataType)
22
    {
23
        return
24
            $dataType->isSubclassOf('\Consolidation\OutputFormatters\StructuredData\Xml\DomDataInterface') ||
25
            $dataType->isSubclassOf('DOMDocument') ||
26
            ($dataType->getName() == 'DOMDocument');
27
    }
28
29
    public function simplifyToArray($structuredData, FormatterOptions $options)
30
    {
31
        if ($structuredData instanceof DomDataInterface) {
32
            $structuredData = $structuredData->getDomData();
33
        }
34
        if ($structuredData instanceof \DOMDocument) {
35
            // $schema = $options->getXmlSchema();
0 ignored issues
show
Unused Code Comprehensibility introduced by
55% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
36
            $simplified = $this->elementToArray($structuredData);
37
            $structuredData = array_shift($simplified);
38
        }
39
        return $structuredData;
40
    }
41
42
    /**
43
     * Recursively convert the provided DOM element into a php array.
44
     *
45
     * @param \DOMNode $element
46
     * @return array
47
     */
48
    protected function elementToArray(\DOMNode $element)
49
    {
50
        if ($element->nodeType == XML_TEXT_NODE) {
51
            return $element->nodeValue;
52
        }
53
        $attributes = $this->getNodeAttributes($element);
54
        $children = $this->getNodeChildren($element);
55
56
        return array_merge($attributes, $children);
57
    }
58
59
    /**
60
     * Get all of the attributes of the provided element.
61
     *
62
     * @param \DOMNode $element
63
     * @return array
64
     */
65
    protected function getNodeAttributes($element)
66
    {
67
        if (empty($element->attributes)) {
68
            return [];
69
        }
70
        $attributes = [];
71
        foreach ($element->attributes as $key => $attribute) {
72
            $attributes[$key] = $attribute->nodeValue;
73
        }
74
        return $attributes;
75
    }
76
77
    /**
78
     * Get all of the children of the provided element, with simplification.
79
     *
80
     * @param \DOMNode $element
81
     * @return array
82
     */
83
    protected function getNodeChildren($element)
84
    {
85
        if (empty($element->childNodes)) {
86
            return [];
87
        }
88
        $uniformChildrenName = $this->hasUniformChildren($element);
89
        if ("{$uniformChildrenName}s" == $element->nodeName) {
90
            $result = $this->getUniformChildren($element->nodeName, $element);
91
        } else {
92
            $result = $this->getUniqueChildren($element->nodeName, $element);
93
        }
94
        return $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
            $simplifiedChildren[$parentKey][] = $value;
158
        }
159
        return $simplifiedChildren;
160
    }
161
162
    /**
163
     * Determine whether the provided value has additional unnecessary
164
     * nesting.  {"color": "red"} is converted to "red". No other
165
     * simplification is done.
166
     *
167
     * @param \DOMNode $value
168
     * @return boolean
169
     */
170
    protected function valueCanBeSimplified($value)
171
    {
172
        if (!is_array($value)) {
173
            return false;
174
        }
175
        if (count($value) != 1) {
176
            return false;
177
        }
178
        $data = array_shift($value);
179
        return is_string($data);
180
    }
181
182
    /**
183
     * Convert the children of the provided DOM element into an array.
184
     * Here, 'unique' means that all of the element names of the children are
185
     * different.  Since the element names will become the key of the
186
     * associative array that is returned, so duplicates are not supported.
187
     * If there are any duplicates, then an exception will be thrown.
188
     *
189
     * @param string $parentKey
190
     * @param \DOMNode $element
191
     * @return array
192
     */
193
    protected function getUniqueChildren($parentKey, $element)
0 ignored issues
show
Unused Code introduced by
The parameter $parentKey is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
194
    {
195
        $children = $this->getNodeChildrenData($element);
196
        if ((count($children) == 1) && (is_string($children[0]))) {
197
            return [$element->nodeName => $children[0]];
198
        }
199
        $simplifiedChildren = [];
200
        foreach ($children as $key => $value) {
201
            if (is_numeric($key) && is_array($value) && (count($value) == 1)) {
202
                $valueKeys = array_keys($value);
203
                $key = $valueKeys[0];
204
                $value = array_shift($value);
205
            }
206
            if (array_key_exists($key, $simplifiedChildren)) {
207
                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.");
208
            }
209
            $simplifiedChildren[$key] = $value;
210
        }
211
        return $simplifiedChildren;
212
    }
213
}
214