Completed
Push — master ( a7a790...5c4323 )
by Eric
03:39
created

XmlSerializationVisitor::doVisitObjectProperty()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 5

Importance

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