Completed
Pull Request — master (#11)
by Eric
21:21 queued 18:00
created

XmlDeserializationVisitor::doVisitObjectProperty()   C

Complexity

Conditions 8
Paths 32

Size

Total Lines 37
Code Lines 22

Duplication

Lines 3
Ratio 8.11 %

Code Coverage

Tests 22
CRAP Score 8

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 3
loc 37
ccs 22
cts 22
cp 1
rs 5.3846
cc 8
eloc 22
nc 32
nop 4
crap 8
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\Inflector\InflectorInterface;
16
use Ivory\Serializer\Instantiator\InstantiatorInterface;
17
use Ivory\Serializer\Mapping\PropertyMetadataInterface;
18
use Ivory\Serializer\Mapping\TypeMetadata;
19
use Ivory\Serializer\Mapping\TypeMetadataInterface;
20
use Ivory\Serializer\Mutator\MutatorInterface;
21
use Ivory\Serializer\Type\Type;
22
use Ivory\Serializer\Visitor\AbstractDeserializationVisitor;
23
24
/**
25
 * @author GeLo <[email protected]>
26
 */
27
class XmlDeserializationVisitor extends AbstractDeserializationVisitor
28
{
29
    /**
30
     * @var InflectorInterface
31
     */
32
    private $inflector;
33
34
    /**
35
     * @var string
36
     */
37
    private $entry;
38
39
    /**
40
     * @var string
41
     */
42
    private $entryAttribute;
43
44
    /**
45
     * @param InstantiatorInterface $instantiator
46
     * @param MutatorInterface      $mutator
47
     * @param InflectorInterface    $inflector
48
     * @param string                $entry
49
     * @param string                $entryAttribute
50
     */
51 1032
    public function __construct(
52
        InstantiatorInterface $instantiator,
53
        MutatorInterface $mutator,
54
        InflectorInterface $inflector,
55
        $entry = 'entry',
56
        $entryAttribute = 'key'
57
    ) {
58 1032
        parent::__construct($instantiator, $mutator);
59
60 1032
        $this->inflector = $inflector;
61 1032
        $this->entry = $entry;
62 1032
        $this->entryAttribute = $entryAttribute;
63 1032
    }
64
65
    /**
66
     * {@inheritdoc}
67
     */
68 105
    protected function decode($data)
69
    {
70 105
        $internalErrors = libxml_use_internal_errors(true);
71 105
        $disableEntityLoader = libxml_disable_entity_loader(true);
72
73 105
        $document = simplexml_load_string($data);
74
75 105
        libxml_use_internal_errors($internalErrors);
76 105
        libxml_disable_entity_loader($disableEntityLoader);
77
78 105
        if ($document === false) {
79
            throw new \InvalidArgumentException(libxml_get_last_error());
80
        }
81
82 105
        return $document;
83
    }
84
85
    /**
86
     * {@inheritdoc}
87
     */
88 6
    protected function doVisitArray($data, TypeMetadataInterface $type, ContextInterface $context)
89
    {
90 6
        $this->result = [];
91
92 6
        if (isset($data[$this->entry])) {
93 6
            $entries = $data[$this->entry];
94 6
            $entries = is_array($entries) ? $entries : [$entries];
95
96 6
            foreach ($entries as $key => $value) {
97 6
                $this->visitArrayItem($key, $value, $type, $context);
98 4
            }
99 4
        }
100
101 6
        foreach ($data as $key => $value) {
102 6
            if ($key !== $this->entry) {
103 6
                $this->visitArrayItem($key, $value, $type, $context);
104 4
            }
105 4
        }
106
107 6
        return $this->result;
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113 78
    protected function doVisitObjectProperty(
114
        $data,
115
        $name,
116
        PropertyMetadataInterface $property,
117
        ContextInterface $context
118
    ) {
119 78
        $key = $name;
120 78
        $type = $property->getType();
121 78
        $isAttribute = $property->isXmlAttribute();
122 78
        $isValue = $property->isXmlValue();
123
124 78 View Code Duplication
        if ($type !== null && $type->getName() === Type::ARRAY_) {
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...
125 9
            $key = $this->inflector->singularize($key);
126 6
        }
127
128 78
        if ($isAttribute) {
129 6
            $data = $data->attributes();
130 4
        }
131
132 78
        $data = $this->visitNode($data, !$isValue ? new TypeMetadata(Type::ARRAY_) : null);
133
134 78
        if ($isAttribute) {
135 6
            $data = $data['@attributes'];
136 4
        }
137
138 78
        if ($isValue) {
139 3
            $data = [$key => $data];
140 2
        }
141
142 78
        if (!isset($data[$key])) {
143 6
            return false;
144
        }
145
146 78
        $data[$name] = $this->visitNode($data[$key], $type);
147
148 78
        return parent::doVisitObjectProperty($data, $name, $property, $context);
149
    }
150
151
    /**
152
     * @param mixed                 $key
153
     * @param mixed                 $value
154
     * @param TypeMetadataInterface $type
155
     * @param ContextInterface      $context
156
     */
157 6
    private function visitArrayItem($key, $value, TypeMetadataInterface $type, ContextInterface $context)
158
    {
159 6
        $result = $this->navigator->navigate(
160 6
            $this->visitNode($value, $valueType = $type->getOption('value')),
161 4
            $context,
162
            $valueType
163 4
        );
164
165 6
        if ($result === null && $context->isNullIgnored()) {
166
            return;
167
        }
168
169 6
        if ($value instanceof \SimpleXMLElement) {
170 6
            $key = $value->getName();
171
172 6
            if ($key === $this->entry) {
173 3
                $attributes = $value->attributes();
174 4
                $key = isset($attributes[$this->entryAttribute]) ? $attributes[$this->entryAttribute] : null;
175 2
            }
176 6
        } elseif ($key === $this->entry) {
177
            $key = null;
178
        }
179
180 6
        $key = $this->navigator->navigate(
181 6
            $this->visitNode($key, $keyType = $type->getOption('key')),
182 4
            $context,
183
            $keyType
184 4
        );
185
186 6
        if ($key === null) {
187
            $this->result[] = $result;
188
        } else {
189 6
            $this->result[$key] = $result;
190
        }
191 6
    }
192
193
    /**
194
     * @param mixed                      $data
195
     * @param TypeMetadataInterface|null $type
196
     *
197
     * @return mixed
198
     */
199 81
    private function visitNode($data, TypeMetadataInterface $type = null)
200
    {
201 81
        if (!$data instanceof \SimpleXMLElement) {
202 60
            return $data;
203
        }
204
205 81
        if (count($data)) {
206 81
            return (array) $data;
207
        }
208
209 36
        $data = (string) $data;
210
211 36
        if ($data !== '') {
212 9
            return $data;
213
        }
214
215 30
        if ($type !== null && $type->getName() === Type::ARRAY_) {
216 9
            return [];
217
        }
218 24
    }
219
}
220