Completed
Push — master ( 440022...ebc8d9 )
by Eric
61:24
created

XmlSerializationVisitor::visitFloat()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 3
crap 2
1
<?php
2
3
/*
4
 * This file is part of the Ivory Serializer package.
5
 *
6
 * (c) Eric GELOEN <[email protected]>
7
 *
8
 * For the full copyright and license information, please read the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Ivory\Serializer\Visitor\Xml;
13
14
use Ivory\Serializer\Accessor\AccessorInterface;
15
use Ivory\Serializer\Context\ContextInterface;
16
use Ivory\Serializer\Mapping\PropertyMetadataInterface;
17
use Ivory\Serializer\Mapping\TypeMetadataInterface;
18
use Ivory\Serializer\Visitor\AbstractVisitor;
19
20
/**
21
 * @author GeLo <[email protected]>
22
 */
23
class XmlSerializationVisitor extends AbstractVisitor
24
{
25
    /**
26
     * @var AccessorInterface
27
     */
28
    private $accessor;
29
30
    /**
31
     * @var \DOMDocument|null
32
     */
33
    private $document;
34
35
    /**
36
     * @var \DOMNode|null
37
     */
38
    private $node;
39
40
    /**
41
     * @var \DOMNode[]
42
     */
43
    private $stack;
44
45
    /**
46
     * @var string
47
     */
48
    private $version;
49
50
    /**
51
     * @var string
52
     */
53
    private $encoding;
54
55
    /**
56
     * @var string
57
     */
58
    private $root;
59
60
    /**
61
     * @var string
62
     */
63
    private $entry;
64
65
    /**
66
     * @var string
67
     */
68
    private $entryAttribute;
69
70
    /**
71
     * @param AccessorInterface $accessor
72
     * @param string            $version
73
     * @param string            $encoding
74
     * @param string            $root
75
     * @param string            $entry
76
     * @param string            $entryAttribute
77
     */
78 738
    public function __construct(
79
        AccessorInterface $accessor,
80
        $version = '1.0',
81
        $encoding = 'UTF-8',
82
        $root = 'result',
83
        $entry = 'entry',
84
        $entryAttribute = 'key'
85
    ) {
86 738
        $this->accessor = $accessor;
87 738
        $this->version = $version;
88 738
        $this->encoding = $encoding;
89 738
        $this->root = $root;
90 738
        $this->entry = $entry;
91 738
        $this->entryAttribute = $entryAttribute;
92 738
    }
93
94
    /**
95
     * {@inheritdoc}
96
     */
97 147
    public function prepare($data, ContextInterface $context)
98
    {
99 147
        $this->document = null;
100 147
        $this->node = null;
101 147
        $this->stack = [];
102
103 147
        return parent::prepare($data, $context);
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109 9
    public function visitBoolean($data, TypeMetadataInterface $type, ContextInterface $context)
110
    {
111 9
        return $this->visitText($data ? 'true' : 'false');
112
    }
113
114
    /**
115
     * {@inheritdoc}
116
     */
117 51
    public function visitData($data, TypeMetadataInterface $type, ContextInterface $context)
118
    {
119 51
        return $this->visitText($data);
120
    }
121
122
    /**
123
     * {@inheritdoc}
124
     */
125 93
    public function visitFloat($data, TypeMetadataInterface $type, ContextInterface $context)
126
    {
127 93
        if (strpos($data = (string) $data, '.') === false) {
128 93
            $data .= '.0';
129
        }
130 93
131 62
        return $this->visitText($data);
132 93
    }
133
134 93
    /**
135
     * {@inheritdoc}
136
     */
137
    public function visitString($data, TypeMetadataInterface $type, ContextInterface $context)
138
    {
139
        $document = $this->getDocument();
140 147
        $data = (string) $data;
141
142 147
        $node = $this->requireCData($data)
143
            ? $document->createCDATASection($data)
144 147
            : $document->createTextNode($data);
145 147
146 98
        return $this->visitNode($node);
147
    }
148 147
149
    /**
150
     * {@inheritdoc}
151
     */
152
    public function getResult()
153
    {
154 120
        $document = $this->getDocument();
155
156
        if ($document->formatOutput) {
157
            $document->loadXML($document->saveXML());
158
        }
159
160 120
        return $document->saveXML();
161 6
    }
162
163
    /**
164
     * {@inheritdoc}
165 120
     */
166 80
    protected function doVisitObjectProperty(
167 120
        $data,
168 80
        $name,
169
        PropertyMetadataInterface $property,
170 120
        ContextInterface $context
171 6
    ) {
172
        if (!$property->isReadable()) {
173
            return false;
174 117
        }
175 117
176 117
        // FIXME - Detect errors
177 117
        $value = $this->accessor->getValue(
178 117
            $data,
179
            $property->hasAccessor() ? $property->getAccessor() : $property->getName()
180 117
        );
181
182
        if ($value === null && $context->isNullIgnored()) {
183
            return false;
184
        }
185
186 27
        $node = $this->createNode($name);
187
        $this->enterNodeScope($node);
188 27
        $this->navigator->navigate($value, $context, $property->getType());
189 27
        $this->leaveNodeScope();
190
        $this->visitNode($node);
191 27
192
        return true;
193 27
    }
194 15
195 3
    /**
196
     * {@inheritdoc}
197
     */
198 12
    protected function doVisitArray($data, TypeMetadataInterface $type, ContextInterface $context)
199
    {
200 12
        $ignoreNull = $context->isNullIgnored();
201 12
        $valueType = $type->getOption('value');
202 8
203
        $this->getDocument();
204 12
205 12
        foreach ($data as $key => $value) {
206 12
            if ($value === null && $ignoreNull) {
207 12
                continue;
208 18
            }
209 27
210
            $node = $this->createNode(is_string($key) ? $key : $this->entry);
211
212
            if (is_int($key)) {
213
                $node->setAttribute($this->entryAttribute, $key);
214 54
            }
215
216 54
            $this->enterNodeScope($node);
217
            $this->navigator->navigate($value, $context, $valueType);
218
            $this->leaveNodeScope();
219
            $this->visitNode($node);
220
        }
221
    }
222
223
    /**
224 141
     * {@inheritdoc}
225
     */
226 141
    private function visitText($data)
227 141
    {
228 94
        return $this->visitNode($this->getDocument()->createTextNode((string) $data));
229
    }
230 141
231
    /**
232
     * @param \DOMNode $node
233
     *
234
     * @return \DOMNode
235
     */
236 120
    private function visitNode(\DOMNode $node)
237
    {
238 120
        if ($this->node !== $node) {
239 120
            $this->node->appendChild($node);
240 120
        }
241
242 120
        return $node;
243
    }
244 120
245 120
    /**
246
     * @param \DOMNode $node
247
     */
248
    private function enterNodeScope(\DOMNode $node)
249
    {
250
        $this->stack[] = $this->node;
251
        $this->node = $node;
252 93
    }
253
254 93
    private function leaveNodeScope()
255
    {
256
        $this->node = array_pop($this->stack);
257
    }
258
259
    /**
260 147
     * @param string $data
261
     *
262 147
     * @return bool
263
     */
264
    private function requireCData($data)
265
    {
266
        return strpos($data, '<') !== false || strpos($data, '>') !== false || strpos($data, '&') !== false;
267
    }
268
269
    /**
270 120
     * @return \DOMDocument
271
     */
272 120
    private function getDocument()
273
    {
274
        return $this->document !== null ? $this->document : $this->document = $this->createDocument();
275 120
    }
276 81
277 3
    /**
278 3
     * @param string $name
279
     *
280
     * @return \DOMElement
281 120
     */
282
    private function createNode($name)
283
    {
284
        $document = $this->getDocument();
285
286
        try {
287 147
            $element = $document->createElement($name);
288
        } catch (\DOMException $e) {
289 147
            $element = $document->createElement($this->entry);
290 147
            $element->setAttribute($this->entryAttribute, $name);
291
        }
292 147
293 147
        return $element;
294
    }
295 147
296
    /**
297
     * @return \DOMDocument
298
     */
299
    private function createDocument()
300
    {
301
        $document = new \DOMDocument($this->version, $this->encoding);
302
        $document->formatOutput = true;
303
304
        $this->node = $document->createElement($this->root);
305
        $document->appendChild($this->node);
306
307
        return $document;
308
    }
309
}
310