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

Serializer::fromArray()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3.0175

Importance

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