73ccf6891dec02658027b79f74810c2ab0987817
[yaffs-website] / serializer / Tests / Normalizer / AbstractObjectNormalizerTest.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\Serializer\Tests\Normalizer;
13
14 use Doctrine\Common\Annotations\AnnotationReader;
15 use PHPUnit\Framework\TestCase;
16 use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
17 use Symfony\Component\PropertyInfo\Type;
18 use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
19 use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
20 use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
21 use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
22 use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
23 use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
24 use Symfony\Component\Serializer\SerializerAwareInterface;
25 use Symfony\Component\Serializer\SerializerInterface;
26
27 class AbstractObjectNormalizerTest extends TestCase
28 {
29     public function testDenormalize()
30     {
31         $normalizer = new AbstractObjectNormalizerDummy();
32         $normalizedData = $normalizer->denormalize(array('foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz'), __NAMESPACE__.'\Dummy');
33
34         $this->assertSame('foo', $normalizedData->foo);
35         $this->assertNull($normalizedData->bar);
36         $this->assertSame('baz', $normalizedData->baz);
37     }
38
39     public function testInstantiateObjectDenormalizer()
40     {
41         $data = array('foo' => 'foo', 'bar' => 'bar', 'baz' => 'baz');
42         $class = __NAMESPACE__.'\Dummy';
43         $context = array();
44
45         $normalizer = new AbstractObjectNormalizerDummy();
46
47         $this->assertInstanceOf(__NAMESPACE__.'\Dummy', $normalizer->instantiateObject($data, $class, $context, new \ReflectionClass($class), array()));
48     }
49
50     /**
51      * @expectedException \Symfony\Component\Serializer\Exception\ExtraAttributesException
52      * @expectedExceptionMessage Extra attributes are not allowed ("fooFoo", "fooBar" are unknown).
53      */
54     public function testDenormalizeWithExtraAttributes()
55     {
56         $factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
57         $normalizer = new AbstractObjectNormalizerDummy($factory);
58         $normalizer->denormalize(
59             array('fooFoo' => 'foo', 'fooBar' => 'bar'),
60             __NAMESPACE__.'\Dummy',
61             'any',
62             array('allow_extra_attributes' => false)
63         );
64     }
65
66     /**
67      * @expectedException \Symfony\Component\Serializer\Exception\ExtraAttributesException
68      * @expectedExceptionMessage Extra attributes are not allowed ("fooFoo", "fooBar" are unknown).
69      */
70     public function testDenormalizeWithExtraAttributesAndNoGroupsWithMetadataFactory()
71     {
72         $normalizer = new AbstractObjectNormalizerWithMetadata();
73         $normalizer->denormalize(
74             array('fooFoo' => 'foo', 'fooBar' => 'bar', 'bar' => 'bar'),
75             Dummy::class,
76             'any',
77             array('allow_extra_attributes' => false)
78         );
79     }
80
81     public function testDenormalizeCollectionDecodedFromXmlWithOneChild()
82     {
83         $denormalizer = $this->getDenormalizerForDummyCollection();
84
85         $dummyCollection = $denormalizer->denormalize(
86             array(
87                 'children' => array(
88                     'bar' => 'first',
89                 ),
90             ),
91             DummyCollection::class,
92             'xml'
93         );
94
95         $this->assertInstanceOf(DummyCollection::class, $dummyCollection);
96         $this->assertInternalType('array', $dummyCollection->children);
97         $this->assertCount(1, $dummyCollection->children);
98         $this->assertInstanceOf(DummyChild::class, $dummyCollection->children[0]);
99     }
100
101     public function testDenormalizeCollectionDecodedFromXmlWithTwoChildren()
102     {
103         $denormalizer = $this->getDenormalizerForDummyCollection();
104
105         $dummyCollection = $denormalizer->denormalize(
106             array(
107                 'children' => array(
108                     array('bar' => 'first'),
109                     array('bar' => 'second'),
110                 ),
111             ),
112             DummyCollection::class,
113             'xml'
114         );
115
116         $this->assertInstanceOf(DummyCollection::class, $dummyCollection);
117         $this->assertInternalType('array', $dummyCollection->children);
118         $this->assertCount(2, $dummyCollection->children);
119         $this->assertInstanceOf(DummyChild::class, $dummyCollection->children[0]);
120         $this->assertInstanceOf(DummyChild::class, $dummyCollection->children[1]);
121     }
122
123     private function getDenormalizerForDummyCollection()
124     {
125         $extractor = $this->getMockBuilder(PhpDocExtractor::class)->getMock();
126         $extractor->method('getTypes')
127             ->will($this->onConsecutiveCalls(
128                 array(
129                     new Type(
130                         'array',
131                         false,
132                         null,
133                         true,
134                         new Type('int'),
135                         new Type('object', false, DummyChild::class)
136                     ),
137                 ),
138                 null
139             ));
140
141         $denormalizer = new AbstractObjectNormalizerCollectionDummy(null, null, $extractor);
142         $arrayDenormalizer = new ArrayDenormalizerDummy();
143         $serializer = new SerializerCollectionDummy(array($arrayDenormalizer, $denormalizer));
144         $arrayDenormalizer->setSerializer($serializer);
145         $denormalizer->setSerializer($serializer);
146
147         return $denormalizer;
148     }
149
150     /**
151      * Test that additional attributes throw an exception if no metadata factory is specified.
152      *
153      * @expectedException \Symfony\Component\Serializer\Exception\LogicException
154      * @expectedExceptionMessage A class metadata factory must be provided in the constructor when setting "allow_extra_attributes" to false.
155      */
156     public function testExtraAttributesException()
157     {
158         $normalizer = new ObjectNormalizer();
159
160         $normalizer->denormalize(array(), \stdClass::class, 'xml', array(
161             'allow_extra_attributes' => false,
162         ));
163     }
164 }
165
166 class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer
167 {
168     protected function extractAttributes($object, $format = null, array $context = array())
169     {
170     }
171
172     protected function getAttributeValue($object, $attribute, $format = null, array $context = array())
173     {
174     }
175
176     protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = array())
177     {
178         $object->$attribute = $value;
179     }
180
181     protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = array())
182     {
183         return \in_array($attribute, array('foo', 'baz'));
184     }
185
186     public function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes, $format = null)
187     {
188         return parent::instantiateObject($data, $class, $context, $reflectionClass, $allowedAttributes, $format);
189     }
190 }
191
192 class Dummy
193 {
194     public $foo;
195     public $bar;
196     public $baz;
197 }
198
199 class AbstractObjectNormalizerWithMetadata extends AbstractObjectNormalizer
200 {
201     public function __construct()
202     {
203         parent::__construct(new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())));
204     }
205
206     protected function extractAttributes($object, $format = null, array $context = array())
207     {
208     }
209
210     protected function getAttributeValue($object, $attribute, $format = null, array $context = array())
211     {
212     }
213
214     protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = array())
215     {
216         $object->$attribute = $value;
217     }
218 }
219
220 class DummyCollection
221 {
222     /** @var DummyChild[] */
223     public $children;
224 }
225
226 class DummyChild
227 {
228     public $bar;
229 }
230
231 class SerializerCollectionDummy implements SerializerInterface, DenormalizerInterface
232 {
233     private $normalizers;
234
235     /**
236      * @param DenormalizerInterface[] $normalizers
237      */
238     public function __construct($normalizers)
239     {
240         $this->normalizers = $normalizers;
241     }
242
243     public function serialize($data, $format, array $context = array())
244     {
245     }
246
247     public function deserialize($data, $type, $format, array $context = array())
248     {
249     }
250
251     public function denormalize($data, $type, $format = null, array $context = array())
252     {
253         foreach ($this->normalizers as $normalizer) {
254             if ($normalizer instanceof DenormalizerInterface && $normalizer->supportsDenormalization($data, $type, $format, $context)) {
255                 return $normalizer->denormalize($data, $type, $format, $context);
256             }
257         }
258     }
259
260     public function supportsDenormalization($data, $type, $format = null)
261     {
262         return true;
263     }
264 }
265
266 class AbstractObjectNormalizerCollectionDummy extends AbstractObjectNormalizer
267 {
268     protected function extractAttributes($object, $format = null, array $context = array())
269     {
270     }
271
272     protected function getAttributeValue($object, $attribute, $format = null, array $context = array())
273     {
274     }
275
276     protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = array())
277     {
278         $object->$attribute = $value;
279     }
280
281     protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = array())
282     {
283         return true;
284     }
285
286     public function instantiateObject(array &$data, $class, array &$context, \ReflectionClass $reflectionClass, $allowedAttributes, $format = null)
287     {
288         return parent::instantiateObject($data, $class, $context, $reflectionClass, $allowedAttributes, $format);
289     }
290
291     public function serialize($data, $format, array $context = array())
292     {
293     }
294
295     public function deserialize($data, $type, $format, array $context = array())
296     {
297     }
298 }
299
300 class ArrayDenormalizerDummy implements DenormalizerInterface, SerializerAwareInterface
301 {
302     /**
303      * @var SerializerInterface|DenormalizerInterface
304      */
305     private $serializer;
306
307     /**
308      * {@inheritdoc}
309      *
310      * @throws NotNormalizableValueException
311      */
312     public function denormalize($data, $class, $format = null, array $context = array())
313     {
314         $serializer = $this->serializer;
315         $class = substr($class, 0, -2);
316
317         foreach ($data as $key => $value) {
318             $data[$key] = $serializer->denormalize($value, $class, $format, $context);
319         }
320
321         return $data;
322     }
323
324     /**
325      * {@inheritdoc}
326      */
327     public function supportsDenormalization($data, $type, $format = null, array $context = array())
328     {
329         return '[]' === substr($type, -2)
330             && $this->serializer->supportsDenormalization($data, substr($type, 0, -2), $format, $context);
331     }
332
333     /**
334      * {@inheritdoc}
335      */
336     public function setSerializer(SerializerInterface $serializer)
337     {
338         $this->serializer = $serializer;
339     }
340 }