Completed
Push — master ( 89419d...f547e3 )
by Asmir
28:00 queued 10:26
created

Serializer::convertArrayObjects()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 13
ccs 10
cts 10
cp 1
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 7
nc 4
nop 1
crap 5
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\Serializer\Construction\ObjectConstructorInterface;
22
use JMS\Serializer\Exception\RuntimeException;
23
use JMS\Serializer\Handler\HandlerRegistryInterface;
24
use JMS\Serializer\EventDispatcher\EventDispatcherInterface;
25
use JMS\Serializer\Exception\UnsupportedFormatException;
26
use JMS\Serializer\ContextFactory\SerializationContextFactoryInterface;
27
use JMS\Serializer\ContextFactory\DeserializationContextFactoryInterface;
28
use JMS\Serializer\ContextFactory\DefaultSerializationContextFactory;
29
use JMS\Serializer\ContextFactory\DefaultDeserializationContextFactory;
30
use Metadata\MetadataFactoryInterface;
31
use PhpCollection\MapInterface;
32
use JMS\Serializer\Expression\ExpressionEvaluatorInterface;
33
34
/**
35
 * Serializer Implementation.
36
 *
37
 * @author Johannes M. Schmitt <[email protected]>
38
 */
39
class Serializer implements SerializerInterface, ArrayTransformerInterface
40
{
41
    private $factory;
42
    private $handlerRegistry;
43
    private $objectConstructor;
44
    private $dispatcher;
45
    private $typeParser;
46
47
    /** @var \PhpCollection\MapInterface */
48
    private $serializationVisitors;
49
50
    /** @var \PhpCollection\MapInterface */
51
    private $deserializationVisitors;
52
53
    private $navigator;
54
55
    /**
56
     * @var SerializationContextFactoryInterface
57
     */
58
    private $serializationContextFactory;
59
60
    /**
61
     * @var DeserializationContextFactoryInterface
62
     */
63
    private $deserializationContextFactory;
64
65
    /**
66
     * Constructor.
67
     *
68
     * @param \Metadata\MetadataFactoryInterface $factory
69
     * @param Handler\HandlerRegistryInterface $handlerRegistry
70
     * @param Construction\ObjectConstructorInterface $objectConstructor
71
     * @param \PhpCollection\MapInterface $serializationVisitors of VisitorInterface
72
     * @param \PhpCollection\MapInterface $deserializationVisitors of VisitorInterface
73
     * @param EventDispatcher\EventDispatcherInterface $dispatcher
74
     * @param TypeParser $typeParser
75
     * @param ExpressionEvaluatorInterface|null $expressionEvaluator
76
     */
77 376
    public function __construct(
78
        MetadataFactoryInterface $factory,
79
        HandlerRegistryInterface $handlerRegistry,
80
        ObjectConstructorInterface $objectConstructor,
81
        MapInterface $serializationVisitors,
82
        MapInterface $deserializationVisitors,
83
        EventDispatcherInterface $dispatcher = null,
84
        TypeParser $typeParser = null,
85
        ExpressionEvaluatorInterface $expressionEvaluator = null
86
    )
87
    {
88 376
        $this->factory = $factory;
89 376
        $this->handlerRegistry = $handlerRegistry;
90 376
        $this->objectConstructor = $objectConstructor;
91 376
        $this->dispatcher = $dispatcher;
92 376
        $this->typeParser = $typeParser ?: new TypeParser();
93 376
        $this->serializationVisitors = $serializationVisitors;
94 376
        $this->deserializationVisitors = $deserializationVisitors;
95
96 376
        $this->navigator = new GraphNavigator($this->factory, $this->handlerRegistry, $this->objectConstructor, $this->dispatcher, $expressionEvaluator);
97
98 376
        $this->serializationContextFactory = new DefaultSerializationContextFactory();
99 376
        $this->deserializationContextFactory = new DefaultDeserializationContextFactory();
100 376
    }
101
102 334
    public function serialize($data, $format, SerializationContext $context = null)
103
    {
104 334
        if (null === $context) {
105 260
            $context = $this->serializationContextFactory->createSerializationContext();
106 260
        }
107
108 334
        return $this->serializationVisitors->get($format)
109
            ->map(function(VisitorInterface $visitor) use ($context, $data, $format) {
110 333
                $type = $context->getInitialType() !== null ? $this->typeParser->parse($context->getInitialType()) : null;
111
112 333
                $this->visit($visitor, $context, $visitor->prepare($data), $format, $type);
113
114 322
                return $visitor->getResult();
115 334
            })
116 321
            ->getOrThrow(new UnsupportedFormatException(sprintf('The format "%s" is not supported for serialization.', $format)))
117 320
        ;
118
    }
119
120 113
    public function deserialize($data, $type, $format, DeserializationContext $context = null)
121
    {
122 113
        if (null === $context) {
123 109
            $context = $this->deserializationContextFactory->createDeserializationContext();
124 109
        }
125
126 113
        return $this->deserializationVisitors->get($format)
127
            ->map(function(VisitorInterface $visitor) use ($context, $data, $format, $type) {
128 113
                $preparedData = $visitor->prepare($data);
129 110
                $navigatorResult = $this->visit($visitor, $context, $preparedData, $format, $this->typeParser->parse($type));
130
131 107
                return $this->handleDeserializeResult($visitor->getResult(), $navigatorResult);
132 113
            })
133 107
            ->getOrThrow(new UnsupportedFormatException(sprintf('The format "%s" is not supported for deserialization.', $format)))
134 107
        ;
135
    }
136
137
    /**
138
     * {@InheritDoc}
139
     */
140 8
    public function toArray($data, SerializationContext $context = null)
141
    {
142 8
        if (null === $context) {
143 8
            $context = $this->serializationContextFactory->createSerializationContext();
144 8
        }
145
146 8
        return $this->serializationVisitors->get('json')
147
            ->map(function(JsonSerializationVisitor $visitor) use ($context, $data) {
148 8
                $type = $context->getInitialType() !== null ? $this->typeParser->parse($context->getInitialType()) : null;
149
150 8
                $this->visit($visitor, $context, $data, 'json', $type);
151 8
                $result = $this->convertArrayObjects($visitor->getRoot());
152
153 8
                if ( ! is_array($result)) {
154 4
                    throw new RuntimeException(sprintf(
155 4
                        'The input data of type "%s" did not convert to an array, but got a result of type "%s".',
156 4
                        is_object($data) ? get_class($data) : gettype($data),
157 4
                        is_object($result) ? get_class($result) : gettype($result)
158 4
                    ));
159
                }
160
161 4
                return $result;
162 8
            })
163 4
            ->get()
164 4
        ;
165
    }
166
167
    /**
168
     * {@InheritDoc}
169
     */
170 3
    public function fromArray(array $data, $type, DeserializationContext $context = null)
171
    {
172 2
        if (null === $context) {
173 2
            $context = $this->deserializationContextFactory->createDeserializationContext();
174 2
        }
175
176 2
        return $this->deserializationVisitors->get('json')
177 2
            ->map(function(JsonDeserializationVisitor $visitor) use ($data, $type, $context) {
178 2
                $navigatorResult = $this->visit($visitor, $context, $data, 'json', $this->typeParser->parse($type));
179
180 2
                return $this->handleDeserializeResult($visitor->getResult(), $navigatorResult);
181 2
            })
182 3
            ->get()
183 2
        ;
184
    }
185
186 359
    private function visit(VisitorInterface $visitor, Context $context, $data, $format, array $type = null)
187
    {
188 359
        $context->initialize(
189 359
            $format,
190 359
            $visitor,
191 359
            $this->navigator,
192 359
            $this->factory
193 359
        );
194
195 359
        $visitor->setNavigator($this->navigator);
196
197 359
        return $this->navigator->accept($data, $type, $context);
198
    }
199
200 109
    private function handleDeserializeResult($visitorResult, $navigatorResult)
201
    {
202
        // This is a special case if the root is handled by a callback on the object itself.
203 109
        if (null === $visitorResult && null !== $navigatorResult) {
204 10
            return $navigatorResult;
205
        }
206
207 99
        return $visitorResult;
208
    }
209
210 8
    private function convertArrayObjects($data)
211
    {
212 8
        if ($data instanceof \ArrayObject || $data instanceof \stdClass) {
213 2
            $data = (array) $data;
214 2
        }
215 8
        if (is_array($data)) {
216 4
            foreach ($data as $k => $v) {
217 3
                $data[$k] = $this->convertArrayObjects($v);
218 4
            }
219 4
        }
220
221 8
        return $data;
222
    }
223
224
    /**
225
     * @return MetadataFactoryInterface
226
     */
227
    public function getMetadataFactory()
228
    {
229
        return $this->factory;
230
    }
231
232
    /**
233
     * @param SerializationContextFactoryInterface $serializationContextFactory
234
     *
235
     * @return self
236
     */
237 5
    public function setSerializationContextFactory(SerializationContextFactoryInterface $serializationContextFactory)
238
    {
239 5
        $this->serializationContextFactory = $serializationContextFactory;
240
241 5
        return $this;
242
    }
243
244
    /**
245
     * @param DeserializationContextFactoryInterface $deserializationContextFactory
246
     *
247
     * @return self
248
     */
249 3
    public function setDeserializationContextFactory(DeserializationContextFactoryInterface $deserializationContextFactory)
250
    {
251 3
        $this->deserializationContextFactory = $deserializationContextFactory;
252
253 3
        return $this;
254
    }
255
}
256