Passed
Push — master ( 622cfa...f34591 )
by Asmir
31:36 queued 28:57
created

Serializer::getSerializationNavigator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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