Passed
Push — master ( bc9d1b...b1e342 )
by Dominik
02:00
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
        if (!isset($data['_type'])) {
39
            throw new \InvalidArgumentException('Data missing _type');
40
        }
41
42 1
        $document = new \DOMDocument('1.0', 'UTF-8');
43 1
        $document->formatOutput = $this->formatOutput;
44
45 1
        $listNode = $document->createElement($this->getMetaPrefix('type', $data['_type']));
46 1
        $document->appendChild($listNode);
47
48 1
        unset($data['_type']);
49
50 1
        $this->dataToNodes($document, $listNode, $data);
51
52 1
        return trim($document->saveXML(null, $this->options));
53
    }
54
55
    /**
56
     * @param string      $name
57
     * @param string|null $value
58
     *
59
     * @return string
60
     */
61 1
    private function getMetaPrefix(string $name, string $value = null)
62
    {
63 1
        if (null === $value) {
64 1
            return 'meta-'.$name;
65
        }
66
67 1
        return 'meta-'.$name.'--'.$value;
68
    }
69
70
    /**
71
     * @param \DOMDocument $document
72
     * @param \DOMNode     $listNode
73
     * @param array        $data
74
     */
75 1
    private function dataToNodes(\DOMDocument $document, \DOMNode $listNode, array $data)
76
    {
77 1
        foreach ($data as $key => $value) {
78 1
            if (is_string($key) && '_' === $key[0]) {
79 1
                $key = $this->getMetaPrefix(substr($key, 1));
80
            }
81
82 1
            if (is_array($value)) {
83 1
                if (isset($value['_type'])) {
84 1
                    $childNode = $document->createElement($this->getMetaPrefix('type', $value['_type']));
85 1
                    unset($value['_type']);
86
                } else {
87 1
                    $childNode = $document->createElement(
88 1
                        is_int($key) ? Inflector::singularize($listNode->nodeName) : $key
89
                    );
90
                }
91
92 1
                $this->dataToNodes($document, $childNode, $value);
93
            } else {
94 1
                $childNode = $this->dataToScalarNode($document, $listNode, $key, $value);
95
            }
96
97 1
            if (is_int($key)) {
98 1
                $childNode->setAttribute('key', (string) $key);
99
            }
100
101 1
            $listNode->appendChild($childNode);
102
        }
103 1
    }
104
105
    /**
106
     * @param \DOMDocument               $document
107
     * @param \DOMNode                   $listNode
108
     * @param string|int                 $key
109
     * @param string|null|bool|float|int $value
110
     *
111
     * @return \DOMNode
112
     */
113 1
    private function dataToScalarNode(\DOMDocument $document, \DOMNode $listNode, $key, $value): \DOMNode
114
    {
115 1
        $stringKey = is_int($key) ? Inflector::singularize($listNode->nodeName) : $key;
116
117 1
        if (is_string($value)) {
118 1
            if (strpos($value, '<') !== false || strpos($value, '&') !== false) {
119 1
                $childNode = $document->createElement($stringKey);
120 1
                $childNode->appendChild($document->createCDATASection($value));
121
            } else {
122 1
                $childNode = $document->createElement($stringKey, $value);
123
            }
124
125 1
            $childNode->setAttribute('type', 'string');
126
127 1
            return $childNode;
128
        }
129
130 1
        if (null === $value) {
131 1
            return $document->createElement($stringKey);
132
        }
133
134 1
        $childNode = $document->createElement($stringKey, $this->getValueAsString($value));
135 1
        $childNode->setAttribute('type', $this->getType($value));
136
137 1
        return $childNode;
138
    }
139
140
    /**
141
     * @param bool|float|int $value
142
     *
143
     * @return string|null
144
     *
145
     * @throws \InvalidArgumentException
146
     */
147 1
    private function getValueAsString($value): string
148
    {
149 1
        if (is_bool($value)) {
150 1
            return $value ? 'true' : 'false';
151
        }
152
153 1
        if (is_float($value) || is_int($value)) {
154 1
            return (string) $value;
155
        }
156
157
        throw new \InvalidArgumentException(sprintf('Unsupported data type: %s', gettype($value)));
158
    }
159
160
    /**
161
     * @param bool|float|int|string $value
162
     *
163
     * @return null|string
164
     */
165 1
    private function getType($value): string
166
    {
167 1
        if (is_bool($value)) {
168 1
            return 'boolean';
169
        }
170
171 1
        if (is_float($value)) {
172 1
            return 'float';
173
        }
174
175 1
        if (is_int($value)) {
176 1
            return 'integer';
177
        }
178
179
        if (is_string($value)) {
180
            return 'string';
181
        }
182
183
        throw new \InvalidArgumentException(sprintf('Unsupported data type: %s', gettype($value)));
184
    }
185
}
186