Completed
Push — master ( 48cf76...5a3c62 )
by Dominik
06:34 queued 01:19
created

XmlTransformer::getValueAsString()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 5.1158

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 5
cts 6
cp 0.8333
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 6
nc 4
nop 1
crap 5.1158
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 1
                $this->dataToNodes($document, $childNode, $value);
58
            } else {
59 1
                $childNode = $this->dataToScalarNode($document, $listNode, $key, $value);
60
            }
61
62 1
            if (is_int($key)) {
63 1
                $childNode->setAttribute('key', (string) $key);
64
            }
65
66 1
            $listNode->appendChild($childNode);
67
        }
68 1
    }
69
70 1
    private function updateKeyValueForChildRoot(&$key, &$value)
71
    {
72 1
        if (!is_int($key)) {
73 1
            return;
74
        }
75
76 1
        if (count($value) !== 1) {
77 1
            return;
78
        }
79
80 1
        $subValue = reset($value);
81
82 1
        if (!is_array($subValue)) {
83 1
            return;
84
        }
85
86 1
        $key = key($value);
87 1
        $value = $subValue;
88 1
    }
89
90
    /**
91
     * @param \DOMDocument               $document
92
     * @param \DOMNode                   $listNode
93
     * @param string|int                 $key
94
     * @param string|null|bool|float|int $value
95
     *
96
     * @return \DOMNode
97
     */
98 1
    private function dataToScalarNode(\DOMDocument $document, \DOMNode $listNode, $key, $value): \DOMNode
99
    {
100 1
        $stringKey = is_int($key) ? Inflector::singularize($listNode->nodeName) : $key;
101
102 1
        if (is_string($value)) {
103 1
            if (strpos($value, '<') !== false || strpos($value, '&') !== false) {
104 1
                $childNode = $document->createElement($stringKey);
105 1
                $childNode->appendChild($document->createCDATASection($value));
106
            } else {
107 1
                $childNode = $document->createElement($stringKey, $value);
108
            }
109
110 1
            $childNode->setAttribute('type', 'string');
111
112 1
            return $childNode;
113
        }
114
115 1
        if (null === $value) {
116 1
            return $document->createElement($stringKey);
117
        }
118
119 1
        $childNode = $document->createElement($stringKey, $this->getValueAsString($value));
120 1
        $childNode->setAttribute('type', $this->getType($value));
121
122 1
        return $childNode;
123
    }
124
125
    /**
126
     * @param bool|float|int $value
127
     *
128
     * @return string|null
129
     *
130
     * @throws \InvalidArgumentException
131
     */
132 1
    private function getValueAsString($value): string
133
    {
134 1
        if (is_bool($value)) {
135 1
            return $value ? 'true' : 'false';
136
        }
137
138 1
        if (is_float($value) || is_int($value)) {
139 1
            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 1
    private function getType($value): string
151
    {
152 1
        if (is_bool($value)) {
153 1
            return 'boolean';
154
        }
155
156 1
        if (is_float($value)) {
157 1
            return 'float';
158
        }
159
160 1
        if (is_int($value)) {
161 1
            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