Passed
Push — master ( d4119b...f5c0f2 )
by Dominik
04:55 queued 02:45
created

JsonxTypeEncoder::getType()   B

Complexity

Conditions 9
Paths 7

Size

Total Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 9

Importance

Changes 0
Metric Value
dl 0
loc 33
ccs 17
cts 17
cp 1
rs 8.0555
c 0
b 0
f 0
cc 9
nc 7
nop 1
crap 9
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
    /**
13
     * @var bool
14
     */
15
    private $prettyPrint;
16
17
    const DATATYPE_OBJECT = 'object';
18
    const DATATYPE_ARRAY = 'array';
19
    const DATATYPE_BOOLEAN = 'boolean';
20
    const DATATYPE_STRING = 'string';
21
    const DATATYPE_NUMBER = 'number';
22
    const DATATYPE_NULL = 'null';
23
24
    /**
25
     * @param bool $prettyPrint
26
     */
27 4
    public function __construct(bool $prettyPrint = false)
28
    {
29 4
        $this->prettyPrint = $prettyPrint;
30 4
    }
31
32
    /**
33
     * @return string
34
     */
35 1
    public function getContentType(): string
36
    {
37 1
        return 'application/x-jsonx';
38
    }
39
40
    /**
41
     * @param array $data
42
     *
43
     * @return string
44
     */
45 3
    public function encode(array $data): string
46
    {
47 3
        $document = new \DOMDocument('1.0', 'UTF-8');
48 3
        $document->formatOutput = $this->prettyPrint;
49
50 3
        if (self::DATATYPE_OBJECT === $this->getType($data)) {
51 2
            $rootNode = $this->createObjectNode($document, $data);
52
        } else {
53 1
            $rootNode = $this->createArrayNode($document, $data);
54
        }
55
56 2
        $rootNode->setAttribute('xsi:schemaLocation', 'http://www.datapower.com/schemas/json jsonx.xsd');
57 2
        $rootNode->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
58 2
        $rootNode->setAttribute('xmlns:json', 'http://www.ibm.com/xmlns/prod/2009/jsonx');
59
60 2
        $document->appendChild($rootNode);
61
62 2
        return trim($document->saveXML($document));
63
    }
64
65
    /**
66
     * @param \DOMDocument $document
67
     * @param string       $name
68
     * @param array        $value
69
     *
70
     * @return \DOMNode
71
     */
72 3
    private function createObjectNode(\DOMDocument $document, array $value, string $name = null): \DOMNode
73
    {
74 3
        $node = $document->createElement('json:object');
75
76 3
        if (null !== $name) {
77 1
            $node->setAttribute('name', $name);
78
        }
79
80 3
        foreach ($value as $subName => $subValue) {
81 3
            $subValueType = $this->getType($subValue);
82 2
            if (self::DATATYPE_OBJECT === $subValueType) {
83 1
                $subNode = $this->createObjectNode($document, $subValue, (string) $subName);
84 2
            } elseif (self::DATATYPE_ARRAY === $subValueType) {
85 1
                $subNode = $this->createArrayNode($document, $subValue, (string) $subName);
86 2
            } elseif (self::DATATYPE_BOOLEAN === $subValueType) {
87 1
                $subNode = $this->createBooleanNode($document, $subValue, (string) $subName);
88 2
            } elseif (self::DATATYPE_STRING === $subValueType) {
89 2
                $subNode = $this->createStringNode($document, $subValue, (string) $subName);
90 1
            } elseif (self::DATATYPE_NUMBER === $subValueType) {
91 1
                $subNode = $this->createNumberNode($document, $subValue, (string) $subName);
92
            } else {
93 1
                $subNode = $this->createNullNode($document, (string) $subName);
94
            }
95
96 2
            $node->appendChild($subNode);
97
        }
98
99 2
        return $node;
100
    }
101
102
    /**
103
     * @param \DOMDocument $document
104
     * @param array        $value
105
     * @param string       $name
106
     *
107
     * @return \DOMNode
108
     */
109 2
    private function createArrayNode(\DOMDocument $document, array $value, string $name = null): \DOMNode
110
    {
111 2
        $node = $document->createElement('json:array');
112
113 2
        if (null !== $name) {
114 1
            $node->setAttribute('name', $name);
115
        }
116
117 2
        foreach ($value as $subValue) {
118 2
            $subValueType = $this->getType($subValue);
119 2
            if (self::DATATYPE_OBJECT === $subValueType) {
120 2
                $subNode = $this->createObjectNode($document, $subValue);
121 1
            } elseif (self::DATATYPE_ARRAY === $subValueType) {
122 1
                $subNode = $this->createArrayNode($document, $subValue);
123 1
            } elseif (self::DATATYPE_BOOLEAN === $subValueType) {
124 1
                $subNode = $this->createBooleanNode($document, $subValue);
125 1
            } elseif (self::DATATYPE_STRING === $subValueType) {
126 1
                $subNode = $this->createStringNode($document, $subValue);
127 1
            } elseif (self::DATATYPE_NUMBER === $subValueType) {
128 1
                $subNode = $this->createNumberNode($document, $subValue);
129
            } else {
130 1
                $subNode = $this->createNullNode($document);
131
            }
132
133 2
            $node->appendChild($subNode);
134
        }
135
136 2
        return $node;
137
    }
138
139
    /**
140
     * @param \DOMDocument $document
141
     * @param bool         $value
142
     * @param string       $name
143
     *
144
     * @return \DOMNode
145
     */
146 2 View Code Duplication
    private function createBooleanNode(\DOMDocument $document, bool $value, string $name = null): \DOMNode
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
147
    {
148 2
        $node = $document->createElement('json:boolean', $value ? 'true' : 'false');
149
150 2
        if (null !== $name) {
151 1
            $node->setAttribute('name', $name);
152
        }
153
154 2
        return $node;
155
    }
156
157
    /**
158
     * @param \DOMDocument $document
159
     * @param string       $value
160
     * @param string       $name
161
     *
162
     * @return \DOMNode
163
     */
164 2
    private function createStringNode(\DOMDocument $document, string $value, string $name = null): \DOMNode
165
    {
166 2
        $node = $document->createElement('json:string', htmlentities($value, ENT_COMPAT | ENT_XML1, 'UTF-8'));
167
168 2
        if (null !== $name) {
169 2
            $node->setAttribute('name', $name);
170
        }
171
172 2
        return $node;
173
    }
174
175
    /**
176
     * @param \DOMDocument $document
177
     * @param int|float    $value
178
     * @param string       $name
179
     *
180
     * @return \DOMNode
181
     */
182 2 View Code Duplication
    private function createNumberNode(\DOMDocument $document, $value, string $name = null): \DOMNode
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
183
    {
184 2
        $node = $document->createElement('json:number', (string) $value);
185
186 2
        if (null !== $name) {
187 1
            $node->setAttribute('name', $name);
188
        }
189
190 2
        return $node;
191
    }
192
193
    /**
194
     * @param \DOMDocument $document
195
     * @param string       $name
196
     *
197
     * @return \DOMNode
198
     */
199 2
    private function createNullNode(\DOMDocument $document, string $name = null): \DOMNode
200
    {
201 2
        $node = $document->createElement('json:null');
202
203 2
        if (null !== $name) {
204 1
            $node->setAttribute('name', $name);
205
        }
206
207 2
        return $node;
208
    }
209
210
    /**
211
     * @param array|bool|string|int|float|null $value
212
     *
213
     * @return string
214
     */
215 3
    private function getType($value): string
216
    {
217 3
        if (is_array($value)) {
218 3
            if ($value !== array_values($value)) {
219 3
                return self::DATATYPE_OBJECT;
220
            }
221
222 2
            return self::DATATYPE_ARRAY;
223
        }
224
225 3
        if (is_bool($value)) {
226 2
            return self::DATATYPE_BOOLEAN;
227
        }
228
229 3
        if (is_string($value)) {
230 2
            return self::DATATYPE_STRING;
231
        }
232
233 3
        if (is_int($value) || is_float($value)) {
234 2
            return self::DATATYPE_NUMBER;
235
        }
236
237 3
        if (null === $value) {
238 2
            return self::DATATYPE_NULL;
239
        }
240
241 1
        throw new \InvalidArgumentException(
242 1
            sprintf(
243 1
                'Value needs to be of type array|bool|string|int|float|null, %s given',
244 1
                is_object($value) ? get_class($value) : gettype($value)
245
            )
246
        );
247
    }
248
}
249