Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 /*
|
Chris@0
|
4 * This file is part of the Symfony package.
|
Chris@0
|
5 *
|
Chris@0
|
6 * (c) Fabien Potencier <fabien@symfony.com>
|
Chris@0
|
7 *
|
Chris@0
|
8 * For the full copyright and license information, please view the LICENSE
|
Chris@0
|
9 * file that was distributed with this source code.
|
Chris@0
|
10 */
|
Chris@0
|
11
|
Chris@0
|
12 namespace Symfony\Component\Serializer;
|
Chris@0
|
13
|
Chris@0
|
14 use Symfony\Component\Serializer\Encoder\ChainDecoder;
|
Chris@0
|
15 use Symfony\Component\Serializer\Encoder\ChainEncoder;
|
Chris@17
|
16 use Symfony\Component\Serializer\Encoder\DecoderInterface;
|
Chris@0
|
17 use Symfony\Component\Serializer\Encoder\EncoderInterface;
|
Chris@17
|
18 use Symfony\Component\Serializer\Exception\LogicException;
|
Chris@14
|
19 use Symfony\Component\Serializer\Exception\NotEncodableValueException;
|
Chris@14
|
20 use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
|
Chris@0
|
21 use Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface;
|
Chris@17
|
22 use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
Chris@0
|
23 use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
|
Chris@0
|
24 use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
Chris@0
|
25
|
Chris@0
|
26 /**
|
Chris@0
|
27 * Serializer serializes and deserializes data.
|
Chris@0
|
28 *
|
Chris@0
|
29 * objects are turned into arrays by normalizers.
|
Chris@0
|
30 * arrays are turned into various output formats by encoders.
|
Chris@0
|
31 *
|
Chris@17
|
32 * $serializer->serialize($obj, 'xml')
|
Chris@17
|
33 * $serializer->decode($data, 'xml')
|
Chris@17
|
34 * $serializer->denormalize($data, 'Class', 'xml')
|
Chris@0
|
35 *
|
Chris@0
|
36 * @author Jordi Boggiano <j.boggiano@seld.be>
|
Chris@0
|
37 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
Chris@0
|
38 * @author Lukas Kahwe Smith <smith@pooteeweet.org>
|
Chris@0
|
39 * @author Kévin Dunglas <dunglas@gmail.com>
|
Chris@0
|
40 */
|
Chris@0
|
41 class Serializer implements SerializerInterface, NormalizerInterface, DenormalizerInterface, EncoderInterface, DecoderInterface
|
Chris@0
|
42 {
|
Chris@0
|
43 /**
|
Chris@0
|
44 * @var Encoder\ChainEncoder
|
Chris@0
|
45 */
|
Chris@0
|
46 protected $encoder;
|
Chris@0
|
47
|
Chris@0
|
48 /**
|
Chris@0
|
49 * @var Encoder\ChainDecoder
|
Chris@0
|
50 */
|
Chris@0
|
51 protected $decoder;
|
Chris@0
|
52
|
Chris@0
|
53 /**
|
Chris@0
|
54 * @var array
|
Chris@0
|
55 */
|
Chris@17
|
56 protected $normalizers = [];
|
Chris@0
|
57
|
Chris@0
|
58 /**
|
Chris@0
|
59 * @var array
|
Chris@0
|
60 *
|
Chris@0
|
61 * @deprecated since 3.1 will be removed in 4.0
|
Chris@0
|
62 */
|
Chris@17
|
63 protected $normalizerCache = [];
|
Chris@0
|
64
|
Chris@0
|
65 /**
|
Chris@0
|
66 * @var array
|
Chris@0
|
67 *
|
Chris@0
|
68 * @deprecated since 3.1 will be removed in 4.0
|
Chris@0
|
69 */
|
Chris@17
|
70 protected $denormalizerCache = [];
|
Chris@0
|
71
|
Chris@17
|
72 public function __construct(array $normalizers = [], array $encoders = [])
|
Chris@0
|
73 {
|
Chris@0
|
74 foreach ($normalizers as $normalizer) {
|
Chris@0
|
75 if ($normalizer instanceof SerializerAwareInterface) {
|
Chris@0
|
76 $normalizer->setSerializer($this);
|
Chris@0
|
77 }
|
Chris@0
|
78
|
Chris@0
|
79 if ($normalizer instanceof DenormalizerAwareInterface) {
|
Chris@0
|
80 $normalizer->setDenormalizer($this);
|
Chris@0
|
81 }
|
Chris@0
|
82
|
Chris@0
|
83 if ($normalizer instanceof NormalizerAwareInterface) {
|
Chris@0
|
84 $normalizer->setNormalizer($this);
|
Chris@0
|
85 }
|
Chris@0
|
86 }
|
Chris@0
|
87 $this->normalizers = $normalizers;
|
Chris@0
|
88
|
Chris@17
|
89 $decoders = [];
|
Chris@17
|
90 $realEncoders = [];
|
Chris@0
|
91 foreach ($encoders as $encoder) {
|
Chris@0
|
92 if ($encoder instanceof SerializerAwareInterface) {
|
Chris@0
|
93 $encoder->setSerializer($this);
|
Chris@0
|
94 }
|
Chris@0
|
95 if ($encoder instanceof DecoderInterface) {
|
Chris@0
|
96 $decoders[] = $encoder;
|
Chris@0
|
97 }
|
Chris@0
|
98 if ($encoder instanceof EncoderInterface) {
|
Chris@0
|
99 $realEncoders[] = $encoder;
|
Chris@0
|
100 }
|
Chris@0
|
101 }
|
Chris@0
|
102 $this->encoder = new ChainEncoder($realEncoders);
|
Chris@0
|
103 $this->decoder = new ChainDecoder($decoders);
|
Chris@0
|
104 }
|
Chris@0
|
105
|
Chris@0
|
106 /**
|
Chris@0
|
107 * {@inheritdoc}
|
Chris@0
|
108 */
|
Chris@17
|
109 final public function serialize($data, $format, array $context = [])
|
Chris@0
|
110 {
|
Chris@14
|
111 if (!$this->supportsEncoding($format, $context)) {
|
Chris@14
|
112 throw new NotEncodableValueException(sprintf('Serialization for the format %s is not supported', $format));
|
Chris@0
|
113 }
|
Chris@0
|
114
|
Chris@14
|
115 if ($this->encoder->needsNormalization($format, $context)) {
|
Chris@0
|
116 $data = $this->normalize($data, $format, $context);
|
Chris@0
|
117 }
|
Chris@0
|
118
|
Chris@0
|
119 return $this->encode($data, $format, $context);
|
Chris@0
|
120 }
|
Chris@0
|
121
|
Chris@0
|
122 /**
|
Chris@0
|
123 * {@inheritdoc}
|
Chris@0
|
124 */
|
Chris@17
|
125 final public function deserialize($data, $type, $format, array $context = [])
|
Chris@0
|
126 {
|
Chris@14
|
127 if (!$this->supportsDecoding($format, $context)) {
|
Chris@14
|
128 throw new NotEncodableValueException(sprintf('Deserialization for the format %s is not supported', $format));
|
Chris@0
|
129 }
|
Chris@0
|
130
|
Chris@0
|
131 $data = $this->decode($data, $format, $context);
|
Chris@0
|
132
|
Chris@0
|
133 return $this->denormalize($data, $type, $format, $context);
|
Chris@0
|
134 }
|
Chris@0
|
135
|
Chris@0
|
136 /**
|
Chris@0
|
137 * {@inheritdoc}
|
Chris@0
|
138 */
|
Chris@17
|
139 public function normalize($data, $format = null, array $context = [])
|
Chris@0
|
140 {
|
Chris@0
|
141 // If a normalizer supports the given data, use it
|
Chris@14
|
142 if ($normalizer = $this->getNormalizer($data, $format, $context)) {
|
Chris@0
|
143 return $normalizer->normalize($data, $format, $context);
|
Chris@0
|
144 }
|
Chris@0
|
145
|
Chris@0
|
146 if (null === $data || is_scalar($data)) {
|
Chris@0
|
147 return $data;
|
Chris@0
|
148 }
|
Chris@0
|
149
|
Chris@14
|
150 if (\is_array($data) || $data instanceof \Traversable) {
|
Chris@17
|
151 $normalized = [];
|
Chris@0
|
152 foreach ($data as $key => $val) {
|
Chris@0
|
153 $normalized[$key] = $this->normalize($val, $format, $context);
|
Chris@0
|
154 }
|
Chris@0
|
155
|
Chris@0
|
156 return $normalized;
|
Chris@0
|
157 }
|
Chris@0
|
158
|
Chris@14
|
159 if (\is_object($data)) {
|
Chris@0
|
160 if (!$this->normalizers) {
|
Chris@0
|
161 throw new LogicException('You must register at least one normalizer to be able to normalize objects.');
|
Chris@0
|
162 }
|
Chris@0
|
163
|
Chris@14
|
164 throw new NotNormalizableValueException(sprintf('Could not normalize object of type %s, no supporting normalizer found.', \get_class($data)));
|
Chris@0
|
165 }
|
Chris@0
|
166
|
Chris@14
|
167 throw new NotNormalizableValueException(sprintf('An unexpected value could not be normalized: %s', var_export($data, true)));
|
Chris@14
|
168 }
|
Chris@14
|
169
|
Chris@14
|
170 /**
|
Chris@14
|
171 * {@inheritdoc}
|
Chris@14
|
172 *
|
Chris@14
|
173 * @throws NotNormalizableValueException
|
Chris@14
|
174 */
|
Chris@17
|
175 public function denormalize($data, $type, $format = null, array $context = [])
|
Chris@14
|
176 {
|
Chris@14
|
177 if (!$this->normalizers) {
|
Chris@14
|
178 throw new LogicException('You must register at least one normalizer to be able to denormalize objects.');
|
Chris@14
|
179 }
|
Chris@14
|
180
|
Chris@14
|
181 if ($normalizer = $this->getDenormalizer($data, $type, $format, $context)) {
|
Chris@14
|
182 return $normalizer->denormalize($data, $type, $format, $context);
|
Chris@14
|
183 }
|
Chris@14
|
184
|
Chris@14
|
185 throw new NotNormalizableValueException(sprintf('Could not denormalize object of type %s, no supporting normalizer found.', $type));
|
Chris@0
|
186 }
|
Chris@0
|
187
|
Chris@0
|
188 /**
|
Chris@0
|
189 * {@inheritdoc}
|
Chris@0
|
190 */
|
Chris@17
|
191 public function supportsNormalization($data, $format = null/*, array $context = []*/)
|
Chris@0
|
192 {
|
Chris@14
|
193 if (\func_num_args() > 2) {
|
Chris@14
|
194 $context = \func_get_arg(2);
|
Chris@14
|
195 } else {
|
Chris@14
|
196 if (__CLASS__ !== \get_class($this)) {
|
Chris@14
|
197 $r = new \ReflectionMethod($this, __FUNCTION__);
|
Chris@14
|
198 if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
|
Chris@17
|
199 @trigger_error(sprintf('The "%s()" method will have a third `$context = []` argument in version 4.0. Not defining it is deprecated since Symfony 3.3.', __METHOD__), E_USER_DEPRECATED);
|
Chris@14
|
200 }
|
Chris@14
|
201 }
|
Chris@14
|
202
|
Chris@17
|
203 $context = [];
|
Chris@14
|
204 }
|
Chris@14
|
205
|
Chris@14
|
206 return null !== $this->getNormalizer($data, $format, $context);
|
Chris@0
|
207 }
|
Chris@0
|
208
|
Chris@0
|
209 /**
|
Chris@0
|
210 * {@inheritdoc}
|
Chris@0
|
211 */
|
Chris@17
|
212 public function supportsDenormalization($data, $type, $format = null/*, array $context = []*/)
|
Chris@0
|
213 {
|
Chris@14
|
214 if (\func_num_args() > 3) {
|
Chris@14
|
215 $context = \func_get_arg(3);
|
Chris@14
|
216 } else {
|
Chris@14
|
217 if (__CLASS__ !== \get_class($this)) {
|
Chris@14
|
218 $r = new \ReflectionMethod($this, __FUNCTION__);
|
Chris@14
|
219 if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
|
Chris@17
|
220 @trigger_error(sprintf('The "%s()" method will have a fourth `$context = []` argument in version 4.0. Not defining it is deprecated since Symfony 3.3.', __METHOD__), E_USER_DEPRECATED);
|
Chris@14
|
221 }
|
Chris@14
|
222 }
|
Chris@0
|
223
|
Chris@17
|
224 $context = [];
|
Chris@14
|
225 }
|
Chris@14
|
226
|
Chris@14
|
227 return null !== $this->getDenormalizer($data, $type, $format, $context);
|
Chris@0
|
228 }
|
Chris@0
|
229
|
Chris@0
|
230 /**
|
Chris@0
|
231 * Returns a matching normalizer.
|
Chris@0
|
232 *
|
Chris@14
|
233 * @param mixed $data Data to get the serializer for
|
Chris@14
|
234 * @param string $format Format name, present to give the option to normalizers to act differently based on formats
|
Chris@14
|
235 * @param array $context Options available to the normalizer
|
Chris@0
|
236 *
|
Chris@0
|
237 * @return NormalizerInterface|null
|
Chris@0
|
238 */
|
Chris@14
|
239 private function getNormalizer($data, $format, array $context)
|
Chris@0
|
240 {
|
Chris@0
|
241 foreach ($this->normalizers as $normalizer) {
|
Chris@14
|
242 if ($normalizer instanceof NormalizerInterface && $normalizer->supportsNormalization($data, $format, $context)) {
|
Chris@0
|
243 return $normalizer;
|
Chris@0
|
244 }
|
Chris@0
|
245 }
|
Chris@0
|
246 }
|
Chris@0
|
247
|
Chris@0
|
248 /**
|
Chris@0
|
249 * Returns a matching denormalizer.
|
Chris@0
|
250 *
|
Chris@14
|
251 * @param mixed $data Data to restore
|
Chris@14
|
252 * @param string $class The expected class to instantiate
|
Chris@14
|
253 * @param string $format Format name, present to give the option to normalizers to act differently based on formats
|
Chris@14
|
254 * @param array $context Options available to the denormalizer
|
Chris@0
|
255 *
|
Chris@0
|
256 * @return DenormalizerInterface|null
|
Chris@0
|
257 */
|
Chris@14
|
258 private function getDenormalizer($data, $class, $format, array $context)
|
Chris@0
|
259 {
|
Chris@0
|
260 foreach ($this->normalizers as $normalizer) {
|
Chris@14
|
261 if ($normalizer instanceof DenormalizerInterface && $normalizer->supportsDenormalization($data, $class, $format, $context)) {
|
Chris@0
|
262 return $normalizer;
|
Chris@0
|
263 }
|
Chris@0
|
264 }
|
Chris@0
|
265 }
|
Chris@0
|
266
|
Chris@0
|
267 /**
|
Chris@0
|
268 * {@inheritdoc}
|
Chris@0
|
269 */
|
Chris@17
|
270 final public function encode($data, $format, array $context = [])
|
Chris@0
|
271 {
|
Chris@0
|
272 return $this->encoder->encode($data, $format, $context);
|
Chris@0
|
273 }
|
Chris@0
|
274
|
Chris@0
|
275 /**
|
Chris@0
|
276 * {@inheritdoc}
|
Chris@0
|
277 */
|
Chris@17
|
278 final public function decode($data, $format, array $context = [])
|
Chris@0
|
279 {
|
Chris@0
|
280 return $this->decoder->decode($data, $format, $context);
|
Chris@0
|
281 }
|
Chris@0
|
282
|
Chris@0
|
283 /**
|
Chris@14
|
284 * {@inheritdoc}
|
Chris@0
|
285 */
|
Chris@17
|
286 public function supportsEncoding($format/*, array $context = []*/)
|
Chris@0
|
287 {
|
Chris@14
|
288 if (\func_num_args() > 1) {
|
Chris@14
|
289 $context = \func_get_arg(1);
|
Chris@14
|
290 } else {
|
Chris@14
|
291 if (__CLASS__ !== \get_class($this)) {
|
Chris@14
|
292 $r = new \ReflectionMethod($this, __FUNCTION__);
|
Chris@14
|
293 if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
|
Chris@17
|
294 @trigger_error(sprintf('The "%s()" method will have a second `$context = []` argument in version 4.0. Not defining it is deprecated since Symfony 3.3.', __METHOD__), E_USER_DEPRECATED);
|
Chris@14
|
295 }
|
Chris@14
|
296 }
|
Chris@14
|
297
|
Chris@17
|
298 $context = [];
|
Chris@0
|
299 }
|
Chris@0
|
300
|
Chris@14
|
301 return $this->encoder->supportsEncoding($format, $context);
|
Chris@0
|
302 }
|
Chris@0
|
303
|
Chris@0
|
304 /**
|
Chris@0
|
305 * {@inheritdoc}
|
Chris@0
|
306 */
|
Chris@17
|
307 public function supportsDecoding($format/*, array $context = []*/)
|
Chris@0
|
308 {
|
Chris@14
|
309 if (\func_num_args() > 1) {
|
Chris@14
|
310 $context = \func_get_arg(1);
|
Chris@14
|
311 } else {
|
Chris@14
|
312 if (__CLASS__ !== \get_class($this)) {
|
Chris@14
|
313 $r = new \ReflectionMethod($this, __FUNCTION__);
|
Chris@14
|
314 if (__CLASS__ !== $r->getDeclaringClass()->getName()) {
|
Chris@17
|
315 @trigger_error(sprintf('The "%s()" method will have a second `$context = []` argument in version 4.0. Not defining it is deprecated since Symfony 3.3.', __METHOD__), E_USER_DEPRECATED);
|
Chris@14
|
316 }
|
Chris@14
|
317 }
|
Chris@0
|
318
|
Chris@17
|
319 $context = [];
|
Chris@14
|
320 }
|
Chris@14
|
321
|
Chris@14
|
322 return $this->decoder->supportsDecoding($format, $context);
|
Chris@0
|
323 }
|
Chris@0
|
324 }
|