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

DomToArraySimplifier   B

Complexity

Total Complexity 36

Size/Duplication

Total Lines 202
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 36
lcom 1
cbo 1
dl 0
loc 202
rs 8.8
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 1
A canSimplify() 0 7 3
A simplifyToArray() 0 12 3
A elementToArray() 0 10 2
A getNodeAttributes() 0 11 3
A getNodeChildren() 0 13 3
A getNodeChildrenData() 0 8 2
B hasUniformChildren() 0 15 5
A getUniformChildren() 0 12 3
A valueCanBeSimplified() 0 11 3
B getUniqueChildren() 0 20 8
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();
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...
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 $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
            $simplifiedChildren[$parentKey][] = $value;
157
        }
158
        return $simplifiedChildren;
159
    }
160
161
    /**
162
     * Determine whether the provided value has additional unnecessary
163
     * nesting.  {"color": "red"} is converted to "red". No other
164
     * simplification is done.
165
     *
166
     * @param \DOMNode $value
167
     * @return boolean
168
     */
169
    protected function valueCanBeSimplified($value)
170
    {
171
        if (!is_array($value)) {
172
            return false;
173
        }
174
        if (count($value) != 1) {
175
            return false;
176
        }
177
        $data = array_shift($value);
178
        return is_string($data);
179
    }
180
181
    /**
182
     * Convert the children of the provided DOM element into an array.
183
     * Here, 'unique' means that all of the element names of the children are
184
     * different.  Since the element names will become the key of the
185
     * associative array that is returned, so duplicates are not supported.
186
     * If there are any duplicates, then an exception will be thrown.
187
     *
188
     * @param string $parentKey
189
     * @param \DOMNode $element
190
     * @return array
191
     */
192
    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...
193
    {
194
        $children = $this->getNodeChildrenData($element);
195
        if ((count($children) == 1) && (is_string($children[0]))) {
196
            return [$element->nodeName => $children[0]];
197
        }
198
        $simplifiedChildren = [];
199
        foreach ($children as $key => $value) {
200
            if (is_numeric($key) && is_array($value) && (count($value) == 1)) {
201
                $valueKeys = array_keys($value);
202
                $key = $valueKeys[0];
203
                $value = array_shift($value);
204
            }
205
            if (array_key_exists($key, $simplifiedChildren)) {
206
                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.");
207
            }
208
            $simplifiedChildren[$key] = $value;
209
        }
210
        return $simplifiedChildren;
211
    }
212
}
213