Serializer::serialize()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 8
nc 2
nop 4
dl 0
loc 16
ccs 8
cts 8
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 ParserInterface
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();
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
        $context->close();
166 259
167
        return $visitor->getResult($result);
168
    }
169 136
170
    /**
171 136
     * {@InheritDoc}
172 132
     */
173
    public function deserialize(string $data, string $type, string $format, ?DeserializationContext $context = null)
174
    {
175 136
        if (null === $context) {
176 136
            $context = $this->deserializationContextFactory->createDeserializationContext();
177
        }
178 136
179
        $visitor = $this->getVisitor(GraphNavigatorInterface::DIRECTION_DESERIALIZATION, $format);
180 130
        $navigator = $this->getNavigator(GraphNavigatorInterface::DIRECTION_DESERIALIZATION);
181
182
        $result = $this->visit($navigator, $visitor, $context, $data, $format, $type);
183
184
        $context->close();
185
186 8
        return $visitor->getResult($result);
187
    }
188 8
189 8
    /**
190
     * {@InheritDoc}
191
     */
192 8
    public function toArray($data, ?SerializationContext $context = null, ?string $type = null): array
193 8
    {
194
        if (null === $context) {
195 8
            $context = $this->serializationContextFactory->createSerializationContext();
196 8
        }
197 8
198
        $visitor = $this->getVisitor(GraphNavigatorInterface::DIRECTION_SERIALIZATION, 'json');
199 8
        $navigator = $this->getNavigator(GraphNavigatorInterface::DIRECTION_SERIALIZATION);
200 4
201 4
        $type = $this->findInitialType($type, $context);
202 4
        $result = $this->visit($navigator, $visitor, $context, $data, 'json', $type);
203 4
        $result = $this->convertArrayObjects($result);
204
205
        if (!\is_array($result)) {
206
            throw new RuntimeException(sprintf(
207 4
                'The input data of type "%s" did not convert to an array, but got a result of type "%s".',
208
                \is_object($data) ? \get_class($data) : \gettype($data),
209
                \is_object($result) ? \get_class($result) : \gettype($result),
210
            ));
211
        }
212
213 2
        return $result;
214
    }
215 2
216 2
    /**
217
     * {@InheritDoc}
218
     */
219 2
    public function fromArray(array $data, string $type, ?DeserializationContext $context = null)
220 2
    {
221
        if (null === $context) {
222 2
            $context = $this->deserializationContextFactory->createDeserializationContext();
223
        }
224
225 313
        $visitor = $this->getVisitor(GraphNavigatorInterface::DIRECTION_DESERIALIZATION, 'json');
226
        $navigator = $this->getNavigator(GraphNavigatorInterface::DIRECTION_DESERIALIZATION);
227 313
228 313
        return $this->visit($navigator, $visitor, $context, $data, 'json', $type, false);
229 313
    }
230 313
231 313
    /**
232
     * @param mixed $data
233
     *
234 313
     * @return mixed
235 313
     */
236
    private function visit(GraphNavigatorInterface $navigator, VisitorInterface $visitor, Context $context, $data, string $format, ?string $type = null, bool $prepare = true)
237 313
    {
238 311
        $context->initialize(
239
            $format,
240
            $visitor,
241 310
            $navigator,
242 164
            $this->factory,
243
        );
244 310
245
        $visitor->setNavigator($navigator);
246
        $navigator->initialize($visitor, $context);
247 8
248
        if ($prepare) {
249 8
            $data = $visitor->prepare($data);
250 2
        }
251
252 8
        if (null !== $type) {
253 4
            $type = $this->typeParser->parse($type);
254 3
        }
255
256
        return $navigator->accept($data, $type);
257
    }
258 8
259
    /**
260
     * @param mixed $data
261
     *
262
     * @return mixed
263
     */
264
    private function convertArrayObjects($data)
265
    {
266
        if ($data instanceof \ArrayObject || $data instanceof \stdClass) {
267
            $data = (array) $data;
268
        }
269
270
        if (\is_array($data)) {
271
            foreach ($data as $k => $v) {
272
                $data[$k] = $this->convertArrayObjects($v);
273
            }
274
        }
275
276
        return $data;
277
    }
278
}
279