JsonxTypeEncoder::getType()   B
last analyzed

Complexity

Conditions 9
Paths 7

Size

Total Lines 30
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 9

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 9
eloc 16
nc 7
nop 1
dl 0
loc 30
ccs 10
cts 10
cp 1
crap 9
rs 8.0555
c 1
b 0
f 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Chubbyphp\Serialization\Encoder;
6
7
/**
8
 * @see https://www.ibm.com/support/knowledgecenter/SS9H2Y_7.6.0/com.ibm.dp.doc/json_jsonx.html
9
 */
10
final class JsonxTypeEncoder implements TypeEncoderInterface
11
{
12
    const DATATYPE_OBJECT = 'object';
13
    const DATATYPE_ARRAY = 'array';
14
    const DATATYPE_BOOLEAN = 'boolean';
15
    const DATATYPE_STRING = 'string';
16
    const DATATYPE_NUMBER = 'number';
17
    const DATATYPE_NULL = 'null';
18
19
    /**
20
     * @var bool
21
     */
22
    private $prettyPrint;
23
24
    /**
25
     * @var string
26 4
     */
27
    private $contentType;
28 4
29 4
    public function __construct(bool $prettyPrint = false, string $contentType = 'application/x-jsonx')
30
    {
31
        $this->prettyPrint = $prettyPrint;
32
33
        if ('application/x-jsonx' === $contentType) {
34 1
            @trigger_error(
35
                'Use "application/jsonx+xml" instead of "application/x-jsonx", cause jsonx is a xml variant.',
36 1
                E_USER_DEPRECATED
37
            );
38
        }
39
40
        $this->contentType = $contentType;
41
    }
42
43
    public function getContentType(): string
44 3
    {
45
        return $this->contentType;
46 3
    }
47 3
48
    public function encode(array $data): string
49 3
    {
50 2
        $document = new \DOMDocument('1.0', 'UTF-8');
51
        $document->formatOutput = $this->prettyPrint;
52 1
53
        if (self::DATATYPE_OBJECT === $this->getType($data)) {
54
            $rootNode = $this->createObjectNode($document, $data);
55 2
        } else {
56 2
            $rootNode = $this->createArrayNode($document, $data);
57 2
        }
58
59 2
        $rootNode->setAttribute('xsi:schemaLocation', 'http://www.datapower.com/schemas/json jsonx.xsd');
60
        $rootNode->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
61 2
        $rootNode->setAttribute('xmlns:json', 'http://www.ibm.com/xmlns/prod/2009/jsonx');
62
63
        $document->appendChild($rootNode);
64
65
        return trim($document->saveXML($document));
66
    }
67
68
    /**
69
     * @param string $name
70
     */
71 3
    private function createObjectNode(\DOMDocument $document, array $value, string $name = null): \DOMNode
72
    {
73 3
        $node = $document->createElement('json:object');
74
75 3
        if (null !== $name) {
76 1
            $node->setAttribute('name', $name);
77
        }
78
79 3
        foreach ($value as $subName => $subValue) {
80 3
            $subValueType = $this->getType($subValue);
81 2
            if (self::DATATYPE_OBJECT === $subValueType) {
82 1
                $subNode = $this->createObjectNode($document, $subValue, (string) $subName);
83 2
            } elseif (self::DATATYPE_ARRAY === $subValueType) {
84 1
                $subNode = $this->createArrayNode($document, $subValue, (string) $subName);
85 2
            } elseif (self::DATATYPE_BOOLEAN === $subValueType) {
86 1
                $subNode = $this->createBooleanNode($document, $subValue, (string) $subName);
87 2
            } elseif (self::DATATYPE_STRING === $subValueType) {
88 2
                $subNode = $this->createStringNode($document, $subValue, (string) $subName);
89 1
            } elseif (self::DATATYPE_NUMBER === $subValueType) {
90 1
                $subNode = $this->createNumberNode($document, $subValue, (string) $subName);
91
            } else {
92 1
                $subNode = $this->createNullNode($document, (string) $subName);
93
            }
94
95 2
            $node->appendChild($subNode);
96
        }
97
98 2
        return $node;
99
    }
100
101
    /**
102
     * @param string $name
103
     */
104
    private function createArrayNode(\DOMDocument $document, array $value, string $name = null): \DOMNode
105
    {
106
        $node = $document->createElement('json:array');
107
108 2
        if (null !== $name) {
109
            $node->setAttribute('name', $name);
110 2
        }
111
112 2
        foreach ($value as $subValue) {
113 1
            $subValueType = $this->getType($subValue);
114
            if (self::DATATYPE_OBJECT === $subValueType) {
115
                $subNode = $this->createObjectNode($document, $subValue);
116 2
            } elseif (self::DATATYPE_ARRAY === $subValueType) {
117 2
                $subNode = $this->createArrayNode($document, $subValue);
118 2
            } elseif (self::DATATYPE_BOOLEAN === $subValueType) {
119 2
                $subNode = $this->createBooleanNode($document, $subValue);
120 1
            } elseif (self::DATATYPE_STRING === $subValueType) {
121 1
                $subNode = $this->createStringNode($document, $subValue);
122 1
            } elseif (self::DATATYPE_NUMBER === $subValueType) {
123 1
                $subNode = $this->createNumberNode($document, $subValue);
124 1
            } else {
125 1
                $subNode = $this->createNullNode($document);
126 1
            }
127 1
128
            $node->appendChild($subNode);
129 1
        }
130
131
        return $node;
132 2
    }
133
134
    /**
135 2
     * @param string $name
136
     */
137
    private function createBooleanNode(\DOMDocument $document, bool $value, string $name = null): \DOMNode
138
    {
139
        $node = $document->createElement('json:boolean', $value ? 'true' : 'false');
140
141
        if (null !== $name) {
142
            $node->setAttribute('name', $name);
143
        }
144
145 2
        return $node;
146
    }
147 2
148
    /**
149 2
     * @param string $name
150 1
     */
151
    private function createStringNode(\DOMDocument $document, string $value, string $name = null): \DOMNode
152
    {
153 2
        $node = $document->createElement('json:string', htmlentities($value, ENT_COMPAT | ENT_XML1, 'UTF-8'));
154
155
        if (null !== $name) {
156
            $node->setAttribute('name', $name);
157
        }
158
159
        return $node;
160
    }
161
162
    /**
163 2
     * @param int|float $value
164
     * @param string    $name
165 2
     */
166
    private function createNumberNode(\DOMDocument $document, $value, string $name = null): \DOMNode
167 2
    {
168 2
        $node = $document->createElement('json:number', (string) $value);
169
170
        if (null !== $name) {
171 2
            $node->setAttribute('name', $name);
172
        }
173
174
        return $node;
175
    }
176
177
    /**
178
     * @param string $name
179
     */
180
    private function createNullNode(\DOMDocument $document, string $name = null): \DOMNode
181 2
    {
182
        $node = $document->createElement('json:null');
183 2
184
        if (null !== $name) {
185 2
            $node->setAttribute('name', $name);
186 1
        }
187
188
        return $node;
189 2
    }
190
191
    /**
192
     * @param array|bool|string|int|float|null $value
193
     */
194
    private function getType($value): string
195
    {
196
        if (is_array($value)) {
197
            if ($value !== array_values($value)) {
198 2
                return self::DATATYPE_OBJECT;
199
            }
200 2
201
            return self::DATATYPE_ARRAY;
202 2
        }
203 1
204
        if (is_bool($value)) {
205
            return self::DATATYPE_BOOLEAN;
206 2
        }
207
208
        if (is_string($value)) {
209
            return self::DATATYPE_STRING;
210
        }
211
212
        if (is_int($value) || is_float($value)) {
213
            return self::DATATYPE_NUMBER;
214 3
        }
215
216 3
        if (null === $value) {
0 ignored issues
show
introduced by
The condition null === $value is always true.
Loading history...
217 3
            return self::DATATYPE_NULL;
218 3
        }
219
220
        throw new \InvalidArgumentException(
221 2
            sprintf(
222
                'Value needs to be of type array|bool|string|int|float|null, %s given',
223
                is_object($value) ? get_class($value) : gettype($value)
224 3
            )
225 2
        );
226
    }
227
}
228