Passed
Push — master ( 3feb78...25ed05 )
by Бабичев
10:24
created

XMLReader::_property()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 24
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5.1502

Importance

Changes 0
Metric Value
cc 5
eloc 10
nc 6
nop 2
dl 0
loc 24
ccs 9
cts 11
cp 0.8182
crap 5.1502
rs 8.5125
c 0
b 0
f 0
1
<?php
2
3
namespace Bavix\XMLReader;
4
5
use Bavix\Foundation\SharedInstance;
6
use Bavix\Helpers\File;
7
use Bavix\Helpers\Arr;
8
use Bavix\Exceptions;
9
use DOMElement;
10
11
class XMLReader
12
{
13
14
    use SharedInstance;
15
16
    /**
17
     * @var array
18
     */
19
    protected $copyright = [
20
        'created-with' => 'https://github.com/bavix/xml'
21
    ];
22
23
    /**
24
     * @var \DOMDocument
25
     */
26
    protected $document;
27
28
    /**
29
     * @return \DOMDocument
30
     */
31 1
    protected function document()
32
    {
33 1
        if (!$this->document)
34
        {
35 1
            $this->document                = new \DOMDocument('1.0', 'utf-8');
36 1
            $this->document->formatOutput  = true;
37 1
            $this->copyright['created-at'] = \date('Y-m-d H:i:s');
38
        }
39
40 1
        return $this->document;
41
    }
42
43
    /**
44
     * @param string $name
45
     *
46
     * @return \DOMElement
47
     */
48 1
    protected function element($name)
49
    {
50 1
        return $this->document()->createElement($name);
51
    }
52
53
    /**
54
     * @param \SimpleXMLElement $element
55
     * @param string            $property
56
     *
57
     * @return array
58
     */
59 1
    protected function _property(\SimpleXMLElement $element, $property)
60
    {
61 1
        $output = [];
62
63 1
        if (method_exists($element, $property))
64
        {
65
66 1
            $properties = $element->$property();
67
68 1
            if ($properties)
69
            {
70 1
                $output['@' . $property] = is_array($properties) ?
71
                    $properties :
72 1
                    $this->_asArray($properties);
73
74 1
                if (empty($output['@' . $property]))
75
                {
76
                    Arr::remove($output, '@' . $property);
77
                }
78
            }
79
80
        }
81
82 1
        return $output;
83
    }
84
85
    /**
86
     * @param \SimpleXMLElement $element
87
     *
88
     * @return array|string
89
     *
90
     * @codeCoverageIgnore
91
     */
92
    protected function _asData(\SimpleXMLElement $element)
93
    {
94
        $output = $this->_property($element, 'attributes');
95
96
        if (!$element->count())
97
        {
98
            $output['@value'] = (string)$element;
99
100
            if (!isset($output['@attributes']))
101
            {
102
                $output = $output['@value'];
103
            }
104
        }
105
106
        return $output;
107
    }
108
109
    /**
110
     * @param \SimpleXMLElement $element
111
     *
112
     * @return array|string
113
     *
114
     * @codeCoverageIgnore
115
     */
116
    protected function _asArray(\SimpleXMLElement $element)
117
    {
118
        $output = $this->_asData($element);
119
120
        if (!$element->count())
121
        {
122
            return $output;
123
        }
124
125
        $first = [];
126
127
        /**
128
         * @var \SimpleXMLElement $item
129
         */
130
        foreach ($element as $key => $item)
131
        {
132
            if (!isset($output[$key]))
133
            {
134
                $first[$key]  = true;
135
                $output[$key] = $this->_asArray($item);
136
                continue;
137
            }
138
139
            if (!empty($first[$key]))
140
            {
141
                $output[$key] = [$output[$key]];
142
            }
143
144
            $output[$key][] = $this->_asArray($item);
145
            $first[$key]    = false;
146
        }
147
148
        return $output;
149
    }
150
151
    /**
152
     * @param string|\DOMNode $mixed
153
     *
154
     * @return \SimpleXMLElement
155
     */
156 1
    protected function _simpleXml($mixed)
157
    {
158 1
        if ($mixed instanceof \DOMNode)
159
        {
160
            return \simplexml_import_dom($mixed);
161
        }
162
163 1
        if (File::isFile($mixed))
164
        {
165
            return \simplexml_load_file($mixed);
166
        }
167
168 1
        return \simplexml_load_string($mixed);
169
    }
170
171
    /**
172
     * @param string|\DOMNode $mixed
173
     *
174
     * @return array
175
     */
176 1
    public function asArray($mixed)
177
    {
178 1
        $data = $this->_simpleXml($mixed);
179
180 1
        return $data ?
181 1
            $this->_asArray($data) :
182 1
            null;
183
    }
184
185
    /**
186
     * @return \DOMDocument
187
     */
188
    public function asObject()
189
    {
190
        return clone $this->document();
191
    }
192
193
    /**
194
     * @param array|\Traversable $storage
195
     *
196
     * @return array
197
     */
198 1
    protected function _convertStorage($storage)
199
    {
200 1
        if ($storage instanceof \Traversable)
201
        {
202
            return \iterator_to_array($storage);
203
        }
204
205 1
        return $storage;
206
    }
207
208
    /**
209
     * @param array|\Traversable $storage
210
     * @param string             $name
211
     *
212
     * @return string
213
     */
214 1
    public function asXML($storage, $name = 'bavix')
215
    {
216 1
        $element = $this->element($name);
217
218 1
        $this->addAttributes($element, $this->copyright);
219 1
        $this->document()->appendChild($element);
220 1
        $this->convert($element, $this->_convertStorage($storage));
221 1
        $xml = $this->document()->saveXML();
222
223 1
        $this->document = null;
224
225 1
        return $xml;
226
    }
227
228
    /**
229
     * @param DOMElement $element
230
     * @param mixed      $storage
231
     *
232
     * @throws Exceptions\Blank
233
     */
234 1
    protected function convert(DOMElement $element, $storage)
235
    {
236 1
        if (!is_array($storage))
237
        {
238 1
            $element->nodeValue = htmlspecialchars($storage);
239
240 1
            return;
241
        }
242
243 1
        if (empty($storage))
244
        {
245
            throw new Exceptions\Blank('Array is empty');
246
        }
247
248 1
        $isInt      = Arr::map(Arr::getKeys($storage), '\is_int');
249 1
        $sequential = !Arr::in($isInt, false);
250
251 1
        foreach ($storage as $key => $data)
252
        {
253 1
            if ($sequential)
254
            {
255 1
                $this->sequential($element, $data);
256 1
                continue;
257
            }
258
259 1
            $this->addNodeWithKey($key, $element, $data);
260
        }
261 1
    }
262
263
    /**
264
     * @param string     $key
265
     * @param DOMElement $element
266
     * @param mixed      $storage
267
     */
268 1
    protected function addNodeWithKey($key, DOMElement $element, $storage)
269
    {
270 1
        if ($key === '@attributes')
271
        {
272 1
            $this->addAttributes($element, $storage);
273
        }
274 1
        else if ($key === '@value' && \is_string($storage))
275
        {
276 1
            $element->nodeValue = \htmlspecialchars($storage);
277
        }
278
        else
279
        {
280 1
            $this->addNode($element, $key, $storage);
281
        }
282 1
    }
283
284
    /**
285
     * @param DOMElement $element
286
     * @param mixed      $storage
287
     */
288 1
    protected function sequential(DOMElement $element, $storage)
289
    {
290 1
        if (is_array($storage))
291
        {
292 1
            $this->addCollectionNode($element, $storage);
293
294 1
            return;
295
        }
296
297 1
        $this->addSequentialNode($element, $storage);
298 1
    }
299
300
    /**
301
     * @param DOMElement $element
302
     * @param string     $key
303
     * @param mixed      $value
304
     *
305
     * @throws Exceptions\Blank
306
     */
307 1
    protected function addNode(DOMElement $element, $key, $value)
308
    {
309 1
        $key   = \str_replace(' ', '-', $key);
310 1
        $child = $this->document()->createElement($key);
311 1
        $element->appendChild($child);
312 1
        $this->convert($child, $value);
313 1
    }
314
315
    /**
316
     * @param DOMElement $element
317
     * @param mixed      $value
318
     *
319
     * @throws Exceptions\Blank
320
     */
321 1
    protected function addCollectionNode(DOMElement $element, $value)
322
    {
323 1
        if ($element->childNodes->length === 0)
324
        {
325 1
            $this->convert($element, $value);
326
327 1
            return;
328
        }
329
330
        /**
331
         * @var $child DOMElement
332
         */
333 1
        $child = $this->document()->createElement($element->nodeName);
334
//        $child = $element->cloneNode();
335 1
        $element->parentNode->appendChild($child);
336 1
        $this->convert($child, $value);
337 1
    }
338
339
    /**
340
     * @param DOMElement $element
341
     * @param mixed      $value
342
     */
343 1
    protected function addSequentialNode(DOMElement $element, $value)
344
    {
345 1
        if (empty($element->nodeValue))
346
        {
347 1
            $element->nodeValue = \htmlspecialchars($value);
348
349 1
            return;
350
        }
351
352 1
        $child = $this->document()->createElement($element->nodeName);
353
//        $child = $element->cloneNode();
354 1
        $child->nodeValue = \htmlspecialchars($value);
355 1
        $element->parentNode->appendChild($child);
356 1
    }
357
358
    /**
359
     * @param DOMElement         $element
360
     * @param array|\Traversable $storage
361
     */
362 1
    protected function addAttributes(DOMElement $element, $storage)
363
    {
364 1
        foreach ($storage as $attrKey => $attrVal)
365
        {
366 1
            $element->setAttribute($attrKey, $attrVal);
367
        }
368 1
    }
369
370
}
371