Passed
Pull Request — master (#1440)
by
unknown
14:04
created

Serializer::deserialize()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 4
dl 0
loc 12
ccs 6
cts 6
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace JMS\Serializer;
6
7
use JMS\Serializer\ContextFactory\DefaultDeserializationContextFactory;
8
use JMS\Serializer\ContextFactory\DefaultSerializationContextFactory;
9
use JMS\Serializer\ContextFactory\DeserializationContextFactoryInterface;
10
use JMS\Serializer\ContextFactory\SerializationContextFactoryInterface;
11
use JMS\Serializer\Exception\InvalidArgumentException;
12
use JMS\Serializer\Exception\RuntimeException;
13
use JMS\Serializer\Exception\UnsupportedFormatException;
14
use JMS\Serializer\GraphNavigator\Factory\GraphNavigatorFactoryInterface;
15
use JMS\Serializer\Type\Parser;
16
use JMS\Serializer\Type\ParserInterface;
17
use JMS\Serializer\Visitor\Factory\DeserializationVisitorFactory;
18
use JMS\Serializer\Visitor\Factory\SerializationVisitorFactory;
19
use Metadata\MetadataFactoryInterface;
20
21
/**
22
 * Serializer Implementation.
23
 *
24
 * @author Johannes M. Schmitt <[email protected]>
25
 */
26
final class Serializer implements SerializerInterface, ArrayTransformerInterface
27
{
28
    /**
29
     * @var MetadataFactoryInterface
30
     */
31
    private $factory;
32
33
    /**
34
     * @var TypeParser
0 ignored issues
show
Bug introduced by
The type JMS\Serializer\TypeParser was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
35
     */
36
    private $typeParser;
37
38
    /**
39
     * @var SerializationVisitorFactory[]
40
     */
41
    private $serializationVisitors;
42
43
    /**
44
     * @var DeserializationVisitorFactory[]
45
     */
46
    private $deserializationVisitors;
47
48
    /**
49
     * @var SerializationContextFactoryInterface
50
     */
51
    private $serializationContextFactory;
52
53
    /**
54
     * @var DeserializationContextFactoryInterface
55
     */
56
    private $deserializationContextFactory;
57
58
    /**
59
     * @var GraphNavigatorFactoryInterface[]
60
     */
61
    private $graphNavigators;
62
63
    /**
64
     * @param GraphNavigatorFactoryInterface[] $graphNavigators
65
     * @param SerializationVisitorFactory[] $serializationVisitors
66
     * @param DeserializationVisitorFactory[] $deserializationVisitors
67
     */
68
    public function __construct(
69
        MetadataFactoryInterface $factory,
70
        array $graphNavigators,
71
        array $serializationVisitors,
72 330
        array $deserializationVisitors,
73
        ?SerializationContextFactoryInterface $serializationContextFactory = null,
74
        ?DeserializationContextFactoryInterface $deserializationContextFactory = null,
75
        ?ParserInterface $typeParser = null
76
    ) {
77
        $this->factory = $factory;
78
        $this->graphNavigators = $graphNavigators;
79
        $this->serializationVisitors = $serializationVisitors;
80
        $this->deserializationVisitors = $deserializationVisitors;
81 330
82 330
        $this->typeParser = $typeParser ?? new Parser();
0 ignored issues
show
Documentation Bug introduced by
It seems like $typeParser ?? new JMS\Serializer\Type\Parser() of type JMS\Serializer\Type\Parser or JMS\Serializer\Type\ParserInterface is incompatible with the declared type JMS\Serializer\TypeParser of property $typeParser.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
83 330
84 330
        $this->serializationContextFactory = $serializationContextFactory ?: new DefaultSerializationContextFactory();
85
        $this->deserializationContextFactory = $deserializationContextFactory ?: new DefaultDeserializationContextFactory();
86 330
    }
87
88 330
    /**
89 330
     * Parses a direction string to one of the direction constants.
90 330
     */
91
    public static function parseDirection(string $dirStr): int
92
    {
93
        switch (strtolower($dirStr)) {
94
            case 'serialization':
95
                return GraphNavigatorInterface::DIRECTION_SERIALIZATION;
96
97
            case 'deserialization':
98
                return GraphNavigatorInterface::DIRECTION_DESERIALIZATION;
99
100
            default:
101
                throw new InvalidArgumentException(sprintf('The direction "%s" does not exist.', $dirStr));
102
        }
103
    }
104
105
    private function findInitialType(?string $type, SerializationContext $context): ?string
106
    {
107
        if (null !== $type) {
108
            return $type;
109
        } elseif ($context->hasAttribute('initial_type')) {
110
            return $context->getAttribute('initial_type');
111
        }
112
113 287
        return null;
114
    }
115 287
116 2
    private function getNavigator(int $direction): GraphNavigatorInterface
117 285
    {
118 27
        if (!isset($this->graphNavigators[$direction])) {
119
            throw new RuntimeException(
120 258
                sprintf(
121
                    'Can not find a graph navigator for the direction "%s".',
122
                    GraphNavigatorInterface::DIRECTION_SERIALIZATION === $direction ? 'serialization' : 'deserialization'
123 313
                )
124
            );
125 313
        }
126
127
        return $this->graphNavigators[$direction]->getGraphNavigator();
128
    }
129
130
    private function getVisitor(int $direction, string $format): VisitorInterface
131
    {
132
        $factories = GraphNavigatorInterface::DIRECTION_SERIALIZATION === $direction
133
            ? $this->serializationVisitors
134 313
            : $this->deserializationVisitors;
135
136
        if (!isset($factories[$format])) {
137 314
            throw new UnsupportedFormatException(
138
                sprintf(
139 314
                    'The format "%s" is not supported for %s.',
140 288
                    $format,
141 314
                    GraphNavigatorInterface::DIRECTION_SERIALIZATION === $direction ? 'serialization' : 'deserialization'
142
                )
143 314
            );
144 1
        }
145 1
146 1
        return $factories[$format]->getVisitor();
147 1
    }
148
149
    /**
150
     * {@InheritDoc}
151 313
     */
152
    public function serialize($data, string $format, ?SerializationContext $context = null, ?string $type = null): string
153
    {
154 280
        if (null === $context) {
155
            $context = $this->serializationContextFactory->createSerializationContext();
156 280
        }
157 197
158
        $visitor = $this->getVisitor(GraphNavigatorInterface::DIRECTION_SERIALIZATION, $format);
159
        $navigator = $this->getNavigator(GraphNavigatorInterface::DIRECTION_SERIALIZATION);
160 280
161 279
        $type = $this->findInitialType($type, $context);
162
163 279
        $result = $this->visit($navigator, $visitor, $context, $data, $format, $type);
164
165 279
        return $visitor->getResult($result);
166 259
    }
167
168
    /**
169 136
     * {@InheritDoc}
170
     */
171 136
    public function deserialize(string $data, string $type, string $format, ?DeserializationContext $context = null)
172 132
    {
173
        if (null === $context) {
174
            $context = $this->deserializationContextFactory->createDeserializationContext();
175 136
        }
176 136
177
        $visitor = $this->getVisitor(GraphNavigatorInterface::DIRECTION_DESERIALIZATION, $format);
178 136
        $navigator = $this->getNavigator(GraphNavigatorInterface::DIRECTION_DESERIALIZATION);
179
180 130
        $result = $this->visit($navigator, $visitor, $context, $data, $format, $type);
181
182
        return $visitor->getResult($result);
183
    }
184
185
    /**
186 8
     * {@InheritDoc}
187
     */
188 8
    public function toArray($data, ?SerializationContext $context = null, ?string $type = null): array
189 8
    {
190
        if (null === $context) {
191
            $context = $this->serializationContextFactory->createSerializationContext();
192 8
        }
193 8
194
        $visitor = $this->getVisitor(GraphNavigatorInterface::DIRECTION_SERIALIZATION, 'json');
195 8
        $navigator = $this->getNavigator(GraphNavigatorInterface::DIRECTION_SERIALIZATION);
196 8
197 8
        $type = $this->findInitialType($type, $context);
198
        $result = $this->visit($navigator, $visitor, $context, $data, 'json', $type);
199 8
        $result = $this->convertArrayObjects($result);
200 4
201 4
        if (!\is_array($result)) {
202 4
            throw new RuntimeException(sprintf(
203 4
                'The input data of type "%s" did not convert to an array, but got a result of type "%s".',
204
                \is_object($data) ? \get_class($data) : \gettype($data),
205
                \is_object($result) ? \get_class($result) : \gettype($result)
206
            ));
207 4
        }
208
209
        return $result;
210
    }
211
212
    /**
213 2
     * {@InheritDoc}
214
     */
215 2
    public function fromArray(array $data, string $type, ?DeserializationContext $context = null)
216 2
    {
217
        if (null === $context) {
218
            $context = $this->deserializationContextFactory->createDeserializationContext();
219 2
        }
220 2
221
        $visitor = $this->getVisitor(GraphNavigatorInterface::DIRECTION_DESERIALIZATION, 'json');
222 2
        $navigator = $this->getNavigator(GraphNavigatorInterface::DIRECTION_DESERIALIZATION);
223
224
        return $this->visit($navigator, $visitor, $context, $data, 'json', $type, false);
225 313
    }
226
227 313
    /**
228 313
     * @param mixed $data
229 313
     *
230 313
     * @return mixed
231 313
     */
232
    private function visit(GraphNavigatorInterface $navigator, VisitorInterface $visitor, Context $context, $data, string $format, ?string $type = null, bool $prepare = true)
233
    {
234 313
        $context->initialize(
235 313
            $format,
236
            $visitor,
237 313
            $navigator,
238 311
            $this->factory
239
        );
240
241 310
        $visitor->setNavigator($navigator);
242 164
        $navigator->initialize($visitor, $context);
243
244 310
        if ($prepare) {
245
            $data = $visitor->prepare($data);
246
        }
247 8
248
        if (null !== $type) {
249 8
            $type = $this->typeParser->parse($type);
250 2
        }
251
252 8
        return $navigator->accept($data, $type);
253 4
    }
254 3
255
    /**
256
     * @param mixed $data
257
     *
258 8
     * @return mixed
259
     */
260
    private function convertArrayObjects($data)
261
    {
262
        if ($data instanceof \ArrayObject || $data instanceof \stdClass) {
263
            $data = (array) $data;
264
        }
265
266
        if (\is_array($data)) {
267
            foreach ($data as $k => $v) {
268
                $data[$k] = $this->convertArrayObjects($v);
269
            }
270
        }
271
272
        return $data;
273
    }
274
275
    /**
276
     * @return MetadataFactoryInterface
277
     */
278
    public function getMetadataFactory()
279
    {
280
        return $this->factory;
281
    }
282
}
283