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

XmlDeserializationVisitor   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 166
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 96.77%

Importance

Changes 0
Metric Value
wmc 24
lcom 1
cbo 5
dl 0
loc 166
ccs 60
cts 62
cp 0.9677
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A decode() 0 16 2
A doVisitArray() 0 13 2
B doVisitObjectProperty() 0 22 6
B visitArrayEntry() 0 31 6
B visitNode() 0 16 5
A isCollection() 0 4 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\Context\ContextInterface;
15
use Ivory\Serializer\Instantiator\InstantiatorInterface;
16
use Ivory\Serializer\Mapping\PropertyMetadataInterface;
17
use Ivory\Serializer\Mapping\TypeMetadataInterface;
18
use Ivory\Serializer\Mutator\MutatorInterface;
19
use Ivory\Serializer\Type\Type;
20
use Ivory\Serializer\Visitor\AbstractDeserializationVisitor;
21
22
/**
23
 * @author GeLo <[email protected]>
24
 */
25
class XmlDeserializationVisitor extends AbstractDeserializationVisitor
26
{
27
    /**
28
     * @var string
29
     */
30
    private $entry;
31
32
    /**
33
     * @var string
34
     */
35
    private $entryAttribute;
36
37
    /**
38
     * @param InstantiatorInterface $instantiator
39
     * @param MutatorInterface      $mutator
40
     * @param string                $entry
41
     * @param string                $entryAttribute
42
     */
43 1376
    public function __construct(
44
        InstantiatorInterface $instantiator,
45
        MutatorInterface $mutator,
46
        $entry = 'entry',
47
        $entryAttribute = 'key'
48
    ) {
49 1376
        parent::__construct($instantiator, $mutator);
50
51 1376
        $this->entry = $entry;
52 1376
        $this->entryAttribute = $entryAttribute;
53 1376
    }
54
55
    /**
56
     * {@inheritdoc}
57
     */
58 140
    protected function decode($data)
59
    {
60 140
        $internalErrors = libxml_use_internal_errors(true);
61 140
        $disableEntityLoader = libxml_disable_entity_loader(true);
62
63 140
        $document = simplexml_load_string($data);
64
65 140
        libxml_use_internal_errors($internalErrors);
66 140
        libxml_disable_entity_loader($disableEntityLoader);
67
68 140
        if ($document === false) {
69
            throw new \InvalidArgumentException(libxml_get_last_error());
70
        }
71
72 140
        return $document;
73
    }
74
75
    /**
76
     * {@inheritdoc}
77
     */
78 8
    protected function doVisitArray($data, TypeMetadataInterface $type, ContextInterface $context)
79
    {
80 8
        $this->result = [];
81
82 8
        $entry = $this->entry;
83 8
        $entryAttribute = $this->entryAttribute;
84
85 8
        foreach ($data as $key => $value) {
86 8
            $this->visitArrayEntry($key, $value, $entry, $entryAttribute, $type, $context);
87 4
        }
88
89 8
        return $this->result;
90
    }
91
92
    /**
93
     * {@inheritdoc}
94
     */
95 104
    protected function doVisitObjectProperty(
96
        $data,
97
        $name,
98
        PropertyMetadataInterface $property,
99
        ContextInterface $context
100
    ) {
101 104
        if ($property->isXmlValue()) {
102 4
            $data = [$name => (string) $data];
103 52
        } elseif ($data instanceof \SimpleXMLElement) {
104 104
            $data = iterator_to_array($property->isXmlAttribute() ? $data->attributes() : $data->children());
105 52
        }
106
107 104
        if (!isset($data[$name])) {
108 8
            return false;
109
        }
110
111 104
        if ($data[$name] instanceof \SimpleXMLElement) {
112 104
            $data[$name] = $this->visitNode($data[$name], $property->getType());
113 52
        }
114
115 104
        return parent::doVisitObjectProperty($data, $name, $property, $context);
116
    }
117
118
    /**
119
     * @param mixed                 $key
120
     * @param \SimpleXMLElement     $data
121
     * @param string                $entry
122
     * @param string                $entryAttribute
123
     * @param TypeMetadataInterface $type
124
     * @param ContextInterface      $context
125
     */
126 8
    private function visitArrayEntry(
127
        $key,
128
        \SimpleXMLElement $data,
129
        $entry,
130
        $entryAttribute,
131
        TypeMetadataInterface $type,
132
        ContextInterface $context
133
    ) {
134 8
        $result = $this->navigator->navigate(
135 8
            $this->visitNode($data, $valueType = $type->getOption('value')),
136 4
            $context,
137
            $valueType
138 4
        );
139
140 8
        if ($result === null && $context->isNullIgnored()) {
141
            return;
142
        }
143
144 8
        if ($key === $entry) {
145 8
            $attributes = $data->attributes();
146 8
            $key = isset($attributes[$entryAttribute]) ? $this->visitNode($attributes[$entryAttribute]) : null;
147 4
        }
148
149 8
        $key = $this->navigator->navigate($key, $context, $type->getOption('key'));
150
151 8
        if ($key === null) {
152 8
            $this->result[] = $result;
153 4
        } else {
154 8
            $this->result[$key] = $result;
155
        }
156 8
    }
157
158
    /**
159
     * @param \SimpleXMLElement          $data
160
     * @param TypeMetadataInterface|null $type
161
     *
162
     * @return mixed
163
     */
164 108
    private function visitNode(\SimpleXMLElement $data, TypeMetadataInterface $type = null)
165
    {
166 108
        if ($data->count()) {
167 16
            return $this->isCollection($type) ? $data : iterator_to_array($data);
168
        }
169
170 108
        $data = (string) $data;
171
172 108
        if (!empty($data)) {
173 80
            return $data;
174
        }
175
176 44
        if ($this->isCollection($type)) {
177 12
            return [];
178
        }
179 40
    }
180
181
    /**
182
     * @param TypeMetadataInterface|null $type
183
     *
184
     * @return bool
185
     */
186 44
    private function isCollection(TypeMetadataInterface $type = null)
187
    {
188 44
        return $type !== null && $type->getName() === Type::ARRAY_;
189
    }
190
}
191