Completed
Pull Request — master (#925)
by Asmir
02:40
created

Serializer::toArray()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 27
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 6.0087

Importance

Changes 0
Metric Value
cc 6
eloc 15
nc 6
nop 3
dl 0
loc 27
ccs 15
cts 16
cp 0.9375
crap 6.0087
rs 8.439
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\Accessor\AccessorStrategyInterface;
25
use JMS\Serializer\Construction\ObjectConstructorInterface;
26
use JMS\Serializer\ContextFactory\DefaultDeserializationContextFactory;
27
use JMS\Serializer\ContextFactory\DefaultSerializationContextFactory;
28
use JMS\Serializer\ContextFactory\DeserializationContextFactoryInterface;
29
use JMS\Serializer\ContextFactory\SerializationContextFactoryInterface;
30
use JMS\Serializer\EventDispatcher\EventDispatcher;
31
use JMS\Serializer\EventDispatcher\EventDispatcherInterface;
32
use JMS\Serializer\Exception\InvalidArgumentException;
33
use JMS\Serializer\Exception\RuntimeException;
34
use JMS\Serializer\Exception\UnsupportedFormatException;
35
use JMS\Serializer\Expression\ExpressionEvaluatorInterface;
36
use JMS\Serializer\Handler\HandlerRegistryInterface;
37
use JMS\Serializer\VisitorFactory\DeserializationVisitorFactory;
38
use JMS\Serializer\VisitorFactory\SerializationVisitorFactory;
39
use Metadata\MetadataFactoryInterface;
40
41
/**
42
 * Serializer Implementation.
43
 *
44
 * @author Johannes M. Schmitt <[email protected]>
45
 */
46
final class Serializer implements SerializerInterface, ArrayTransformerInterface
47
{
48
    private $factory;
49
    private $handlerRegistry;
50
    private $objectConstructor;
51
    private $dispatcher;
52
    private $typeParser;
53
54
    private $serializationVisitors = array();
55
56
    private $deserializationVisitors = array();
57
58
    private $serializationNavigator;
0 ignored issues
show
introduced by
The private property $serializationNavigator is not used, and could be removed.
Loading history...
59
    private $deserializationNavigator;
0 ignored issues
show
introduced by
The private property $deserializationNavigator is not used, and could be removed.
Loading history...
60
61
    /**
62
     * @var SerializationContextFactoryInterface
63
     */
64
    private $serializationContextFactory;
65
66
    /**
67
     * @var DeserializationContextFactoryInterface
68
     */
69
    private $deserializationContextFactory;
70
    /**
71
     * @var AccessorStrategyInterface
72
     */
73
    private $accessorStrategy;
74
75
    private $expressionEvaluator;
76
    /**
77
     * @param \Metadata\MetadataFactoryInterface $factory
78
     * @param Handler\HandlerRegistryInterface $handlerRegistry
79
     * @param Construction\ObjectConstructorInterface $objectConstructor
80
     * @param SerializationVisitorFactory[] $serializationVisitors
81
     * @param DeserializationVisitorFactory[] $deserializationVisitors
82
     * @param AccessorStrategyInterface $accessorStrategy
83
     * @param EventDispatcherInterface|null $dispatcher
84
     * @param AbstractParser|null $typeParser
85
     * @param ExpressionEvaluatorInterface|null $expressionEvaluator
86
     * @param SerializationContextFactoryInterface|null $serializationContextFactory
87
     * @param DeserializationContextFactoryInterface|null $deserializationContextFactory
88
     */
89 314
    public function __construct(
90
        MetadataFactoryInterface $factory,
91
        HandlerRegistryInterface $handlerRegistry,
92
        ObjectConstructorInterface $objectConstructor,
93
        array $serializationVisitors,
94
        array $deserializationVisitors,
95
        AccessorStrategyInterface $accessorStrategy,
96
        EventDispatcherInterface $dispatcher = null,
97
        AbstractParser $typeParser = null,
98
        ExpressionEvaluatorInterface $expressionEvaluator = null,
99
        SerializationContextFactoryInterface $serializationContextFactory = null,
100
        DeserializationContextFactoryInterface $deserializationContextFactory = null
101
    )
102
    {
103 314
        $this->factory = $factory;
104 314
        $this->handlerRegistry = $handlerRegistry;
105 314
        $this->objectConstructor = $objectConstructor;
106 314
        $this->dispatcher = $dispatcher ?: new EventDispatcher();
107 314
        $this->typeParser = $typeParser ?: new TypeParser();
108 314
        $this->serializationVisitors = $serializationVisitors;
109 314
        $this->deserializationVisitors = $deserializationVisitors;
110 314
        $this->accessorStrategy = $accessorStrategy;
111
112 314
        $this->expressionEvaluator = $expressionEvaluator;
113
114 314
        $this->serializationContextFactory = $serializationContextFactory ?: new DefaultSerializationContextFactory();
115 314
        $this->deserializationContextFactory = $deserializationContextFactory ?: new DefaultDeserializationContextFactory();
116 314
    }
117
118
    /**
119
     * Parses a direction string to one of the direction constants.
120
     *
121
     * @param string $dirStr
122
     *
123
     * @return integer
124
     */
125
    public static function parseDirection(string $dirStr) : int
126
    {
127
        switch (strtolower($dirStr)) {
128
            case 'serialization':
129
                return GraphNavigatorInterface::DIRECTION_SERIALIZATION;
130
131
            case 'deserialization':
132
                return GraphNavigatorInterface::DIRECTION_DESERIALIZATION;
133
134
            default:
135
                throw new InvalidArgumentException(sprintf('The direction "%s" does not exist.', $dirStr));
136
        }
137
    }
138
139 272
    private function findInitialType($type, SerializationContext $context)
140
    {
141 272
        if ($type !== null) {
142
            return $this->typeParser->parse($type);
143 272
        } elseif ($context->hasAttribute('initial_type')) {
144 27
            return $this->typeParser->parse($context->getAttribute('initial_type'));
145
        }
146 245
        return null;
147
    }
148
149 272
    private function getSerializationNavigator() : GraphNavigatorInterface
150
    {
151 272
        return new SerializationGraphNavigator($this->factory, $this->handlerRegistry, $this->accessorStrategy, $this->dispatcher, $this->expressionEvaluator);
152
    }
153
154 132
    private function getDeserializationNavigator(): GraphNavigatorInterface
155
    {
156 132
        return new DeserializationGraphNavigator($this->factory, $this->handlerRegistry, $this->objectConstructor, $this->accessorStrategy, $this->dispatcher, $this->expressionEvaluator);
157
    }
158
159 265
    public function serialize($data, string $format, SerializationContext $context = null, string $type = null):string
160
    {
161 265
        if (null === $context) {
162 182
            $context = $this->serializationContextFactory->createSerializationContext();
163
        }
164
165 265
        if (!isset($this->serializationVisitors[$format])) {
166 1
            throw new UnsupportedFormatException(sprintf('The format "%s" is not supported for serialization.', $format));
167
        }
168
169 264
        $type = $this->findInitialType($type, $context);
170
171 264
        $visitor = $this->serializationVisitors[$format]->getVisitor();
172 264
        $navigator = $this->getSerializationNavigator();
173
174 264
        $result = $this->visit($navigator, $visitor, $context, $data, $format, $type);
175 244
        return $visitor->getResult($result);
176
    }
177
178 130
    public function deserialize(string $data, string $type, string $format, DeserializationContext $context = null)
179
    {
180 130
        if (null === $context) {
181 126
            $context = $this->deserializationContextFactory->createDeserializationContext();
182
        }
183
184 130
        if (!isset($this->deserializationVisitors[$format])) {
185
            throw new UnsupportedFormatException(sprintf('The format "%s" is not supported for deserialization.', $format));
186
        }
187
188 130
        $visitor = $this->deserializationVisitors[$format]->getVisitor();
189 130
        $navigator = $this->getDeserializationNavigator();
190
191 130
        $result = $this->visit($navigator, $visitor, $context, $data, $format, $this->typeParser->parse($type));
192
193 124
        return $visitor->getResult($result);
194
    }
195
196
    /**
197
     * {@InheritDoc}
198
     */
199 8
    public function toArray($data, SerializationContext $context = null, string $type = null):array
200
    {
201 8
        if (null === $context) {
202 8
            $context = $this->serializationContextFactory->createSerializationContext();
203
        }
204
205 8
        if (!isset($this->serializationVisitors['json'])) {
206
            throw new UnsupportedFormatException(sprintf('The format "%s" is not supported for fromArray.', 'json'));
207
        }
208
209 8
        $type = $this->findInitialType($type, $context);
210
211 8
        $visitor = $this->serializationVisitors['json']->getVisitor();
212 8
        $navigator = $this->getSerializationNavigator();
213
214 8
        $result = $this->visit($navigator, $visitor, $context, $data, 'json', $type);
215 8
        $result = $this->convertArrayObjects($result);
216
217 8
        if (!\is_array($result)) {
218 4
            throw new RuntimeException(sprintf(
219 4
                'The input data of type "%s" did not convert to an array, but got a result of type "%s".',
220 4
                \is_object($data) ? \get_class($data) : \gettype($data),
221 4
                \is_object($result) ? \get_class($result) : \gettype($result)
222
            ));
223
        }
224
225 4
        return $result;
226
    }
227
228
    /**
229
     * {@InheritDoc}
230
     */
231 2
    public function fromArray(array $data, string $type, DeserializationContext $context = null)
232
    {
233 2
        if (null === $context) {
234 2
            $context = $this->deserializationContextFactory->createDeserializationContext();
235
        }
236
237 2
        if (!isset($this->deserializationVisitors['json'])) {
238
            throw new UnsupportedFormatException(sprintf('The format "%s" is not supported for fromArray.', 'json'));
239
        }
240
241 2
        $visitor = $this->deserializationVisitors['json']->getVisitor();
242 2
        $navigator = $this->getDeserializationNavigator();
243
244 2
        return $this->visit($navigator, $visitor, $context, $data, 'json', $this->typeParser->parse($type), false);
245
    }
246
247 297
    private function visit(GraphNavigatorInterface $navigator, VisitorInterface $visitor, Context $context, $data, $format, array $type = null, $prepare = true)
248
    {
249 297
        $context->initialize(
250 297
            $format,
251 297
            $visitor,
252 297
            $navigator,
253 297
            $this->factory
254
        );
255
256 297
        $visitor->setNavigator($navigator);
257 297
        $navigator->initialize($visitor, $context);
258
259 297
        if ($prepare) {
260 295
            $data = $visitor->prepare($data);
261
        }
262 294
        return $navigator->accept($data, $type);
263
    }
264
265 8
    private function convertArrayObjects($data)
266
    {
267 8
        if ($data instanceof \ArrayObject || $data instanceof \stdClass) {
268 2
            $data = (array)$data;
269
        }
270 8
        if (\is_array($data)) {
271 4
            foreach ($data as $k => $v) {
272 3
                $data[$k] = $this->convertArrayObjects($v);
273
            }
274
        }
275
276 8
        return $data;
277
    }
278
279
    /**
280
     * @return MetadataFactoryInterface
281
     */
282
    public function getMetadataFactory()
283
    {
284
        return $this->factory;
285
    }
286
}
287