4cd20ab318f42d1e092acf737309b37836de6a11
[yaffs-website] / serializer / Tests / Normalizer / PropertyNormalizerTest.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\Serializer\Mapping\Factory\ClassMetadataFactory;
17 use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
18 use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
19 use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
20 use Symfony\Component\Serializer\Serializer;
21 use Symfony\Component\Serializer\SerializerInterface;
22 use Symfony\Component\Serializer\Tests\Fixtures\GroupDummy;
23 use Symfony\Component\Serializer\Tests\Fixtures\GroupDummyChild;
24 use Symfony\Component\Serializer\Tests\Fixtures\MaxDepthDummy;
25 use Symfony\Component\Serializer\Tests\Fixtures\PropertyCircularReferenceDummy;
26 use Symfony\Component\Serializer\Tests\Fixtures\PropertySiblingHolder;
27
28 class PropertyNormalizerTest extends TestCase
29 {
30     /**
31      * @var PropertyNormalizer
32      */
33     private $normalizer;
34     /**
35      * @var SerializerInterface
36      */
37     private $serializer;
38
39     protected function setUp()
40     {
41         $this->serializer = $this->getMockBuilder('Symfony\Component\Serializer\SerializerInterface')->getMock();
42         $this->normalizer = new PropertyNormalizer();
43         $this->normalizer->setSerializer($this->serializer);
44     }
45
46     public function testNormalize()
47     {
48         $obj = new PropertyDummy();
49         $obj->foo = 'foo';
50         $obj->setBar('bar');
51         $obj->setCamelCase('camelcase');
52         $this->assertEquals(
53             array('foo' => 'foo', 'bar' => 'bar', 'camelCase' => 'camelcase'),
54             $this->normalizer->normalize($obj, 'any')
55         );
56     }
57
58     public function testDenormalize()
59     {
60         $obj = $this->normalizer->denormalize(
61             array('foo' => 'foo', 'bar' => 'bar'),
62             __NAMESPACE__.'\PropertyDummy',
63             'any'
64         );
65         $this->assertEquals('foo', $obj->foo);
66         $this->assertEquals('bar', $obj->getBar());
67     }
68
69     public function testNormalizeWithParentClass()
70     {
71         $group = new GroupDummyChild();
72         $group->setBaz('baz');
73         $group->setFoo('foo');
74         $group->setBar('bar');
75         $group->setKevin('Kevin');
76         $group->setCoopTilleuls('coop');
77         $this->assertEquals(
78             array('foo' => 'foo', 'bar' => 'bar', 'kevin' => 'Kevin', 'coopTilleuls' => 'coop', 'fooBar' => null, 'symfony' => null, 'baz' => 'baz'),
79             $this->normalizer->normalize($group, 'any')
80         );
81     }
82
83     public function testDenormalizeWithParentClass()
84     {
85         $obj = $this->normalizer->denormalize(
86             array('foo' => 'foo', 'bar' => 'bar', 'kevin' => 'Kevin', 'baz' => 'baz'),
87             GroupDummyChild::class,
88             'any'
89         );
90         $this->assertEquals('foo', $obj->getFoo());
91         $this->assertEquals('bar', $obj->getBar());
92         $this->assertEquals('Kevin', $obj->getKevin());
93         $this->assertEquals('baz', $obj->getBaz());
94         $this->assertNull($obj->getSymfony());
95     }
96
97     public function testConstructorDenormalize()
98     {
99         $obj = $this->normalizer->denormalize(
100             array('foo' => 'foo', 'bar' => 'bar'),
101             __NAMESPACE__.'\PropertyConstructorDummy',
102             'any'
103         );
104         $this->assertEquals('foo', $obj->getFoo());
105         $this->assertEquals('bar', $obj->getBar());
106     }
107
108     public function testConstructorDenormalizeWithNullArgument()
109     {
110         $obj = $this->normalizer->denormalize(
111             array('foo' => null, 'bar' => 'bar'),
112             __NAMESPACE__.'\PropertyConstructorDummy', '
113             any'
114         );
115         $this->assertNull($obj->getFoo());
116         $this->assertEquals('bar', $obj->getBar());
117     }
118
119     /**
120      * @dataProvider provideCallbacks
121      */
122     public function testCallbacks($callbacks, $value, $result, $message)
123     {
124         $this->normalizer->setCallbacks($callbacks);
125
126         $obj = new PropertyConstructorDummy('', $value);
127
128         $this->assertEquals(
129             $result,
130             $this->normalizer->normalize($obj, 'any'),
131             $message
132         );
133     }
134
135     /**
136      * @expectedException \InvalidArgumentException
137      */
138     public function testUncallableCallbacks()
139     {
140         $this->normalizer->setCallbacks(array('bar' => null));
141
142         $obj = new PropertyConstructorDummy('baz', 'quux');
143
144         $this->normalizer->normalize($obj, 'any');
145     }
146
147     public function testIgnoredAttributes()
148     {
149         $this->normalizer->setIgnoredAttributes(array('foo', 'bar', 'camelCase'));
150
151         $obj = new PropertyDummy();
152         $obj->foo = 'foo';
153         $obj->setBar('bar');
154
155         $this->assertEquals(
156             array(),
157             $this->normalizer->normalize($obj, 'any')
158         );
159     }
160
161     public function testGroupsNormalize()
162     {
163         $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
164         $this->normalizer = new PropertyNormalizer($classMetadataFactory);
165         $this->normalizer->setSerializer($this->serializer);
166
167         $obj = new GroupDummy();
168         $obj->setFoo('foo');
169         $obj->setBar('bar');
170         $obj->setFooBar('fooBar');
171         $obj->setSymfony('symfony');
172         $obj->setKevin('kevin');
173         $obj->setCoopTilleuls('coopTilleuls');
174
175         $this->assertEquals(array(
176             'bar' => 'bar',
177         ), $this->normalizer->normalize($obj, null, array(PropertyNormalizer::GROUPS => array('c'))));
178
179         // The PropertyNormalizer is also able to hydrate properties from parent classes
180         $this->assertEquals(array(
181             'symfony' => 'symfony',
182             'foo' => 'foo',
183             'fooBar' => 'fooBar',
184             'bar' => 'bar',
185             'kevin' => 'kevin',
186             'coopTilleuls' => 'coopTilleuls',
187         ), $this->normalizer->normalize($obj, null, array(PropertyNormalizer::GROUPS => array('a', 'c'))));
188     }
189
190     public function testGroupsDenormalize()
191     {
192         $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
193         $this->normalizer = new PropertyNormalizer($classMetadataFactory);
194         $this->normalizer->setSerializer($this->serializer);
195
196         $obj = new GroupDummy();
197         $obj->setFoo('foo');
198
199         $toNormalize = array('foo' => 'foo', 'bar' => 'bar');
200
201         $normalized = $this->normalizer->denormalize(
202             $toNormalize,
203             'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy',
204             null,
205             array(PropertyNormalizer::GROUPS => array('a'))
206         );
207         $this->assertEquals($obj, $normalized);
208
209         $obj->setBar('bar');
210
211         $normalized = $this->normalizer->denormalize(
212             $toNormalize,
213             'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy',
214             null,
215             array(PropertyNormalizer::GROUPS => array('a', 'b'))
216         );
217         $this->assertEquals($obj, $normalized);
218     }
219
220     public function testGroupsNormalizeWithNameConverter()
221     {
222         $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
223         $this->normalizer = new PropertyNormalizer($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter());
224         $this->normalizer->setSerializer($this->serializer);
225
226         $obj = new GroupDummy();
227         $obj->setFooBar('@dunglas');
228         $obj->setSymfony('@coopTilleuls');
229         $obj->setCoopTilleuls('les-tilleuls.coop');
230
231         $this->assertEquals(
232             array(
233                 'bar' => null,
234                 'foo_bar' => '@dunglas',
235                 'symfony' => '@coopTilleuls',
236             ),
237             $this->normalizer->normalize($obj, null, array(PropertyNormalizer::GROUPS => array('name_converter')))
238         );
239     }
240
241     public function testGroupsDenormalizeWithNameConverter()
242     {
243         $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
244         $this->normalizer = new PropertyNormalizer($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter());
245         $this->normalizer->setSerializer($this->serializer);
246
247         $obj = new GroupDummy();
248         $obj->setFooBar('@dunglas');
249         $obj->setSymfony('@coopTilleuls');
250
251         $this->assertEquals(
252             $obj,
253             $this->normalizer->denormalize(array(
254                 'bar' => null,
255                 'foo_bar' => '@dunglas',
256                 'symfony' => '@coopTilleuls',
257                 'coop_tilleuls' => 'les-tilleuls.coop',
258             ), 'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy', null, array(PropertyNormalizer::GROUPS => array('name_converter')))
259         );
260     }
261
262     public function provideCallbacks()
263     {
264         return array(
265             array(
266                 array(
267                     'bar' => function ($bar) {
268                         return 'baz';
269                     },
270                 ),
271                 'baz',
272                 array('foo' => '', 'bar' => 'baz'),
273                 'Change a string',
274             ),
275             array(
276                 array(
277                     'bar' => function ($bar) {
278                         return;
279                     },
280                 ),
281                 'baz',
282                 array('foo' => '', 'bar' => null),
283                 'Null an item',
284             ),
285             array(
286                 array(
287                     'bar' => function ($bar) {
288                         return $bar->format('d-m-Y H:i:s');
289                     },
290                 ),
291                 new \DateTime('2011-09-10 06:30:00'),
292                 array('foo' => '', 'bar' => '10-09-2011 06:30:00'),
293                 'Format a date',
294             ),
295             array(
296                 array(
297                     'bar' => function ($bars) {
298                         $foos = '';
299                         foreach ($bars as $bar) {
300                             $foos .= $bar->getFoo();
301                         }
302
303                         return $foos;
304                     },
305                 ),
306                 array(new PropertyConstructorDummy('baz', ''), new PropertyConstructorDummy('quux', '')),
307                 array('foo' => '', 'bar' => 'bazquux'),
308                 'Collect a property',
309             ),
310             array(
311                 array(
312                     'bar' => function ($bars) {
313                         return \count($bars);
314                     },
315                 ),
316                 array(new PropertyConstructorDummy('baz', ''), new PropertyConstructorDummy('quux', '')),
317                 array('foo' => '', 'bar' => 2),
318                 'Count a property',
319             ),
320         );
321     }
322
323     /**
324      * @expectedException \Symfony\Component\Serializer\Exception\CircularReferenceException
325      */
326     public function testUnableToNormalizeCircularReference()
327     {
328         $serializer = new Serializer(array($this->normalizer));
329         $this->normalizer->setSerializer($serializer);
330         $this->normalizer->setCircularReferenceLimit(2);
331
332         $obj = new PropertyCircularReferenceDummy();
333
334         $this->normalizer->normalize($obj);
335     }
336
337     public function testSiblingReference()
338     {
339         $serializer = new Serializer(array($this->normalizer));
340         $this->normalizer->setSerializer($serializer);
341
342         $siblingHolder = new PropertySiblingHolder();
343
344         $expected = array(
345             'sibling0' => array('coopTilleuls' => 'Les-Tilleuls.coop'),
346             'sibling1' => array('coopTilleuls' => 'Les-Tilleuls.coop'),
347             'sibling2' => array('coopTilleuls' => 'Les-Tilleuls.coop'),
348         );
349         $this->assertEquals($expected, $this->normalizer->normalize($siblingHolder));
350     }
351
352     public function testCircularReferenceHandler()
353     {
354         $serializer = new Serializer(array($this->normalizer));
355         $this->normalizer->setSerializer($serializer);
356         $this->normalizer->setCircularReferenceHandler(function ($obj) {
357             return \get_class($obj);
358         });
359
360         $obj = new PropertyCircularReferenceDummy();
361
362         $expected = array('me' => 'Symfony\Component\Serializer\Tests\Fixtures\PropertyCircularReferenceDummy');
363         $this->assertEquals($expected, $this->normalizer->normalize($obj));
364     }
365
366     public function testDenormalizeNonExistingAttribute()
367     {
368         $this->assertEquals(
369             new PropertyDummy(),
370             $this->normalizer->denormalize(array('non_existing' => true), __NAMESPACE__.'\PropertyDummy')
371         );
372     }
373
374     public function testDenormalizeShouldIgnoreStaticProperty()
375     {
376         $obj = $this->normalizer->denormalize(array('outOfScope' => true), __NAMESPACE__.'\PropertyDummy');
377
378         $this->assertEquals(new PropertyDummy(), $obj);
379         $this->assertEquals('out_of_scope', PropertyDummy::$outOfScope);
380     }
381
382     /**
383      * @expectedException \Symfony\Component\Serializer\Exception\LogicException
384      * @expectedExceptionMessage Cannot normalize attribute "bar" because the injected serializer is not a normalizer
385      */
386     public function testUnableToNormalizeObjectAttribute()
387     {
388         $serializer = $this->getMockBuilder('Symfony\Component\Serializer\SerializerInterface')->getMock();
389         $this->normalizer->setSerializer($serializer);
390
391         $obj = new PropertyDummy();
392         $object = new \stdClass();
393         $obj->setBar($object);
394
395         $this->normalizer->normalize($obj, 'any');
396     }
397
398     public function testNoTraversableSupport()
399     {
400         $this->assertFalse($this->normalizer->supportsNormalization(new \ArrayObject()));
401     }
402
403     public function testNoStaticPropertySupport()
404     {
405         $this->assertFalse($this->normalizer->supportsNormalization(new StaticPropertyDummy()));
406     }
407
408     public function testMaxDepth()
409     {
410         $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
411         $this->normalizer = new PropertyNormalizer($classMetadataFactory);
412         $serializer = new Serializer(array($this->normalizer));
413         $this->normalizer->setSerializer($serializer);
414
415         $level1 = new MaxDepthDummy();
416         $level1->foo = 'level1';
417
418         $level2 = new MaxDepthDummy();
419         $level2->foo = 'level2';
420         $level1->child = $level2;
421
422         $level3 = new MaxDepthDummy();
423         $level3->foo = 'level3';
424         $level2->child = $level3;
425
426         $result = $serializer->normalize($level1, null, array(PropertyNormalizer::ENABLE_MAX_DEPTH => true));
427
428         $expected = array(
429             'foo' => 'level1',
430             'child' => array(
431                     'foo' => 'level2',
432                     'child' => array(
433                             'child' => null,
434                             'bar' => null,
435                         ),
436                     'bar' => null,
437                 ),
438             'bar' => null,
439         );
440
441         $this->assertEquals($expected, $result);
442     }
443
444     public function testInheritedPropertiesSupport()
445     {
446         $this->assertTrue($this->normalizer->supportsNormalization(new PropertyChildDummy()));
447     }
448 }
449
450 class PropertyDummy
451 {
452     public static $outOfScope = 'out_of_scope';
453     public $foo;
454     private $bar;
455     protected $camelCase;
456
457     public function getBar()
458     {
459         return $this->bar;
460     }
461
462     public function setBar($bar)
463     {
464         $this->bar = $bar;
465     }
466
467     public function getCamelCase()
468     {
469         return $this->camelCase;
470     }
471
472     public function setCamelCase($camelCase)
473     {
474         $this->camelCase = $camelCase;
475     }
476 }
477
478 class PropertyConstructorDummy
479 {
480     protected $foo;
481     private $bar;
482
483     public function __construct($foo, $bar)
484     {
485         $this->foo = $foo;
486         $this->bar = $bar;
487     }
488
489     public function getFoo()
490     {
491         return $this->foo;
492     }
493
494     public function getBar()
495     {
496         return $this->bar;
497     }
498 }
499
500 class PropertyCamelizedDummy
501 {
502     private $kevinDunglas;
503     public $fooBar;
504     public $bar_foo;
505
506     public function __construct($kevinDunglas = null)
507     {
508         $this->kevinDunglas = $kevinDunglas;
509     }
510 }
511
512 class StaticPropertyDummy
513 {
514     private static $property = 'value';
515 }
516
517 class PropertyParentDummy
518 {
519     private $foo = 'bar';
520 }
521
522 class PropertyChildDummy extends PropertyParentDummy
523 {
524 }