Completed
Pull Request — master (#11)
by Eric
04:22
created

XmlSerializationVisitor::doVisitArray()   C

Complexity

Conditions 12
Paths 136

Size

Total Lines 51
Code Lines 30

Duplication

Lines 17
Ratio 33.33 %

Code Coverage

Tests 38
CRAP Score 12

Importance

Changes 2
Bugs 1 Features 0
Metric Value
dl 17
loc 51
ccs 38
cts 38
cp 1
rs 5.2371
c 2
b 1
f 0
cc 12
eloc 30
nc 136
nop 3
crap 12

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\ClassMetadataInterface;
17
use Ivory\Serializer\Mapping\PropertyMetadataInterface;
18
use Ivory\Serializer\Mapping\TypeMetadataInterface;
19
use Ivory\Serializer\Visitor\AbstractVisitor;
20
21
/**
22
 * @author GeLo <[email protected]>
23
 */
24
class XmlSerializationVisitor extends AbstractVisitor
25
{
26
    /**
27
     * @var AccessorInterface
28
     */
29
    private $accessor;
30
31
    /**
32
     * @var \DOMDocument|null
33
     */
34
    private $document;
35
36
    /**
37
     * @var \DOMElement|null
38
     */
39
    private $node;
40
41
    /**
42
     * @var \DOMElement[]
43
     */
44
    private $stack;
45
46
    /**
47
     * @var string
48
     */
49
    private $version;
50
51
    /**
52
     * @var string
53
     */
54
    private $encoding;
55
56
    /**
57
     * @var bool
58
     */
59
    private $formatOutput;
60
61
    /**
62
     * @var string
63
     */
64
    private $root;
65
66
    /**
67
     * @var string
68
     */
69
    private $entry;
70
71
    /**
72
     * @var string
73
     */
74
    private $entryAttribute;
75
76
    /**
77
     * @param AccessorInterface $accessor
78
     * @param string            $version
79
     * @param string            $encoding
80
     * @param bool              $formatOutput
81
     * @param string            $root
82
     * @param string            $entry
83
     * @param string            $entryAttribute
84
     */
85 1376
    public function __construct(
86
        AccessorInterface $accessor,
87
        $version = '1.0',
88
        $encoding = 'UTF-8',
89
        $formatOutput = true,
90
        $root = 'result',
91
        $entry = 'entry',
92
        $entryAttribute = 'key'
93
    ) {
94 1376
        $this->accessor = $accessor;
95 1376
        $this->version = $version;
96 1376
        $this->encoding = $encoding;
97 1376
        $this->formatOutput = $formatOutput;
98 1376
        $this->root = $root;
99 1376
        $this->entry = $entry;
100 1376
        $this->entryAttribute = $entryAttribute;
101 1376
    }
102
103
    /**
104
     * {@inheritdoc}
105
     */
106 204
    public function prepare($data, ContextInterface $context)
107
    {
108 204
        $this->document = null;
109 204
        $this->node = null;
110 204
        $this->stack = [];
111
112 204
        return parent::prepare($data, $context);
113
    }
114
115
    /**
116
     * {@inheritdoc}
117
     */
118 12
    public function visitBoolean($data, TypeMetadataInterface $type, ContextInterface $context)
119
    {
120 12
        return $this->visitText($data ? 'true' : 'false');
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126 64
    public function visitData($data, TypeMetadataInterface $type, ContextInterface $context)
127
    {
128 64
        return $this->visitText($data);
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134 12 View Code Duplication
    public function visitFloat($data, TypeMetadataInterface $type, ContextInterface $context)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
135
    {
136 12
        $data = (string) $data;
137
138 12
        if (strpos($data, '.') === false) {
139 12
            $data .= '.0';
140 6
        }
141
142 12
        return $this->visitText($data);
143
    }
144
145
    /**
146
     * {@inheritdoc}
147
     */
148 132
    public function visitString($data, TypeMetadataInterface $type, ContextInterface $context)
149
    {
150 132
        $document = $this->getDocument();
151 132
        $data = (string) $data;
152
153 132
        $node = strpos($data, '<') !== false || strpos($data, '>') !== false || strpos($data, '&') !== false
154 66
            ? $document->createCDATASection($data)
155 132
            : $document->createTextNode($data);
156
157 132
        return $this->visitNode($node);
158
    }
159
160
    /**
161
     * {@inheritdoc}
162
     */
163 168
    public function startVisitingObject($data, ClassMetadataInterface $class, ContextInterface $context)
164
    {
165 168
        $result = parent::startVisitingObject($data, $class, $context);
166
167 168
        if ($result && $class->hasXmlRoot()) {
168 8
            $this->getDocument($class->getXmlRoot());
169 4
        }
170
171 168
        return $result;
172
    }
173
174
    /**
175
     * {@inheritdoc}
176
     */
177 204
    public function getResult()
178
    {
179 204
        $document = $this->getDocument();
180
181 204
        if ($document->formatOutput) {
182 204
            $document->loadXML($document->saveXML());
183 102
        }
184
185 204
        return $document->saveXML();
186
    }
187
188
    /**
189
     * {@inheritdoc}
190
     */
191 168
    protected function doVisitObjectProperty(
192
        $data,
193
        $name,
194
        PropertyMetadataInterface $property,
195
        ContextInterface $context
196
    ) {
197 168
        if (!$property->isReadable()) {
198 8
            return false;
199
        }
200
201 168
        $value = $this->accessor->getValue(
202 84
            $data,
203 168
            $property->hasAccessor() ? $property->getAccessor() : $property->getName()
204 84
        );
205
206 168
        if ($value === null && $context->isNullIgnored()) {
207 8
            return false;
208
        }
209
210 164
        $node = $this->createNode($name);
211 164
        $this->enterNodeScope($node);
212 164
        $this->navigator->navigate($value, $context, $property->getType());
213 164
        $this->leaveNodeScope();
214
215 164
        if ($property->isXmlAttribute()) {
216 8
            $this->node->setAttribute($name, $node->nodeValue);
217 164
        } elseif ($property->isXmlValue()) {
218 4
            $this->visitNode($node->firstChild);
219 162
        } elseif ($property->isXmlInline()) {
220 4
            $children = $node->childNodes;
221 4
            $count = $children->length;
222
223 4
            for ($index = 0; $index < $count; ++$index) {
224 4
                $this->visitNode($children->item(0));
225 2
            }
226 2
        } else {
227 160
            $this->visitNode($node);
228
        }
229
230 164
        return true;
231
    }
232
233
    /**
234
     * {@inheritdoc}
235
     */
236 40
    protected function doVisitArray($data, TypeMetadataInterface $type, ContextInterface $context)
237
    {
238 40
        $entry = $this->entry;
239 40
        $entryAttribute = $this->entryAttribute;
240 40
        $keyAsAttribute = false;
241 40
        $keyAsNode = true;
242
243 40
        $metadataStack = $context->getMetadataStack();
244 40
        $metadataIndex = count($metadataStack) - 2;
245 40
        $metadata = isset($metadataStack[$metadataIndex]) ? $metadataStack[$metadataIndex] : null;
246
247 40 View Code Duplication
        if ($metadata instanceof PropertyMetadataInterface) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
248 28
            if ($metadata->hasXmlEntry()) {
249 4
                $entry = $metadata->getXmlEntry();
250 2
            }
251
252 28
            if ($metadata->hasXmlEntryAttribute()) {
253 4
                $entryAttribute = $metadata->getXmlEntryAttribute();
254 2
            }
255
256 28
            if ($metadata->hasXmlKeyAsAttribute()) {
257 4
                $keyAsAttribute = $metadata->useXmlKeyAsAttribute();
258 2
            }
259
260 28
            if ($metadata->hasXmlKeyAsNode()) {
261 4
                $keyAsNode = $metadata->useXmlKeyAsNode();
262 2
            }
263 14
        }
264
265 40
        $valueType = $type->getOption('value');
266 40
        $ignoreNull = $context->isNullIgnored();
267
268 40
        $this->getDocument();
269
270 40
        foreach ($data as $key => $value) {
271 24
            if ($value === null && $ignoreNull) {
272 4
                continue;
273
            }
274
275 20
            $node = $this->createNode($keyAsNode ? $key : $entry, $entry, $entryAttribute);
276
277 20
            if ($keyAsAttribute) {
278 4
                $node->setAttribute($entryAttribute, $key);
279 2
            }
280
281 20
            $this->enterNodeScope($node);
282 20
            $this->navigator->navigate($value, $context, $valueType);
283 20
            $this->leaveNodeScope();
284 20
            $this->visitNode($node);
285 20
        }
286 40
    }
287
288
    /**
289
     * {@inheritdoc}
290
     */
291 72
    private function visitText($data)
292
    {
293 72
        return $this->visitNode($this->getDocument()->createTextNode((string) $data));
294
    }
295
296
    /**
297
     * @param \DOMNode $node
298
     *
299
     * @return \DOMNode
300
     */
301 196
    private function visitNode(\DOMNode $node)
302
    {
303 196
        if ($this->node !== $node) {
304 196
            $this->node->appendChild($node);
305 98
        }
306
307 196
        return $node;
308
    }
309
310
    /**
311
     * @param \DOMElement $node
312
     */
313 168
    private function enterNodeScope(\DOMElement $node)
314
    {
315 168
        $this->stack[] = $this->node;
316 168
        $this->node = $node;
317 168
    }
318
319 168
    private function leaveNodeScope()
320
    {
321 168
        $this->node = array_pop($this->stack);
322 168
    }
323
324
    /**
325
     * @param string|null $root
326
     *
327
     * @return \DOMDocument
328
     */
329 204
    private function getDocument($root = null)
330
    {
331 204
        return $this->document !== null ? $this->document : $this->document = $this->createDocument($root);
332
    }
333
334
    /**
335
     * @param string      $name
336
     * @param string|null $entry
337
     * @param string|null $entryAttribute
338
     *
339
     * @return \DOMElement
340
     */
341 168
    private function createNode($name, $entry = null, $entryAttribute = null)
342
    {
343 168
        $document = $this->getDocument();
344
345
        try {
346 168
            $node = $document->createElement($name);
347 96
        } catch (\DOMException $e) {
348 24
            $node = $document->createElement($entry ?: $this->entry);
349 24
            $node->setAttribute($entryAttribute ?: $this->entryAttribute, $name);
350
        }
351
352 168
        return $node;
353
    }
354
355
    /**
356
     * @param string|null $root
357
     *
358
     * @return \DOMDocument
359
     */
360 204
    private function createDocument($root = null)
361
    {
362 204
        $document = new \DOMDocument($this->version, $this->encoding);
363 204
        $document->formatOutput = $this->formatOutput;
364
365 204
        $this->node = $document->createElement($root ?: $this->root);
366 204
        $document->appendChild($this->node);
367
368 204
        return $document;
369
    }
370
}
371