Completed
Push — master ( 6d901c...c17605 )
by Eric
236:56 queued 233:24
created

XmlSerializationVisitor::createDocument()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 6
nc 1
nop 0
crap 1
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 48
    public function visitData($data, TypeMetadataInterface $type, ContextInterface $context)
118
    {
119 48
        return $this->visitText($data);
120
    }
121
122
    /**
123
     * {@inheritdoc}
124
     */
125 9
    public function visitFloat($data, TypeMetadataInterface $type, ContextInterface $context)
126
    {
127 9
        $data = (string) $data;
128
129 9
        if (strpos($data, '.') === false) {
130 9
            $data .= '.0';
131 6
        }
132
133 9
        return $this->visitText($data);
134
    }
135
136
    /**
137
     * {@inheritdoc}
138
     */
139 93
    public function visitString($data, TypeMetadataInterface $type, ContextInterface $context)
140
    {
141 93
        $document = $this->getDocument();
142 93
        $data = (string) $data;
143
144 93
        $node = $this->requireCData($data)
145 62
            ? $document->createCDATASection($data)
146 93
            : $document->createTextNode($data);
147
148 93
        return $this->visitNode($node);
149
    }
150
151
    /**
152
     * {@inheritdoc}
153
     */
154 147
    public function getResult()
155
    {
156 147
        $document = $this->getDocument();
157
158 147
        if ($document->formatOutput) {
159 147
            $document->loadXML($document->saveXML());
160 98
        }
161
162 147
        return $document->saveXML();
163
    }
164
165
    /**
166
     * {@inheritdoc}
167
     */
168 120
    protected function doVisitObjectProperty(
169
        $data,
170
        $name,
171
        PropertyMetadataInterface $property,
172
        ContextInterface $context
173
    ) {
174 120
        if (!$property->isReadable()) {
175 6
            return false;
176
        }
177
178
        // FIXME - Detect errors
179 120
        $value = $this->accessor->getValue(
180 80
            $data,
181 120
            $property->hasAccessor() ? $property->getAccessor() : $property->getName()
182 80
        );
183
184 120
        if ($value === null && $context->isNullIgnored()) {
185 6
            return false;
186
        }
187
188 117
        $node = $this->createNode($name);
189 117
        $this->enterNodeScope($node);
190 117
        $this->navigator->navigate($value, $context, $property->getType());
191 117
        $this->leaveNodeScope();
192 117
        $this->visitNode($node);
193
194 117
        return true;
195
    }
196
197
    /**
198
     * {@inheritdoc}
199
     */
200 27
    protected function doVisitArray($data, TypeMetadataInterface $type, ContextInterface $context)
201
    {
202 27
        $ignoreNull = $context->isNullIgnored();
203 27
        $valueType = $type->getOption('value');
204
205 27
        $this->getDocument();
206
207 27
        foreach ($data as $key => $value) {
208 15
            if ($value === null && $ignoreNull) {
209 3
                continue;
210
            }
211
212 12
            $node = $this->createNode(is_string($key) ? $key : $this->entry);
213
214 12
            if (is_int($key)) {
215 12
                $node->setAttribute($this->entryAttribute, $key);
216 8
            }
217
218 12
            $this->enterNodeScope($node);
219 12
            $this->navigator->navigate($value, $context, $valueType);
220 12
            $this->leaveNodeScope();
221 12
            $this->visitNode($node);
222 18
        }
223 27
    }
224
225
    /**
226
     * {@inheritdoc}
227
     */
228 54
    private function visitText($data)
229
    {
230 54
        return $this->visitNode($this->getDocument()->createTextNode((string) $data));
231
    }
232
233
    /**
234
     * @param \DOMNode $node
235
     *
236
     * @return \DOMNode
237
     */
238 141
    private function visitNode(\DOMNode $node)
239
    {
240 141
        if ($this->node !== $node) {
241 141
            $this->node->appendChild($node);
242 94
        }
243
244 141
        return $node;
245
    }
246
247
    /**
248
     * @param \DOMNode $node
249
     */
250 120
    private function enterNodeScope(\DOMNode $node)
251
    {
252 120
        $this->stack[] = $this->node;
253 120
        $this->node = $node;
254 120
    }
255
256 120
    private function leaveNodeScope()
257
    {
258 120
        $this->node = array_pop($this->stack);
259 120
    }
260
261
    /**
262
     * @param string $data
263
     *
264
     * @return bool
265
     */
266 93
    private function requireCData($data)
267
    {
268 93
        return strpos($data, '<') !== false || strpos($data, '>') !== false || strpos($data, '&') !== false;
269
    }
270
271
    /**
272
     * @return \DOMDocument
273
     */
274 147
    private function getDocument()
275
    {
276 147
        return $this->document !== null ? $this->document : $this->document = $this->createDocument();
277
    }
278
279
    /**
280
     * @param string $name
281
     *
282
     * @return \DOMElement
283
     */
284 120
    private function createNode($name)
285
    {
286 120
        $document = $this->getDocument();
287
288
        try {
289 120
            $element = $document->createElement($name);
290 81
        } catch (\DOMException $e) {
291 3
            $element = $document->createElement($this->entry);
292 3
            $element->setAttribute($this->entryAttribute, $name);
293
        }
294
295 120
        return $element;
296
    }
297
298
    /**
299
     * @return \DOMDocument
300
     */
301 147
    private function createDocument()
302
    {
303 147
        $document = new \DOMDocument($this->version, $this->encoding);
304 147
        $document->formatOutput = true;
305
306 147
        $this->node = $document->createElement($this->root);
307 147
        $document->appendChild($this->node);
308
309 147
        return $document;
310
    }
311
}
312