Passed
Push — master ( 2cca3c...c649c5 )
by Asmir
05:31 queued 02:57
created

Serializer::serialize()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 4
dl 0
loc 13
ccs 8
cts 8
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * Copyright 2016 Johannes M. Schmitt <[email protected]>
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
namespace JMS\Serializer;
22
23
use JMS\Parser\AbstractParser;
24
use JMS\Serializer\ContextFactory\DefaultDeserializationContextFactory;
25
use JMS\Serializer\ContextFactory\DefaultSerializationContextFactory;
26
use JMS\Serializer\ContextFactory\DeserializationContextFactoryInterface;
27
use JMS\Serializer\ContextFactory\SerializationContextFactoryInterface;
28
use JMS\Serializer\Exception\InvalidArgumentException;
29
use JMS\Serializer\Exception\RuntimeException;
30
use JMS\Serializer\Exception\UnsupportedFormatException;
31
use JMS\Serializer\GraphNavigator\Factory\GraphNavigatorFactoryInterface;
32
use JMS\Serializer\Visitor\Factory\DeserializationVisitorFactory;
33
use JMS\Serializer\Visitor\Factory\SerializationVisitorFactory;
34
use Metadata\MetadataFactoryInterface;
35
36
/**
37
 * Serializer Implementation.
38
 *
39
 * @author Johannes M. Schmitt <[email protected]>
40
 */
41
final class Serializer implements SerializerInterface, ArrayTransformerInterface
42
{
43
    /**
44
     * @var MetadataFactoryInterface
45
     */
46
    private $factory;
47
48
    /**
49
     * @var TypeParser
50
     */
51
    private $typeParser;
52
53
    /**
54
     * @var array|SerializationVisitorFactory[]
55
     */
56
    private $serializationVisitors = [];
57
58
    /**
59
     * @var array|DeserializationVisitorFactory[]
60
     */
61
    private $deserializationVisitors = [];
62
63
    /**
64
     * @var SerializationContextFactoryInterface
65
     */
66
    private $serializationContextFactory;
67
68
    /**
69
     * @var DeserializationContextFactoryInterface
70
     */
71
    private $deserializationContextFactory;
72
73
    /**
74
     * @var array|GraphNavigatorFactoryInterface[]
75
     */
76
    private $graphNavigators;
77
78
    /**
79
     * @param MetadataFactoryInterface $factory
80
     * @param array|GraphNavigatorFactoryInterface[] $graphNavigators
81
     * @param SerializationVisitorFactory[] $serializationVisitors
82
     * @param DeserializationVisitorFactory[] $deserializationVisitors
83
     * @param SerializationContextFactoryInterface|null $serializationContextFactory
84
     * @param DeserializationContextFactoryInterface|null $deserializationContextFactory
85
     * @param AbstractParser|null $typeParser
86
     */
87 317
    public function __construct(
88
        MetadataFactoryInterface $factory,
89
        array $graphNavigators,
90
        array $serializationVisitors,
91
        array $deserializationVisitors,
92
        SerializationContextFactoryInterface $serializationContextFactory = null,
93
        DeserializationContextFactoryInterface $deserializationContextFactory = null,
94
        AbstractParser $typeParser = null
95
    ) {
96 317
        $this->factory = $factory;
97 317
        $this->graphNavigators = $graphNavigators;
98 317
        $this->serializationVisitors = $serializationVisitors;
99 317
        $this->deserializationVisitors = $deserializationVisitors;
100
101 317
        $this->typeParser = $typeParser ?: new TypeParser();
102
103 317
        $this->serializationContextFactory = $serializationContextFactory ?: new DefaultSerializationContextFactory();
104 317
        $this->deserializationContextFactory = $deserializationContextFactory ?: new DefaultDeserializationContextFactory();
105 317
    }
106
107
    /**
108
     * Parses a direction string to one of the direction constants.
109
     *
110
     * @param string $dirStr
111
     *
112
     * @return integer
113
     */
114
    public static function parseDirection(string $dirStr): int
115
    {
116
        switch (strtolower($dirStr)) {
117
            case 'serialization':
118
                return GraphNavigatorInterface::DIRECTION_SERIALIZATION;
119
120
            case 'deserialization':
121
                return GraphNavigatorInterface::DIRECTION_DESERIALIZATION;
122
123
            default:
124
                throw new InvalidArgumentException(sprintf('The direction "%s" does not exist.', $dirStr));
125
        }
126
    }
127
128 275
    private function findInitialType(?string $type, SerializationContext $context)
129
    {
130 275
        if ($type !== null) {
131 2
            return $type;
132 273
        } elseif ($context->hasAttribute('initial_type')) {
133 27
            return $context->getAttribute('initial_type');
134
        }
135 246
        return null;
136
    }
137
138 300
    private function getNavigator(int $direction): GraphNavigatorInterface
139
    {
140 300
        if (!isset($this->graphNavigators[$direction])) {
141
            throw new RuntimeException(
142
                sprintf(
143
                    'Can not find a graph navigator for the direction "%s".',
144
                    $direction === GraphNavigatorInterface::DIRECTION_SERIALIZATION ? 'serialization' : 'deserialization'
145
                )
146
            );
147
        }
148
149 300
        return $this->graphNavigators[$direction]->getGraphNavigator();
150
    }
151
152 301
    private function getVisitor(int $direction, string $format): VisitorInterface
153
    {
154 301
        $factories = $direction === GraphNavigatorInterface::DIRECTION_SERIALIZATION
155 276
            ? $this->serializationVisitors
156 301
            : $this->deserializationVisitors;
157
158 301
        if (!isset($factories[$format])) {
159 1
            throw new UnsupportedFormatException(
160 1
                sprintf(
161 1
                    'The format "%s" is not supported for %s.', $format,
162 1
                    $direction === GraphNavigatorInterface::DIRECTION_SERIALIZATION ? 'serialization' : 'deserialization'
163
                ));
164
        }
165
166 300
        return $factories[$format]->getVisitor();
167
    }
168
169 268
    public function serialize($data, string $format, SerializationContext $context = null, string $type = null): string
170
    {
171 268
        if (null === $context) {
172 185
            $context = $this->serializationContextFactory->createSerializationContext();
173
        }
174
175 268
        $visitor = $this->getVisitor(GraphNavigatorInterface::DIRECTION_SERIALIZATION, $format);
176 267
        $navigator = $this->getNavigator(GraphNavigatorInterface::DIRECTION_SERIALIZATION);
177
178 267
        $type = $this->findInitialType($type, $context);
179
180 267
        $result = $this->visit($navigator, $visitor, $context, $data, $format, $type);
181 247
        return $visitor->getResult($result);
182
    }
183
184 131
    public function deserialize(string $data, string $type, string $format, DeserializationContext $context = null)
185
    {
186 131
        if (null === $context) {
187 127
            $context = $this->deserializationContextFactory->createDeserializationContext();
188
        }
189
190 131
        $visitor = $this->getVisitor(GraphNavigatorInterface::DIRECTION_DESERIALIZATION, $format);
191 131
        $navigator = $this->getNavigator(GraphNavigatorInterface::DIRECTION_DESERIALIZATION);
192
193 131
        $result = $this->visit($navigator, $visitor, $context, $data, $format, $type);
194
195 125
        return $visitor->getResult($result);
196
    }
197
198
    /**
199
     * {@InheritDoc}
200
     */
201 8
    public function toArray($data, SerializationContext $context = null, string $type = null): array
202
    {
203 8
        if (null === $context) {
204 8
            $context = $this->serializationContextFactory->createSerializationContext();
205
        }
206
207 8
        $visitor = $this->getVisitor(GraphNavigatorInterface::DIRECTION_SERIALIZATION, 'json');
208 8
        $navigator = $this->getNavigator(GraphNavigatorInterface::DIRECTION_SERIALIZATION);
209
210 8
        $type = $this->findInitialType($type, $context);
211 8
        $result = $this->visit($navigator, $visitor, $context, $data, 'json', $type);
212 8
        $result = $this->convertArrayObjects($result);
213
214 8
        if (!\is_array($result)) {
215 4
            throw new RuntimeException(sprintf(
216 4
                'The input data of type "%s" did not convert to an array, but got a result of type "%s".',
217 4
                \is_object($data) ? \get_class($data) : \gettype($data),
218 4
                \is_object($result) ? \get_class($result) : \gettype($result)
219
            ));
220
        }
221
222 4
        return $result;
223
    }
224
225
    /**
226
     * {@InheritDoc}
227
     */
228 2
    public function fromArray(array $data, string $type, DeserializationContext $context = null)
229
    {
230 2
        if (null === $context) {
231 2
            $context = $this->deserializationContextFactory->createDeserializationContext();
232
        }
233
234 2
        $visitor = $this->getVisitor(GraphNavigatorInterface::DIRECTION_DESERIALIZATION, 'json');
235 2
        $navigator = $this->getNavigator(GraphNavigatorInterface::DIRECTION_DESERIALIZATION);
236
237 2
        return $this->visit($navigator, $visitor, $context, $data, 'json', $type, false);
238
    }
239
240 300
    private function visit(GraphNavigatorInterface $navigator, VisitorInterface $visitor, Context $context, $data, string $format, string $type = null, bool $prepare = true)
241
    {
242 300
        $context->initialize(
243 300
            $format,
244 300
            $visitor,
245 300
            $navigator,
246 300
            $this->factory
247
        );
248
249 300
        $visitor->setNavigator($navigator);
250 300
        $navigator->initialize($visitor, $context);
251
252 300
        if ($prepare) {
253 298
            $data = $visitor->prepare($data);
254
        }
255
256 297
        if ($type !== null) {
257 159
            $type = $this->typeParser->parse($type);
258
        }
259 297
        return $navigator->accept($data, $type);
260
    }
261
262 8
    private function convertArrayObjects($data)
263
    {
264 8
        if ($data instanceof \ArrayObject || $data instanceof \stdClass) {
265 2
            $data = (array)$data;
266
        }
267 8
        if (\is_array($data)) {
268 4
            foreach ($data as $k => $v) {
269 3
                $data[$k] = $this->convertArrayObjects($v);
270
            }
271
        }
272
273 8
        return $data;
274
    }
275
276
    /**
277
     * @return MetadataFactoryInterface
278
     */
279
    public function getMetadataFactory(): MetadataFactoryInterface
280
    {
281
        return $this->factory;
282
    }
283
}
284