Test Setup Failed
Push — master ( b50703...9d91c5 )
by Dominik
06:16
created

XmlDecoderType::decode()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
c 0
b 0
f 0
rs 9.4285
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Chubbyphp\Deserialization\Decoder;
6
7
final class XmlDecoderType implements DecoderTypeInterface
8
{
9
    /**
10
     * @var int
11
     */
12
    private $options;
13
14
    /**
15
     * @param int $options
16
     */
17
    public function __construct($options = 0)
18
    {
19
        $this->options = $options;
20
    }
21
22
    /**
23
     * @return string
24
     */
25
    public function getContentType(): string
26
    {
27
        return 'application/xml';
28
    }
29
30
    /**
31
     * @param string $data
32
     *
33
     * @return array
34
     *
35
     * @throws DecoderException
36
     */
37
    public function decode(string $data): array
38
    {
39
        $document = new \DOMDocument();
40
41
        if (!@$document->loadXML($data, $this->options)) {
42
            throw DecoderException::createNotParsable($this->getContentType());
43
        }
44
45
        return $this->transformType($document->getElementsByTagName('object')->item(0));
46
    }
47
48
    /**
49
     * @param \DOMElement $node
50
     *
51
     * @return array
52
     */
53
    private function transformType(\DOMElement $node): array
54
    {
55
        $data = [];
56
57
        $childNodes = [];
58
        foreach ($node->childNodes as $childNode) {
59
            if ($childNode instanceof \DOMElement) {
60
                $childNodes[] = $childNode;
61
            }
62
        }
63
64
        foreach ($childNodes as $childNode) {
65
            if (0 === $childNode->childNodes->length) {
66
                $data[$this->getKey($childNode)] = null;
67
68
                continue;
69
            }
70
71
            if (1 === $childNode->childNodes->length) {
72
                $data[$this->getKey($childNode)] = $this->getValue($childNode);
73
74
                continue;
75
            }
76
77
            if ('object' === $childNode->nodeName && !$childNode->hasAttribute('key')) {
78
                return $this->transformType($childNode);
79
            }
80
81
            $data[$this->getKey($childNode)] = $this->transformType($childNode);
82
        }
83
84
        if ('object' === $node->nodeName && $node->hasAttribute('value')) {
85
            $data['_type'] = $node->getAttribute('value');
86
        }
87
88
        return $data;
89
    }
90
91
    /**
92
     * @param \DOMElement $node
93
     *
94
     * @return string|int
95
     */
96
    private function getKey(\DOMElement $node)
97
    {
98
        if ($node->hasAttribute('key')) {
99
            return (int) $node->getAttribute('key');
100
        }
101
102
        $name = $node->nodeName;
103
        if (0 === strpos($name, 'meta-')) {
104
            $name = '_'.substr($name, 5);
105
        }
106
107
        return $name;
108
    }
109
110
    /**
111
     * @param \DOMElement $node
112
     *
113
     * @return bool|string
114
     */
115
    private function getValue(\DOMElement $node)
116
    {
117
        $value = $node->nodeValue;
118
119
        if ('boolean' === $type = $node->getAttribute('type')) {
120
            return 'true' === $value;
121
        }
122
123
        settype($value, $type);
124
125
        return $value;
126
    }
127
}
128