|
1 <?php |
|
2 |
|
3 namespace Symfony\Component\Serializer; |
|
4 |
|
5 use Symfony\Component\Serializer\Normalizer\NormalizerInterface; |
|
6 use Symfony\Component\Serializer\Encoder\EncoderInterface; |
|
7 use Symfony\Component\Serializer\Encoder\DecoderInterface; |
|
8 use Symfony\Component\Serializer\Encoder\NormalizationAwareInterface; |
|
9 use Symfony\Component\Serializer\Exception\RuntimeException; |
|
10 use Symfony\Component\Serializer\Exception\LogicException; |
|
11 use Symfony\Component\Serializer\Exception\UnexpectedValueException; |
|
12 |
|
13 /* |
|
14 * This file is part of the Symfony framework. |
|
15 * |
|
16 * (c) Fabien Potencier <fabien@symfony.com> |
|
17 * |
|
18 * This source file is subject to the MIT license that is bundled |
|
19 * with this source code in the file LICENSE. |
|
20 */ |
|
21 |
|
22 /** |
|
23 * Serializer serializes and deserializes data |
|
24 * |
|
25 * objects are turned into arrays by normalizers |
|
26 * arrays are turned into various output formats by encoders |
|
27 * |
|
28 * $serializer->serialize($obj, 'xml') |
|
29 * $serializer->decode($data, 'xml') |
|
30 * $serializer->denormalize($data, 'Class', 'xml') |
|
31 * |
|
32 * @author Jordi Boggiano <j.boggiano@seld.be> |
|
33 * @author Johannes M. Schmitt <schmittjoh@gmail.com> |
|
34 * @author Lukas Kahwe Smith <smith@pooteeweet.org> |
|
35 */ |
|
36 class Serializer implements SerializerInterface |
|
37 { |
|
38 protected $normalizers = array(); |
|
39 protected $encoders = array(); |
|
40 protected $normalizerCache = array(); |
|
41 protected $denormalizerCache = array(); |
|
42 |
|
43 public function __construct(array $normalizers = array(), array $encoders = array()) |
|
44 { |
|
45 foreach ($normalizers as $normalizer) { |
|
46 if ($normalizer instanceof SerializerAwareInterface) { |
|
47 $normalizer->setSerializer($this); |
|
48 } |
|
49 } |
|
50 $this->normalizers = $normalizers; |
|
51 |
|
52 foreach ($encoders as $encoder) { |
|
53 if ($encoder instanceof SerializerAwareInterface) { |
|
54 $encoder->setSerializer($this); |
|
55 } |
|
56 } |
|
57 $this->encoders = $encoders; |
|
58 } |
|
59 |
|
60 /** |
|
61 * {@inheritdoc} |
|
62 */ |
|
63 public final function serialize($data, $format) |
|
64 { |
|
65 if (!$this->supportsSerialization($format)) { |
|
66 throw new UnexpectedValueException('Serialization for the format '.$format.' is not supported'); |
|
67 } |
|
68 |
|
69 $encoder = $this->getEncoder($format); |
|
70 |
|
71 if (!$encoder instanceof NormalizationAwareInterface) { |
|
72 $data = $this->normalize($data, $format); |
|
73 } |
|
74 |
|
75 return $this->encode($data, $format); |
|
76 } |
|
77 |
|
78 /** |
|
79 * {@inheritdoc} |
|
80 */ |
|
81 public final function deserialize($data, $type, $format) |
|
82 { |
|
83 if (!$this->supportsDeserialization($format)) { |
|
84 throw new UnexpectedValueException('Deserialization for the format '.$format.' is not supported'); |
|
85 } |
|
86 |
|
87 $data = $this->decode($data, $format); |
|
88 |
|
89 return $this->denormalize($data, $type, $format); |
|
90 } |
|
91 |
|
92 /** |
|
93 * {@inheritdoc} |
|
94 */ |
|
95 public function normalize($data, $format = null) |
|
96 { |
|
97 if (null === $data || is_scalar($data)) { |
|
98 return $data; |
|
99 } |
|
100 if ($data instanceof \Traversable) { |
|
101 $normalized = array(); |
|
102 foreach ($data as $key => $val) { |
|
103 $normalized[$key] = $this->normalize($val, $format); |
|
104 } |
|
105 |
|
106 return $normalized; |
|
107 } |
|
108 if (is_object($data)) { |
|
109 return $this->normalizeObject($data, $format); |
|
110 } |
|
111 if (is_array($data)) { |
|
112 foreach ($data as $key => $val) { |
|
113 $data[$key] = $this->normalize($val, $format); |
|
114 } |
|
115 |
|
116 return $data; |
|
117 } |
|
118 throw new UnexpectedValueException('An unexpected value could not be normalized: '.var_export($data, true)); |
|
119 } |
|
120 |
|
121 /** |
|
122 * {@inheritdoc} |
|
123 */ |
|
124 public function denormalize($data, $type, $format = null) |
|
125 { |
|
126 return $this->denormalizeObject($data, $type, $format); |
|
127 } |
|
128 |
|
129 /** |
|
130 * {@inheritdoc} |
|
131 */ |
|
132 public final function encode($data, $format) |
|
133 { |
|
134 return $this->getEncoder($format)->encode($data, $format); |
|
135 } |
|
136 |
|
137 /** |
|
138 * {@inheritdoc} |
|
139 */ |
|
140 public final function decode($data, $format) |
|
141 { |
|
142 return $this->getEncoder($format)->decode($data, $format); |
|
143 } |
|
144 |
|
145 /** |
|
146 * Normalizes an object into a set of arrays/scalars |
|
147 * |
|
148 * @param object $object object to normalize |
|
149 * @param string $format format name, present to give the option to normalizers to act differently based on formats |
|
150 * @return array|scalar |
|
151 */ |
|
152 private function normalizeObject($object, $format = null) |
|
153 { |
|
154 if (!$this->normalizers) { |
|
155 throw new LogicException('You must register at least one normalizer to be able to normalize objects.'); |
|
156 } |
|
157 $class = get_class($object); |
|
158 if (isset($this->normalizerCache[$class][$format])) { |
|
159 return $this->normalizerCache[$class][$format]->normalize($object, $format); |
|
160 } |
|
161 foreach ($this->normalizers as $normalizer) { |
|
162 if ($normalizer->supportsNormalization($object, $class, $format)) { |
|
163 $this->normalizerCache[$class][$format] = $normalizer; |
|
164 |
|
165 return $normalizer->normalize($object, $format); |
|
166 } |
|
167 } |
|
168 throw new UnexpectedValueException('Could not normalize object of type '.$class.', no supporting normalizer found.'); |
|
169 } |
|
170 |
|
171 /** |
|
172 * Denormalizes data back into an object of the given class |
|
173 * |
|
174 * @param mixed $data data to restore |
|
175 * @param string $class the expected class to instantiate |
|
176 * @param string $format format name, present to give the option to normalizers to act differently based on formats |
|
177 * @return object |
|
178 */ |
|
179 private function denormalizeObject($data, $class, $format = null) |
|
180 { |
|
181 if (!$this->normalizers) { |
|
182 throw new LogicException('You must register at least one normalizer to be able to denormalize objects.'); |
|
183 } |
|
184 if (isset($this->denormalizerCache[$class][$format])) { |
|
185 return $this->denormalizerCache[$class][$format]->denormalize($data, $class, $format); |
|
186 } |
|
187 foreach ($this->normalizers as $normalizer) { |
|
188 if ($normalizer->supportsDenormalization($data, $class, $format)) { |
|
189 $this->denormalizerCache[$class][$format] = $normalizer; |
|
190 |
|
191 return $normalizer->denormalize($data, $class, $format); |
|
192 } |
|
193 } |
|
194 throw new UnexpectedValueException('Could not denormalize object of type '.$class.', no supporting normalizer found.'); |
|
195 } |
|
196 |
|
197 /** |
|
198 * {@inheritdoc} |
|
199 */ |
|
200 public function supportsSerialization($format) |
|
201 { |
|
202 return $this->supportsEncoding($format); |
|
203 } |
|
204 |
|
205 /** |
|
206 * {@inheritdoc} |
|
207 */ |
|
208 public function supportsDeserialization($format) |
|
209 { |
|
210 return $this->supportsDecoding($format); |
|
211 } |
|
212 |
|
213 /** |
|
214 * {@inheritdoc} |
|
215 */ |
|
216 public function supportsEncoding($format) |
|
217 { |
|
218 try { |
|
219 $encoder = $this->getEncoder($format); |
|
220 } catch (\RuntimeException $e) { |
|
221 return false; |
|
222 } |
|
223 |
|
224 return $encoder instanceof EncoderInterface; |
|
225 } |
|
226 |
|
227 /** |
|
228 * {@inheritdoc} |
|
229 */ |
|
230 public function supportsDecoding($format) |
|
231 { |
|
232 try { |
|
233 $encoder = $this->getEncoder($format); |
|
234 } catch (\RuntimeException $e) { |
|
235 return false; |
|
236 } |
|
237 |
|
238 return $encoder instanceof DecoderInterface; |
|
239 } |
|
240 |
|
241 /** |
|
242 * {@inheritdoc} |
|
243 */ |
|
244 public function getEncoder($format) |
|
245 { |
|
246 if (!isset($this->encoders[$format])) { |
|
247 throw new RuntimeException(sprintf('No encoder found for format "%s".', $format)); |
|
248 } |
|
249 |
|
250 return $this->encoders[$format]; |
|
251 } |
|
252 } |