JsonxTypeDecoder::decodeNode()   B
last analyzed

Complexity

Conditions 7
Paths 7

Size

Total Lines 31
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 7

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 7
eloc 15
c 1
b 0
f 1
nc 7
nop 1
dl 0
loc 31
ccs 12
cts 12
cp 1
crap 7
rs 8.8333
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Chubbyphp\Deserialization\Decoder;
6
7
use Chubbyphp\Deserialization\DeserializerRuntimeException;
8
9
/**
10
 * @see https://www.ibm.com/support/knowledgecenter/SS9H2Y_7.6.0/com.ibm.dp.doc/json_jsonx.html
11
 */
12
final class JsonxTypeDecoder implements TypeDecoderInterface
13
{
14
    const DATATYPE_OBJECT = 'object';
15
    const DATATYPE_ARRAY = 'array';
16
    const DATATYPE_BOOLEAN = 'boolean';
17
    const DATATYPE_STRING = 'string';
18
    const DATATYPE_NUMBER = 'number';
19
    const DATATYPE_NULL = 'null';
20
21
    /**
22
     * @var string
23
     */
24 4
    private $contentType;
25
26 4
    public function __construct(string $contentType = 'application/x-jsonx')
27
    {
28
        if ('application/x-jsonx' === $contentType) {
29
            @trigger_error(
30
                'Use "application/jsonx+xml" instead of "application/x-jsonx", cause jsonx is a xml variant.',
31
                E_USER_DEPRECATED
32
            );
33
        }
34
35
        $this->contentType = $contentType;
36 5
    }
37
38 5
    public function getContentType(): string
39
    {
40 5
        return $this->contentType;
41 1
    }
42
43
    /**
44 4
     * @throws DeserializerRuntimeException
45
     */
46
    public function decode(string $data): array
47
    {
48
        $document = new \DOMDocument();
49
50
        if (!@$document->loadXML($data)) {
51
            throw DeserializerRuntimeException::createNotParsable($this->getContentType());
52 4
        }
53
54 4
        return $this->decodeNode($document->documentElement);
55
    }
56 4
57
    /**
58 4
     * @return array|bool|string|int|float|null
59 2
     */
60
    private function decodeNode(\DOMNode $node)
61
    {
62 4
        $nodeName = $node->nodeName;
63 1
64
        $nodeType = substr($nodeName, 5);
65
66 4
        if (self::DATATYPE_OBJECT === $nodeType) {
67 2
            return $this->decodeObjectNode($node);
68
        }
69
70 4
        if (self::DATATYPE_ARRAY === $nodeType) {
71 2
            return $this->decodeArrayNode($node);
72
        }
73
74 4
        if (self::DATATYPE_BOOLEAN === $nodeType) {
75 2
            return $this->decodeBooleanNode($node);
76
        }
77
78 3
        if (self::DATATYPE_STRING === $nodeType) {
79 1
            return $this->decodeStringNode($node);
80
        }
81
82 2
        if (self::DATATYPE_NUMBER === $nodeType) {
83
            return $this->decodeNumberNode($node);
84
        }
85
86
        if (self::DATATYPE_NULL === $nodeType) {
87
            return null;
88
        }
89
90 2
        throw DeserializerRuntimeException::createNotParsable($this->getContentType());
91
    }
92 2
93 2
    private function decodeObjectNode(\DOMNode $node): array
94 2
    {
95 2
        $data = [];
96
        foreach ($node->childNodes as $childNode) {
97
            if ($childNode instanceof \DOMText) {
98 2
                continue;
99
            }
100
101 2
            $data[$childNode->getAttribute('name')] = $this->decodeNode($childNode);
102
        }
103
104
        return $data;
105
    }
106
107
    private function decodeArrayNode(\DOMNode $node): array
108
    {
109 1
        $data = [];
110
        foreach ($node->childNodes as $childNode) {
111 1
            if ($childNode instanceof \DOMText) {
112 1
                continue;
113 1
            }
114 1
115
            $data[] = $this->decodeNode($childNode);
116
        }
117 1
118
        return $data;
119
    }
120 1
121
    private function decodeBooleanNode(\DOMNode $node): bool
122
    {
123
        return 'true' === $node->nodeValue;
124
    }
125
126
    private function decodeStringNode(\DOMNode $node): string
127
    {
128 2
        return html_entity_decode($node->nodeValue, ENT_COMPAT | ENT_XML1, 'UTF-8');
129
    }
130 2
131
    /**
132
     * @return int|float
133
     */
134
    private function decodeNumberNode(\DOMNode $node)
135
    {
136
        $value = $node->nodeValue;
137
138 2
        if ($value === (string) (int) $value) {
139
            return (int) $value;
140 2
        }
141
142
        return (float) $value;
143
    }
144
}
145