4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Serializer\Tests\Normalizer;
14 use Doctrine\Common\Annotations\AnnotationReader;
15 use PHPUnit\Framework\TestCase;
16 use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
17 use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
18 use Symfony\Component\Serializer\Serializer;
19 use Symfony\Component\Serializer\SerializerInterface;
20 use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
21 use Symfony\Component\Serializer\Tests\Fixtures\CircularReferenceDummy;
22 use Symfony\Component\Serializer\Tests\Fixtures\SiblingHolder;
23 use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
24 use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
25 use Symfony\Component\Serializer\Tests\Fixtures\GroupDummy;
28 * @author Kévin Dunglas <dunglas@gmail.com>
30 class ObjectNormalizerTest extends TestCase
33 * @var ObjectNormalizer
37 * @var SerializerInterface
41 protected function setUp()
43 $this->serializer = $this->getMockBuilder(__NAMESPACE__.'\ObjectSerializerNormalizer')->getMock();
44 $this->normalizer = new ObjectNormalizer();
45 $this->normalizer->setSerializer($this->serializer);
48 public function testNormalize()
50 $obj = new ObjectDummy();
51 $object = new \stdClass();
55 $obj->setCamelCase('camelcase');
56 $obj->setObject($object);
59 ->expects($this->once())
61 ->with($object, 'any')
62 ->will($this->returnValue('string_object'))
71 'camelCase' => 'camelcase',
72 'object' => 'string_object',
74 $this->normalizer->normalize($obj, 'any')
78 public function testDenormalize()
80 $obj = $this->normalizer->denormalize(
81 array('foo' => 'foo', 'bar' => 'bar', 'baz' => true, 'fooBar' => 'foobar'),
82 __NAMESPACE__.'\ObjectDummy',
85 $this->assertEquals('foo', $obj->getFoo());
86 $this->assertEquals('bar', $obj->bar);
87 $this->assertTrue($obj->isBaz());
90 public function testDenormalizeWithObject()
92 $data = new \stdClass();
95 $data->fooBar = 'foobar';
96 $obj = $this->normalizer->denormalize($data, __NAMESPACE__.'\ObjectDummy', 'any');
97 $this->assertEquals('foo', $obj->getFoo());
98 $this->assertEquals('bar', $obj->bar);
104 public function testLegacyDenormalizeOnCamelCaseFormat()
106 $this->normalizer->setCamelizedAttributes(array('camel_case'));
107 $obj = $this->normalizer->denormalize(
108 array('camel_case' => 'camelCase'),
109 __NAMESPACE__.'\ObjectDummy'
111 $this->assertEquals('camelCase', $obj->getCamelCase());
114 public function testNameConverterSupport()
116 $this->normalizer = new ObjectNormalizer(null, new CamelCaseToSnakeCaseNameConverter());
117 $obj = $this->normalizer->denormalize(
118 array('camel_case' => 'camelCase'),
119 __NAMESPACE__.'\ObjectDummy'
121 $this->assertEquals('camelCase', $obj->getCamelCase());
124 public function testDenormalizeNull()
126 $this->assertEquals(new ObjectDummy(), $this->normalizer->denormalize(null, __NAMESPACE__.'\ObjectDummy'));
129 public function testConstructorDenormalize()
131 $obj = $this->normalizer->denormalize(
132 array('foo' => 'foo', 'bar' => 'bar', 'baz' => true, 'fooBar' => 'foobar'),
133 __NAMESPACE__.'\ObjectConstructorDummy', 'any');
134 $this->assertEquals('foo', $obj->getFoo());
135 $this->assertEquals('bar', $obj->bar);
136 $this->assertTrue($obj->isBaz());
139 public function testConstructorDenormalizeWithNullArgument()
141 $obj = $this->normalizer->denormalize(
142 array('foo' => 'foo', 'bar' => null, 'baz' => true),
143 __NAMESPACE__.'\ObjectConstructorDummy', 'any');
144 $this->assertEquals('foo', $obj->getFoo());
145 $this->assertNull($obj->bar);
146 $this->assertTrue($obj->isBaz());
149 public function testConstructorDenormalizeWithMissingOptionalArgument()
151 $obj = $this->normalizer->denormalize(
152 array('foo' => 'test', 'baz' => array(1, 2, 3)),
153 __NAMESPACE__.'\ObjectConstructorOptionalArgsDummy', 'any');
154 $this->assertEquals('test', $obj->getFoo());
155 $this->assertEquals(array(), $obj->bar);
156 $this->assertEquals(array(1, 2, 3), $obj->getBaz());
160 * @see https://bugs.php.net/62715
162 * @requires PHP 5.3.17
164 public function testConstructorDenormalizeWithOptionalDefaultArgument()
166 $obj = $this->normalizer->denormalize(
167 array('bar' => 'test'),
168 __NAMESPACE__.'\ObjectConstructorArgsWithDefaultValueDummy', 'any');
169 $this->assertEquals(array(), $obj->getFoo());
170 $this->assertEquals('test', $obj->getBar());
173 public function testConstructorWithObjectDenormalize()
175 $data = new \stdClass();
179 $data->fooBar = 'foobar';
180 $obj = $this->normalizer->denormalize($data, __NAMESPACE__.'\ObjectConstructorDummy', 'any');
181 $this->assertEquals('foo', $obj->getFoo());
182 $this->assertEquals('bar', $obj->bar);
185 public function testGroupsNormalize()
187 $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
188 $this->normalizer = new ObjectNormalizer($classMetadataFactory);
189 $this->normalizer->setSerializer($this->serializer);
191 $obj = new GroupDummy();
194 $obj->setFooBar('fooBar');
195 $obj->setSymfony('symfony');
196 $obj->setKevin('kevin');
197 $obj->setCoopTilleuls('coopTilleuls');
199 $this->assertEquals(array(
201 ), $this->normalizer->normalize($obj, null, array(ObjectNormalizer::GROUPS => array('c'))));
203 $this->assertEquals(array(
204 'symfony' => 'symfony',
206 'fooBar' => 'fooBar',
209 'coopTilleuls' => 'coopTilleuls',
210 ), $this->normalizer->normalize($obj, null, array(ObjectNormalizer::GROUPS => array('a', 'c'))));
213 public function testGroupsDenormalize()
215 $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
216 $this->normalizer = new ObjectNormalizer($classMetadataFactory);
217 $this->normalizer->setSerializer($this->serializer);
219 $obj = new GroupDummy();
222 $toNormalize = array('foo' => 'foo', 'bar' => 'bar');
224 $normalized = $this->normalizer->denormalize(
226 'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy',
228 array(ObjectNormalizer::GROUPS => array('a'))
230 $this->assertEquals($obj, $normalized);
234 $normalized = $this->normalizer->denormalize(
236 'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy',
238 array(ObjectNormalizer::GROUPS => array('a', 'b'))
240 $this->assertEquals($obj, $normalized);
243 public function testNormalizeNoPropertyInGroup()
245 $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
246 $this->normalizer = new ObjectNormalizer($classMetadataFactory);
247 $this->normalizer->setSerializer($this->serializer);
249 $obj = new GroupDummy();
252 $this->assertEquals(array(), $this->normalizer->normalize($obj, null, array('groups' => array('notExist'))));
255 public function testGroupsNormalizeWithNameConverter()
257 $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
258 $this->normalizer = new ObjectNormalizer($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter());
259 $this->normalizer->setSerializer($this->serializer);
261 $obj = new GroupDummy();
262 $obj->setFooBar('@dunglas');
263 $obj->setSymfony('@coopTilleuls');
264 $obj->setCoopTilleuls('les-tilleuls.coop');
269 'foo_bar' => '@dunglas',
270 'symfony' => '@coopTilleuls',
272 $this->normalizer->normalize($obj, null, array(ObjectNormalizer::GROUPS => array('name_converter')))
276 public function testGroupsDenormalizeWithNameConverter()
278 $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
279 $this->normalizer = new ObjectNormalizer($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter());
280 $this->normalizer->setSerializer($this->serializer);
282 $obj = new GroupDummy();
283 $obj->setFooBar('@dunglas');
284 $obj->setSymfony('@coopTilleuls');
288 $this->normalizer->denormalize(array(
290 'foo_bar' => '@dunglas',
291 'symfony' => '@coopTilleuls',
292 'coop_tilleuls' => 'les-tilleuls.coop',
293 ), 'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy', null, array(ObjectNormalizer::GROUPS => array('name_converter')))
298 * @dataProvider provideCallbacks
300 public function testCallbacks($callbacks, $value, $result, $message)
302 $this->normalizer->setCallbacks($callbacks);
304 $obj = new ObjectConstructorDummy('', $value, true);
308 $this->normalizer->normalize($obj, 'any'),
314 * @expectedException \InvalidArgumentException
316 public function testUncallableCallbacks()
318 $this->normalizer->setCallbacks(array('bar' => null));
320 $obj = new ObjectConstructorDummy('baz', 'quux', true);
322 $this->normalizer->normalize($obj, 'any');
325 public function testIgnoredAttributes()
327 $this->normalizer->setIgnoredAttributes(array('foo', 'bar', 'baz', 'camelCase', 'object'));
329 $obj = new ObjectDummy();
335 array('fooBar' => 'foobar'),
336 $this->normalizer->normalize($obj, 'any')
340 public function testIgnoredAttributesDenormalize()
342 $this->normalizer->setIgnoredAttributes(array('fooBar', 'bar', 'baz'));
344 $obj = new ObjectDummy();
349 $this->normalizer->denormalize(array('fooBar' => 'fooBar', 'foo' => 'foo', 'baz' => 'baz'), __NAMESPACE__.'\ObjectDummy')
353 public function provideCallbacks()
358 'bar' => function ($bar) {
363 array('foo' => '', 'bar' => 'baz', 'baz' => true),
368 'bar' => function ($bar) {
373 array('foo' => '', 'bar' => null, 'baz' => true),
378 'bar' => function ($bar) {
379 return $bar->format('d-m-Y H:i:s');
382 new \DateTime('2011-09-10 06:30:00'),
383 array('foo' => '', 'bar' => '10-09-2011 06:30:00', 'baz' => true),
388 'bar' => function ($bars) {
390 foreach ($bars as $bar) {
391 $foos .= $bar->getFoo();
397 array(new ObjectConstructorDummy('baz', '', false), new ObjectConstructorDummy('quux', '', false)),
398 array('foo' => '', 'bar' => 'bazquux', 'baz' => true),
399 'Collect a property',
403 'bar' => function ($bars) {
407 array(new ObjectConstructorDummy('baz', '', false), new ObjectConstructorDummy('quux', '', false)),
408 array('foo' => '', 'bar' => 2, 'baz' => true),
415 * @expectedException \Symfony\Component\Serializer\Exception\LogicException
416 * @expectedExceptionMessage Cannot normalize attribute "object" because injected serializer is not a normalizer
418 public function testUnableToNormalizeObjectAttribute()
420 $serializer = $this->getMockBuilder('Symfony\Component\Serializer\SerializerInterface')->getMock();
421 $this->normalizer->setSerializer($serializer);
423 $obj = new ObjectDummy();
424 $object = new \stdClass();
425 $obj->setObject($object);
427 $this->normalizer->normalize($obj, 'any');
431 * @expectedException \Symfony\Component\Serializer\Exception\CircularReferenceException
433 public function testUnableToNormalizeCircularReference()
435 $serializer = new Serializer(array($this->normalizer));
436 $this->normalizer->setSerializer($serializer);
437 $this->normalizer->setCircularReferenceLimit(2);
439 $obj = new CircularReferenceDummy();
441 $this->normalizer->normalize($obj);
444 public function testSiblingReference()
446 $serializer = new Serializer(array($this->normalizer));
447 $this->normalizer->setSerializer($serializer);
449 $siblingHolder = new SiblingHolder();
452 'sibling0' => array('coopTilleuls' => 'Les-Tilleuls.coop'),
453 'sibling1' => array('coopTilleuls' => 'Les-Tilleuls.coop'),
454 'sibling2' => array('coopTilleuls' => 'Les-Tilleuls.coop'),
456 $this->assertEquals($expected, $this->normalizer->normalize($siblingHolder));
459 public function testCircularReferenceHandler()
461 $serializer = new Serializer(array($this->normalizer));
462 $this->normalizer->setSerializer($serializer);
463 $this->normalizer->setCircularReferenceHandler(function ($obj) {
464 return get_class($obj);
467 $obj = new CircularReferenceDummy();
469 $expected = array('me' => 'Symfony\Component\Serializer\Tests\Fixtures\CircularReferenceDummy');
470 $this->assertEquals($expected, $this->normalizer->normalize($obj));
473 public function testDenormalizeNonExistingAttribute()
477 $this->normalizer->denormalize(array('non_existing' => true), __NAMESPACE__.'\ObjectDummy')
481 public function testNoTraversableSupport()
483 $this->assertFalse($this->normalizer->supportsNormalization(new \ArrayObject()));
486 public function testNormalizeStatic()
488 $this->assertEquals(array('foo' => 'K'), $this->normalizer->normalize(new ObjectWithStaticPropertiesAndMethods()));
491 public function testNormalizeUpperCaseAttributes()
493 $this->assertEquals(array('Foo' => 'Foo', 'Bar' => 'BarBar'), $this->normalizer->normalize(new ObjectWithUpperCaseAttributeNames()));
496 public function testNormalizeNotSerializableContext()
498 $objectDummy = new ObjectDummy();
508 $this->assertEquals($expected, $this->normalizer->normalize($objectDummy, null, array('not_serializable' => function () {})));
517 protected $camelCase;
520 public function getFoo()
525 public function setFoo($foo)
530 public function isBaz()
535 public function setBaz($baz)
540 public function getFooBar()
542 return $this->foo.$this->bar;
545 public function getCamelCase()
547 return $this->camelCase;
550 public function setCamelCase($camelCase)
552 $this->camelCase = $camelCase;
555 public function otherMethod()
557 throw new \RuntimeException('Dummy::otherMethod() should not be called');
560 public function setObject($object)
562 $this->object = $object;
565 public function getObject()
567 return $this->object;
571 class ObjectConstructorDummy
577 public function __construct($foo, $bar, $baz)
584 public function getFoo()
589 public function isBaz()
594 public function otherMethod()
596 throw new \RuntimeException('Dummy::otherMethod() should not be called');
600 abstract class ObjectSerializerNormalizer implements SerializerInterface, NormalizerInterface
604 class ObjectConstructorOptionalArgsDummy
610 public function __construct($foo, $bar = array(), $baz = array())
617 public function getFoo()
622 public function getBaz()
627 public function otherMethod()
629 throw new \RuntimeException('Dummy::otherMethod() should not be called');
633 class ObjectConstructorArgsWithDefaultValueDummy
638 public function __construct($foo = array(), $bar)
644 public function getFoo()
649 public function getBar()
654 public function otherMethod()
656 throw new \RuntimeException('Dummy::otherMethod() should not be called');
660 class ObjectWithStaticPropertiesAndMethods
663 public static $bar = 'A';
665 public static function getBaz()
671 class ObjectWithUpperCaseAttributeNames
673 private $Foo = 'Foo';
674 public $Bar = 'BarBar';
676 public function getFoo()