Passed
Branch dev (ed2997)
by Бабичев
19:38 queued 09:42
created

XMLReader::asObject()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Bavix\XMLReader;
4
5
use Bavix\Exceptions;
6
use Bavix\Foundation\SharedInstance;
7
use Bavix\Helpers\Arr;
8
use Bavix\Helpers\File;
9
use Bavix\Helpers\JSON;
10
use DOMElement;
11
12
class XMLReader
13
{
14
15
    use SharedInstance;
16
17
    /**
18
     * @var array
19
     */
20
    protected $copyright = ['created-with' => 'https://github.com/bavix/xml'];
21
22
    /**
23
     * @var \DOMDocument
24
     */
25
    protected $document;
26
27
    /**
28
     * @return \DOMDocument
29
     */
30 1
    protected function document()
31
    {
32 1
        if (!$this->document)
33
        {
34 1
            $this->document                = new \DOMDocument('1.0', 'utf-8');
35 1
            $this->document->formatOutput  = true;
36 1
            $this->copyright['created-at'] = date('Y-m-d H:i:s');
37
        }
38
39 1
        return $this->document;
40
    }
41
42
    /**
43
     * @param string $name
44
     *
45
     * @return \DOMElement
46
     */
47 1
    protected function element($name)
48
    {
49 1
        return $this->document()->createElement($name);
50
    }
51
52
    /**
53
     * @param \SimpleXMLElement $element
54
     *
55
     * @return array|string
56
     *
57
     * @codeCoverageIgnore
58
     */
59
    protected function _asData(\SimpleXMLElement $element)
60
    {
61
        $output = [];
62
63
        $attributes = $element->attributes();
64
65
        if ($attributes)
66
        {
67
            $output['@attributes'] = $this->_asArray($attributes);
68
69
            if (empty($output['@attributes']))
70
            {
71
                Arr::remove($output, '@attributes');
72
            }
73
        }
74
75
        if (!$element->count())
76
        {
77
            $output['@value'] = (string)$element;
78
79
            if (!isset($output['@attributes']))
80
            {
81
                $output = $output['@value'];
82
            }
83
        }
84
85
        return $output;
86
    }
87
88
    /**
89
     * @param \SimpleXMLElement $element
90
     *
91
     * @return array|string
92
     *
93
     * @codeCoverageIgnore
94
     */
95
    protected function _asArray(\SimpleXMLElement $element)
96
    {
97
        $output = $this->_asData($element);
98
99
        if (!$element->count())
100
        {
101
            return $output;
102
        }
103
104
        /**
105
         * @var \SimpleXMLElement $item
106
         */
107
        foreach ($element as $key => $item)
108
        {
109
            if (!$item->count())
110
            {
111
                 $output[$key] = $this->_asArray($item);
112
                 continue;
113
            }
114
115
            Arr::initOrPush(
116
                $output,
0 ignored issues
show
Bug introduced by
It seems like $output can also be of type string; however, parameter $storage of Bavix\Helpers\Arr::initOrPush() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

116
                /** @scrutinizer ignore-type */ $output,
Loading history...
117
                $key,
118
                $this->_asArray($item)
0 ignored issues
show
Bug introduced by
$this->_asArray($item) of type string|array is incompatible with the type integer expected by parameter $value of Bavix\Helpers\Arr::initOrPush(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

118
                /** @scrutinizer ignore-type */ $this->_asArray($item)
Loading history...
119
            );
120
        }
121
122
        return $output;
123
    }
124
125
    /**
126
     * @param string|\DOMNode $mixed
127
     *
128
     * @return array
129
     */
130 1
    public function asArray($mixed)
131
    {
132 1
        if ($mixed instanceof \DOMNode)
133
        {
134
            return $this->_asArray(\simplexml_import_dom($mixed));
135
        }
136
137 1
        if (File::isFile($mixed))
138
        {
139
            return $this->_asArray(\simplexml_load_file($mixed));
140
        }
141
142 1
        return $this->_asArray(\simplexml_load_string($mixed));
143
    }
144
145
    /**
146
     * @return \DOMDocument
147
     */
148
    public function asObject()
149
    {
150
        return clone $this->document();
151
    }
152
153
    /**
154
     * @param array $storage
155
     *
156
     * @return string
157
     */
158 1
    public function asXML(array $storage)
159
    {
160 1
        $element = $this->element('bavix');
161
162 1
        $this->addAttributes($element, $this->copyright);
163 1
        $this->document()->appendChild($element);
164 1
        $this->convert($element, $storage);
165 1
        $xml = $this->document()->saveXML();
166
167 1
        $this->document = null;
168
169 1
        return $xml;
170
    }
171
172
    /**
173
     * @param DOMElement $element
174
     * @param mixed      $storage
175
     *
176
     * @throws Exceptions\Blank
177
     */
178 1
    protected function convert(DOMElement $element, $storage)
179
    {
180 1
        if (!is_array($storage))
181
        {
182 1
            $element->nodeValue = htmlspecialchars($storage);
183
184 1
            return;
185
        }
186
187 1
        if (empty($storage))
188
        {
189
            throw new Exceptions\Blank('Array is empty');
190
        }
191
192 1
        $isInt      = Arr::map(Arr::getKeys($storage), 'is_int');
193 1
        $sequential = !Arr::in($isInt, false, true);
194
195 1
        foreach ($storage as $key => $data)
196
        {
197 1
            if ($sequential)
198
            {
199 1
                $this->sequential($element, $data);
200 1
                continue;
201
            }
202
203 1
            $this->addNodeWithKey($key, $element, $data);
204
        }
205 1
    }
206
207
    /**
208
     * @param string     $key
209
     * @param DOMElement $element
210
     * @param mixed      $storage
211
     */
212 1
    protected function addNodeWithKey($key, DOMElement $element, $storage)
213
    {
214 1
        if ($key === '@attributes')
215
        {
216 1
            $this->addAttributes($element, $storage);
217
        }
218 1
        else if ($key === '@value' && is_string($storage))
219
        {
220 1
            $element->nodeValue = htmlspecialchars($storage);
221
        }
222
        else
223
        {
224 1
            $this->addNode($element, $key, $storage);
225
        }
226 1
    }
227
228
    /**
229
     * @param DOMElement $element
230
     * @param mixed      $storage
231
     */
232 1
    protected function sequential(DOMElement $element, $storage)
233
    {
234 1
        if (is_array($storage))
235
        {
236 1
            $this->addCollectionNode($element, $storage);
237
238 1
            return;
239
        }
240
241
        $this->addSequentialNode($element, $storage);
242
    }
243
244
    /**
245
     * @param DOMElement $element
246
     * @param string     $key
247
     * @param mixed      $value
248
     *
249
     * @throws Exceptions\Blank
250
     */
251 1
    protected function addNode(DOMElement $element, $key, $value)
252
    {
253 1
        $key   = str_replace(' ', '_', $key);
254 1
        $child = $this->document()->createElement($key);
255 1
        $element->appendChild($child);
256 1
        $this->convert($child, $value);
257 1
    }
258
259
    /**
260
     * @param DOMElement $element
261
     * @param mixed      $value
262
     *
263
     * @throws Exceptions\Blank
264
     */
265 1
    protected function addCollectionNode(DOMElement $element, $value)
266
    {
267 1
        if ($element->childNodes->length === 0)
268
        {
269 1
            $this->convert($element, $value);
270
271 1
            return;
272
        }
273
274
        /**
275
         * @var $child DOMElement
276
         */
277 1
        $child = $element->cloneNode();
278 1
        $element->parentNode->appendChild($child);
279 1
        $this->convert($child, $value);
280 1
    }
281
282
    /**
283
     * @param DOMElement $element
284
     * @param mixed      $value
285
     */
286
    protected function addSequentialNode(DOMElement $element, $value)
287
    {
288
        if (empty($element->nodeValue))
289
        {
290
            $element->nodeValue = htmlspecialchars($value);
291
292
            return;
293
        }
294
295
        $child            = $element->cloneNode();
296
        $child->nodeValue = htmlspecialchars($value);
297
        $element->parentNode->appendChild($child);
298
    }
299
300
    /**
301
     * @param DOMElement $element
302
     * @param array      $storage
303
     */
304 1
    protected function addAttributes(DOMElement $element, array $storage)
305
    {
306 1
        foreach ($storage as $attrKey => $attrVal)
307
        {
308 1
            $element->setAttribute($attrKey, $attrVal);
309
        }
310 1
    }
311
312
}
313