Passed
Branch master (bf85d9)
by Johannes
05:40
created

Serializer::findInitialType()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.0416

Importance

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