Test Failed
Push — master ( b3e488...48cf76 )
by Dominik
02:02
created

XmlTransformer::dataToNodes()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 18
ccs 13
cts 13
cp 1
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 11
nc 5
nop 3
crap 5
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Chubbyphp\Serialization\Transformer;
6
7
use Doctrine\Common\Inflector\Inflector;
8
9
final class XmlTransformer implements TransformerInterface
10
{
11
    /**
12
     * @var bool
13
     */
14
    private $formatOutput;
15
16
    /**
17
     * @var int
18
     */
19
    private $options;
20
21
    /**
22
     * @param bool $formatOutput
23
     * @param int  $options
24
     */
25 1
    public function __construct(bool $formatOutput = false, int $options = LIBXML_NOEMPTYTAG)
26
    {
27 1
        $this->formatOutput = $formatOutput;
28 1
        $this->options = $options;
29 1
    }
30
31
    /**
32
     * @param array $data
33
     *
34
     * @return string
35
     */
36 1
    public function transform(array $data): string
37
    {
38 1
        $document = new \DOMDocument('1.0', 'UTF-8');
39 1
        $document->formatOutput = $this->formatOutput;
40
41 1
        $this->dataToNodes($document, $document, $data);
42
43 1
        return trim($document->saveXML(null, $this->options));
44
    }
45
46
    /**
47
     * @param \DOMDocument $document
48
     * @param \DOMNode     $listNode
49
     * @param array        $data
50
     */
51 1
    private function dataToNodes(\DOMDocument $document, \DOMNode $listNode, array $data)
52
    {
53 1
        foreach ($data as $key => $value) {
54 1
            if (is_array($value)) {
55 1
                $this->updateKeyValueForChildRoot($key, $value);
56 1
                $childNode = $document->createElement(is_int($key) ? Inflector::singularize($listNode->nodeName) : $key);
57
                $this->dataToNodes($document, $childNode, $value);
58 1
            } else {
59 1
                $childNode = $this->dataToScalarNode($document, $listNode, $key, $value);
60 1
            }
61 1
62
            if (is_int($key)) {
63 1
                $childNode->setAttribute('key', (string) $key);
64
            }
65 1
66 1
            $listNode->appendChild($childNode);
67 1
        }
68
    }
69 1
70 1
    private function updateKeyValueForChildRoot(&$key, &$value)
71
    {
72
        if (!is_int($key)) {
73
            return;
74 1
        }
75
76 1
        if (count($value) !== 1) {
77
            return;
78
        }
79
80
        $subValue = reset($value);
81
82
        if (!is_array($subValue)) {
83
            return;
84
        }
85 1
86
        $key = key($value);
87 1
        $value = $subValue;
88 1
    }
89
90
    /**
91 1
     * @param \DOMDocument               $document
92 1
     * @param \DOMNode                   $listNode
93
     * @param string|int                 $key
94
     * @param string|null|bool|float|int $value
95
     *
96
     * @return \DOMNode
97
     */
98
    private function dataToScalarNode(\DOMDocument $document, \DOMNode $listNode, $key, $value): \DOMNode
99
    {
100
        $stringKey = is_int($key) ? Inflector::singularize($listNode->nodeName) : $key;
101
102
        if (is_string($value)) {
103 1
            if (strpos($value, '<') !== false || strpos($value, '&') !== false) {
104
                $childNode = $document->createElement($stringKey);
105 1
                $childNode->appendChild($document->createCDATASection($value));
106 1
            } else {
107
                $childNode = $document->createElement($stringKey, $value);
108
            }
109 1
110 1
            $childNode->setAttribute('type', 'string');
111
112
            return $childNode;
113 1
        }
114 1
115
        if (null === $value) {
116
            return $document->createElement($stringKey);
117
        }
118
119
        $childNode = $document->createElement($stringKey, $this->getValueAsString($value));
120
        $childNode->setAttribute('type', $this->getType($value));
121
122
        return $childNode;
123
    }
124
125
    /**
126
     * @param bool|float|int $value
127
     *
128
     * @return string|null
129
     *
130
     * @throws \InvalidArgumentException
131
     */
132
    private function getValueAsString($value): string
133
    {
134
        if (is_bool($value)) {
135
            return $value ? 'true' : 'false';
136
        }
137
138
        if (is_float($value) || is_int($value)) {
139
            return (string) $value;
140
        }
141
142
        throw new \InvalidArgumentException(sprintf('Unsupported data type: %s', gettype($value)));
143
    }
144
145
    /**
146
     * @param bool|float|int|string $value
147
     *
148
     * @return null|string
149
     */
150
    private function getType($value): string
151
    {
152
        if (is_bool($value)) {
153
            return 'boolean';
154
        }
155
156
        if (is_float($value)) {
157
            return 'float';
158
        }
159
160
        if (is_int($value)) {
161
            return 'integer';
162
        }
163
164
        if (is_string($value)) {
165
            return 'string';
166
        }
167
168
        throw new \InvalidArgumentException(sprintf('Unsupported data type: %s', gettype($value)));
169
    }
170
}
171