[serializer] extract normalizer tests to traits
This commit is contained in:
parent
77f642ef39
commit
2b6ebea73c
@ -32,20 +32,89 @@ abstract class AbstractNormalizer implements NormalizerInterface, DenormalizerIn
|
||||
use ObjectToPopulateTrait;
|
||||
use SerializerAwareTrait;
|
||||
|
||||
const CIRCULAR_REFERENCE_LIMIT = 'circular_reference_limit';
|
||||
const OBJECT_TO_POPULATE = 'object_to_populate';
|
||||
const GROUPS = 'groups';
|
||||
const ATTRIBUTES = 'attributes';
|
||||
const ALLOW_EXTRA_ATTRIBUTES = 'allow_extra_attributes';
|
||||
const DEFAULT_CONSTRUCTOR_ARGUMENTS = 'default_constructor_arguments';
|
||||
const CALLBACKS = 'callbacks';
|
||||
const CIRCULAR_REFERENCE_HANDLER = 'circular_reference_handler';
|
||||
const IGNORED_ATTRIBUTES = 'ignored_attributes';
|
||||
/* constants to configure the context */
|
||||
|
||||
/**
|
||||
* How many loops of circular reference to allow while normalizing.
|
||||
*
|
||||
* The default value of 1 means that when we encounter the same object a
|
||||
* second time, we consider that a circular reference.
|
||||
*
|
||||
* You can raise this value for special cases, e.g. in combination with the
|
||||
* max depth setting of the object normalizer.
|
||||
*/
|
||||
public const CIRCULAR_REFERENCE_LIMIT = 'circular_reference_limit';
|
||||
|
||||
/**
|
||||
* Instead of creating a new instance of an object, update the specified object.
|
||||
*
|
||||
* If you have a nested structure, child objects will be overwritten with
|
||||
* new instances unless you set DEEP_OBJECT_TO_POPULATE to true.
|
||||
*/
|
||||
public const OBJECT_TO_POPULATE = 'object_to_populate';
|
||||
|
||||
/**
|
||||
* Only (de)normalize attributes that are in the specified groups.
|
||||
*/
|
||||
public const GROUPS = 'groups';
|
||||
|
||||
/**
|
||||
* Limit (de)normalize to the specified names.
|
||||
*
|
||||
* For nested structures, this list needs to reflect the object tree.
|
||||
*/
|
||||
public const ATTRIBUTES = 'attributes';
|
||||
|
||||
/**
|
||||
* If ATTRIBUTES are specified, and the source has fields that are not part of that list,
|
||||
* either ignore those attributes (true) or throw an ExtraAttributesException (false).
|
||||
*/
|
||||
public const ALLOW_EXTRA_ATTRIBUTES = 'allow_extra_attributes';
|
||||
|
||||
/**
|
||||
* Hashmap of default values for constructor arguments.
|
||||
*
|
||||
* The names need to match the parameter names in the constructor arguments.
|
||||
*/
|
||||
public const DEFAULT_CONSTRUCTOR_ARGUMENTS = 'default_constructor_arguments';
|
||||
|
||||
/**
|
||||
* Hashmap of field name => callable to normalize this field.
|
||||
*
|
||||
* The callable is called if the field is encountered with the arguments:
|
||||
*
|
||||
* - mixed $attributeValue value of this field
|
||||
* - object $object the whole object being normalized
|
||||
* - string $attributeName name of the attribute being normalized
|
||||
* - string $format the requested format
|
||||
* - array $context the serialization context
|
||||
*/
|
||||
public const CALLBACKS = 'callbacks';
|
||||
|
||||
/**
|
||||
* Handler to call when a circular reference has been detected.
|
||||
*
|
||||
* If you specify no handler, a CircularReferenceException is thrown.
|
||||
*
|
||||
* The method will be called with ($object, $format, $context) and its
|
||||
* return value is returned as the result of the normalize call.
|
||||
*/
|
||||
public const CIRCULAR_REFERENCE_HANDLER = 'circular_reference_handler';
|
||||
|
||||
/**
|
||||
* Skip the specified attributes when normalizing an object tree.
|
||||
*
|
||||
* This list is applied to each element of nested structures.
|
||||
*
|
||||
* Note: The behaviour for nested structures is different from ATTRIBUTES
|
||||
* for historical reason. Aligning the behaviour would be a BC break.
|
||||
*/
|
||||
public const IGNORED_ATTRIBUTES = 'ignored_attributes';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const CIRCULAR_REFERENCE_LIMIT_COUNTERS = 'circular_reference_limit_counters';
|
||||
protected const CIRCULAR_REFERENCE_LIMIT_COUNTERS = 'circular_reference_limit_counters';
|
||||
|
||||
protected $defaultContext = [
|
||||
self::ALLOW_EXTRA_ATTRIBUTES => true,
|
||||
|
@ -33,13 +33,60 @@ use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
|
||||
*/
|
||||
abstract class AbstractObjectNormalizer extends AbstractNormalizer
|
||||
{
|
||||
const ENABLE_MAX_DEPTH = 'enable_max_depth';
|
||||
const DEPTH_KEY_PATTERN = 'depth_%s::%s';
|
||||
const DISABLE_TYPE_ENFORCEMENT = 'disable_type_enforcement';
|
||||
const SKIP_NULL_VALUES = 'skip_null_values';
|
||||
const MAX_DEPTH_HANDLER = 'max_depth_handler';
|
||||
const EXCLUDE_FROM_CACHE_KEY = 'exclude_from_cache_key';
|
||||
const DEEP_OBJECT_TO_POPULATE = 'deep_object_to_populate';
|
||||
/**
|
||||
* Set to true to respect the max depth metadata on fields.
|
||||
*/
|
||||
public const ENABLE_MAX_DEPTH = 'enable_max_depth';
|
||||
|
||||
/**
|
||||
* How to track the current depth in the context.
|
||||
*/
|
||||
private const DEPTH_KEY_PATTERN = 'depth_%s::%s';
|
||||
|
||||
/**
|
||||
* While denormalizing, we can verify that types match.
|
||||
*
|
||||
* You can disable this by setting this flag to true.
|
||||
*/
|
||||
public const DISABLE_TYPE_ENFORCEMENT = 'disable_type_enforcement';
|
||||
|
||||
/**
|
||||
* Flag to control whether fields with the value `null` should be output
|
||||
* when normalizing or omitted.
|
||||
*/
|
||||
public const SKIP_NULL_VALUES = 'skip_null_values';
|
||||
|
||||
/**
|
||||
* Callback to allow to set a value for an attribute when the max depth has
|
||||
* been reached.
|
||||
*
|
||||
* If no callback is given, the attribute is skipped. If a callable is
|
||||
* given, its return value is used (even if null).
|
||||
*
|
||||
* The arguments are:
|
||||
*
|
||||
* - mixed $attributeValue value of this field
|
||||
* - object $object the whole object being normalized
|
||||
* - string $attributeName name of the attribute being normalized
|
||||
* - string $format the requested format
|
||||
* - array $context the serialization context
|
||||
*/
|
||||
public const MAX_DEPTH_HANDLER = 'max_depth_handler';
|
||||
|
||||
/**
|
||||
* Specify which context key are not relevant to determine which attributes
|
||||
* of an object to (de)normalize.
|
||||
*/
|
||||
public const EXCLUDE_FROM_CACHE_KEY = 'exclude_from_cache_key';
|
||||
|
||||
/**
|
||||
* Flag to tell the denormalizer to also populate existing objects on
|
||||
* attributes of the main object.
|
||||
*
|
||||
* Setting this to true is only useful if you also specify the root object
|
||||
* in OBJECT_TO_POPULATE.
|
||||
*/
|
||||
public const DEEP_OBJECT_TO_POPULATE = 'deep_object_to_populate';
|
||||
|
||||
private $propertyTypeExtractor;
|
||||
private $typesCache = [];
|
||||
|
@ -19,4 +19,20 @@ class DeepObjectPopulateChildDummy
|
||||
public $foo;
|
||||
|
||||
public $bar;
|
||||
|
||||
// needed to have GetSetNormalizer consider this class as supported
|
||||
public function getFoo()
|
||||
{
|
||||
return $this->foo;
|
||||
}
|
||||
|
||||
public function setFoo($foo)
|
||||
{
|
||||
$this->foo = $foo;
|
||||
}
|
||||
|
||||
public function setBar($bar)
|
||||
{
|
||||
$this->bar = $bar;
|
||||
}
|
||||
}
|
||||
|
@ -42,4 +42,9 @@ class MaxDepthDummy
|
||||
{
|
||||
return $this->child;
|
||||
}
|
||||
|
||||
public function getFoo()
|
||||
{
|
||||
return $this->foo;
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\AbstractNormalizerDummy;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\NullableConstructorArgumentDummy;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\ProxyDummy;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\StaticConstructorDummy;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\StaticConstructorNormalizer;
|
||||
|
||||
@ -99,18 +98,6 @@ class AbstractNormalizerTest extends TestCase
|
||||
$this->assertEquals([$a3, $a4], $result);
|
||||
}
|
||||
|
||||
public function testObjectToPopulateWithProxy()
|
||||
{
|
||||
$proxyDummy = new ProxyDummy();
|
||||
|
||||
$context = [AbstractNormalizer::OBJECT_TO_POPULATE => $proxyDummy];
|
||||
|
||||
$normalizer = new ObjectNormalizer();
|
||||
$normalizer->denormalize(['foo' => 'bar'], 'Symfony\Component\Serializer\Tests\Fixtures\ToBeProxyfiedDummy', null, $context);
|
||||
|
||||
$this->assertSame('bar', $proxyDummy->getFoo());
|
||||
}
|
||||
|
||||
public function testObjectWithStaticConstructor()
|
||||
{
|
||||
$normalizer = new StaticConstructorNormalizer();
|
||||
|
@ -23,8 +23,6 @@ use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
||||
use Symfony\Component\Serializer\SerializerAwareInterface;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\DeepObjectPopulateChildDummy;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\DeepObjectPopulateParentDummy;
|
||||
|
||||
class AbstractObjectNormalizerTest extends TestCase
|
||||
{
|
||||
@ -163,58 +161,6 @@ class AbstractObjectNormalizerTest extends TestCase
|
||||
'allow_extra_attributes' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testSkipNullValues()
|
||||
{
|
||||
$dummy = new Dummy();
|
||||
$dummy->bar = 'present';
|
||||
|
||||
$normalizer = new ObjectNormalizer();
|
||||
$result = $normalizer->normalize($dummy, null, [AbstractObjectNormalizer::SKIP_NULL_VALUES => true]);
|
||||
$this->assertSame(['bar' => 'present'], $result);
|
||||
}
|
||||
|
||||
public function testDeepObjectToPopulate()
|
||||
{
|
||||
$child = new DeepObjectPopulateChildDummy();
|
||||
$child->bar = 'bar-old';
|
||||
$child->foo = 'foo-old';
|
||||
|
||||
$parent = new DeepObjectPopulateParentDummy();
|
||||
$parent->setChild($child);
|
||||
|
||||
$context = [
|
||||
AbstractObjectNormalizer::OBJECT_TO_POPULATE => $parent,
|
||||
AbstractObjectNormalizer::DEEP_OBJECT_TO_POPULATE => true,
|
||||
];
|
||||
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
$normalizer = new ObjectNormalizer($classMetadataFactory, null, null, new PhpDocExtractor());
|
||||
|
||||
$newChild = new DeepObjectPopulateChildDummy();
|
||||
$newChild->bar = 'bar-new';
|
||||
$newChild->foo = 'foo-old';
|
||||
|
||||
$serializer = $this->getMockBuilder(__NAMESPACE__.'\ObjectSerializerDenormalizer')->getMock();
|
||||
$serializer
|
||||
->method('supportsDenormalization')
|
||||
->with($this->arrayHasKey('bar'),
|
||||
$this->equalTo(DeepObjectPopulateChildDummy::class),
|
||||
$this->isNull(),
|
||||
$this->contains($child))
|
||||
->willReturn(true);
|
||||
$serializer->method('denormalize')->willReturn($newChild);
|
||||
|
||||
$normalizer->setSerializer($serializer);
|
||||
$normalizer->denormalize([
|
||||
'child' => [
|
||||
'bar' => 'bar-new',
|
||||
],
|
||||
], 'Symfony\Component\Serializer\Tests\Fixtures\DeepObjectPopulateParentDummy', null, $context);
|
||||
|
||||
$this->assertSame('bar-new', $parent->getChild()->bar);
|
||||
$this->assertSame('foo-old', $parent->getChild()->foo);
|
||||
}
|
||||
}
|
||||
|
||||
class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer
|
||||
|
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests\Normalizer\Features;
|
||||
|
||||
use Symfony\Component\Serializer\Exception\ExtraAttributesException;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
/**
|
||||
* Test AbstractNormalizer::ATTRIBUTES and AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES.
|
||||
*/
|
||||
trait AttributesTestTrait
|
||||
{
|
||||
abstract protected function getNormalizerForAttributes(): NormalizerInterface;
|
||||
|
||||
abstract protected function getDenormalizerForAttributes(): DenormalizerInterface;
|
||||
|
||||
public function testAttributesNormalize()
|
||||
{
|
||||
$normalizer = $this->getNormalizerForAttributes();
|
||||
|
||||
$objectInner = new ObjectInner();
|
||||
$objectInner->foo = 'innerFoo';
|
||||
$objectInner->bar = 'innerBar';
|
||||
|
||||
$objectDummy = new ObjectDummy();
|
||||
$objectDummy->setFoo('foo');
|
||||
$objectDummy->setBaz(true);
|
||||
$objectDummy->setObject($objectInner);
|
||||
|
||||
$context = ['attributes' => ['foo', 'baz', 'object' => ['foo']]];
|
||||
$this->assertEquals(
|
||||
[
|
||||
'foo' => 'foo',
|
||||
'baz' => true,
|
||||
'object' => ['foo' => 'innerFoo'],
|
||||
],
|
||||
$normalizer->normalize($objectDummy, null, $context)
|
||||
);
|
||||
|
||||
$context = ['attributes' => ['foo', 'baz', 'object']];
|
||||
$this->assertEquals(
|
||||
[
|
||||
'foo' => 'foo',
|
||||
'baz' => true,
|
||||
'object' => ['foo' => 'innerFoo', 'bar' => 'innerBar'],
|
||||
],
|
||||
$normalizer->normalize($objectDummy, null, $context)
|
||||
);
|
||||
}
|
||||
|
||||
public function testAttributesContextDenormalize()
|
||||
{
|
||||
$normalizer = $this->getDenormalizerForAttributes();
|
||||
|
||||
$objectInner = new ObjectInner();
|
||||
$objectInner->foo = 'innerFoo';
|
||||
|
||||
$objectOuter = new ObjectOuter();
|
||||
$objectOuter->bar = 'bar';
|
||||
$objectOuter->setInner($objectInner);
|
||||
|
||||
$context = ['attributes' => ['bar', 'inner' => ['foo']]];
|
||||
$this->assertEquals($objectOuter, $normalizer->denormalize(
|
||||
[
|
||||
'foo' => 'foo',
|
||||
'bar' => 'bar',
|
||||
'date' => '2017-02-03',
|
||||
'inner' => ['foo' => 'innerFoo', 'bar' => 'innerBar'],
|
||||
], ObjectOuter::class, null, $context));
|
||||
}
|
||||
|
||||
public function testAttributesContextDenormalizeIgnoreExtraAttributes()
|
||||
{
|
||||
$normalizer = $this->getDenormalizerForAttributes();
|
||||
|
||||
$objectInner = new ObjectInner();
|
||||
$objectInner->foo = 'innerFoo';
|
||||
|
||||
$objectOuter = new ObjectOuter();
|
||||
$objectOuter->setInner($objectInner);
|
||||
|
||||
$context = ['attributes' => ['inner' => ['foo']]];
|
||||
$this->assertEquals($objectOuter, $normalizer->denormalize(
|
||||
[
|
||||
'foo' => 'foo',
|
||||
'bar' => 'changed',
|
||||
'date' => '2017-02-03',
|
||||
'inner' => ['foo' => 'innerFoo', 'bar' => 'innerBar'],
|
||||
], ObjectOuter::class, null, $context));
|
||||
}
|
||||
|
||||
public function testAttributesContextDenormalizeExceptionExtraAttributes()
|
||||
{
|
||||
$normalizer = $this->getDenormalizerForAttributes();
|
||||
|
||||
$context = [
|
||||
'attributes' => ['bar', 'inner' => ['foo']],
|
||||
'allow_extra_attributes' => false,
|
||||
];
|
||||
$this->expectException(ExtraAttributesException::class);
|
||||
$normalizer->denormalize(
|
||||
[
|
||||
'bar' => 'bar',
|
||||
'inner' => ['foo' => 'innerFoo', 'bar' => 'innerBar'],
|
||||
], ObjectOuter::class, null, $context);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests\Normalizer\Features;
|
||||
|
||||
class CallbacksObject
|
||||
{
|
||||
public $bar;
|
||||
|
||||
public function __construct($bar = null)
|
||||
{
|
||||
$this->bar = $bar;
|
||||
}
|
||||
|
||||
public function getBar()
|
||||
{
|
||||
return $this->bar;
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests\Normalizer\Features;
|
||||
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
/**
|
||||
* Test AbstractNormalizer::CALLBACKS.
|
||||
*/
|
||||
trait CallbacksTestTrait
|
||||
{
|
||||
abstract protected function getNormalizerForCallbacks(): NormalizerInterface;
|
||||
|
||||
/**
|
||||
* @dataProvider provideCallbacks
|
||||
*/
|
||||
public function testCallbacks($callbacks, $valueBar, $result)
|
||||
{
|
||||
$normalizer = $this->getNormalizerForCallbacks();
|
||||
|
||||
$obj = new CallbacksObject();
|
||||
$obj->bar = $valueBar;
|
||||
|
||||
$this->assertEquals(
|
||||
$result,
|
||||
$normalizer->normalize($obj, 'any', ['callbacks' => $callbacks])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideInvalidCallbacks()
|
||||
*/
|
||||
public function testUncallableCallbacks($callbacks)
|
||||
{
|
||||
$normalizer = $this->getNormalizerForCallbacks();
|
||||
|
||||
$obj = new CallbacksObject();
|
||||
|
||||
$this->markTestSkipped('Callback validation for callbacks in the context has been forgotten. See https://github.com/symfony/symfony/pull/30950');
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$normalizer->normalize($obj, null, ['callbacks' => $callbacks]);
|
||||
}
|
||||
|
||||
public function provideCallbacks()
|
||||
{
|
||||
return [
|
||||
'Change a string' => [
|
||||
[
|
||||
'bar' => function ($bar) {
|
||||
$this->assertEquals('baz', $bar);
|
||||
|
||||
return 'baz';
|
||||
},
|
||||
],
|
||||
'baz',
|
||||
['bar' => 'baz'],
|
||||
],
|
||||
'Null an item' => [
|
||||
[
|
||||
'bar' => function ($value, $object, $attributeName, $format, $context) {
|
||||
$this->assertSame('baz', $value);
|
||||
$this->assertInstanceOf(CallbacksObject::class, $object);
|
||||
$this->assertSame('bar', $attributeName);
|
||||
$this->assertSame('any', $format);
|
||||
$this->assertArrayHasKey('circular_reference_limit_counters', $context);
|
||||
},
|
||||
],
|
||||
'baz',
|
||||
['bar' => null],
|
||||
],
|
||||
'Format a date' => [
|
||||
[
|
||||
'bar' => function ($bar) {
|
||||
$this->assertInstanceOf(\DateTime::class, $bar);
|
||||
|
||||
return $bar->format('d-m-Y H:i:s');
|
||||
},
|
||||
],
|
||||
new \DateTime('2011-09-10 06:30:00'),
|
||||
['bar' => '10-09-2011 06:30:00'],
|
||||
],
|
||||
'Collect a property' => [
|
||||
[
|
||||
'bar' => function (array $bars) {
|
||||
$result = '';
|
||||
foreach ($bars as $bar) {
|
||||
$result .= $bar->bar;
|
||||
}
|
||||
|
||||
return $result;
|
||||
},
|
||||
],
|
||||
[new CallbacksObject('baz'), new CallbacksObject('quux')],
|
||||
['bar' => 'bazquux'],
|
||||
],
|
||||
'Count a property' => [
|
||||
[
|
||||
'bar' => function (array $bars) {
|
||||
return \count($bars);
|
||||
},
|
||||
],
|
||||
[new CallbacksObject(), new CallbacksObject()],
|
||||
['bar' => 2],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function provideInvalidCallbacks()
|
||||
{
|
||||
return [
|
||||
[['bar' => null]],
|
||||
[['bar' => 'thisisnotavalidfunction']],
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests\Normalizer\Features;
|
||||
|
||||
use Symfony\Component\Serializer\Exception\CircularReferenceException;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
/**
|
||||
* Test AbstractNormalizer::CIRCULAR_REFERENCE_LIMIT and AbstractNormalizer::CIRCULAR_REFERENCE_HANDLER.
|
||||
*/
|
||||
trait CircularReferenceTestTrait
|
||||
{
|
||||
abstract protected function getNormalizerForCircularReference(): NormalizerInterface;
|
||||
|
||||
abstract protected function getSelfReferencingModel();
|
||||
|
||||
public function testUnableToNormalizeCircularReference()
|
||||
{
|
||||
$normalizer = $this->getNormalizerForCircularReference();
|
||||
|
||||
$obj = $this->getSelfReferencingModel();
|
||||
|
||||
$this->expectException(CircularReferenceException::class);
|
||||
$normalizer->normalize($obj, null, ['circular_reference_limit' => 2]);
|
||||
}
|
||||
|
||||
public function testCircularReferenceHandler()
|
||||
{
|
||||
$normalizer = $this->getNormalizerForCircularReference();
|
||||
|
||||
$obj = $this->getSelfReferencingModel();
|
||||
$expected = ['me' => \get_class($obj)];
|
||||
|
||||
$context = [
|
||||
'circular_reference_handler' => function ($actualObj, string $format, array $context) use ($obj) {
|
||||
$this->assertInstanceOf(\get_class($obj), $actualObj);
|
||||
$this->assertSame('test', $format);
|
||||
$this->assertArrayHasKey('foo', $context);
|
||||
|
||||
return \get_class($actualObj);
|
||||
},
|
||||
'foo' => 'bar',
|
||||
];
|
||||
$this->assertEquals($expected, $normalizer->normalize($obj, 'test', $context));
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests\Normalizer\Features;
|
||||
|
||||
class ConstructorArgumentsObject
|
||||
{
|
||||
private $foo;
|
||||
private $bar;
|
||||
private $baz;
|
||||
|
||||
public function __construct($foo, $bar, $baz)
|
||||
{
|
||||
$this->foo = $foo;
|
||||
$this->bar = $bar;
|
||||
$this->baz = $baz;
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests\Normalizer\Features;
|
||||
|
||||
use Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\NotSerializedConstructorArgumentDummy;
|
||||
|
||||
trait ConstructorArgumentsTestTrait
|
||||
{
|
||||
abstract protected function getDenormalizerForConstructArguments(): DenormalizerInterface;
|
||||
|
||||
public function testDefaultConstructorArguments()
|
||||
{
|
||||
$data = [
|
||||
'foo' => 10,
|
||||
];
|
||||
|
||||
$denormalizer = $this->getDenormalizerForConstructArguments();
|
||||
|
||||
$result = $denormalizer->denormalize($data, ConstructorArgumentsObject::class, 'json', [
|
||||
'default_constructor_arguments' => [
|
||||
ConstructorArgumentsObject::class => ['foo' => '', 'bar' => '', 'baz' => null],
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(new ConstructorArgumentsObject(10, '', null), $result);
|
||||
}
|
||||
|
||||
public function testMetadataAwareNameConvertorWithNotSerializedConstructorParameter()
|
||||
{
|
||||
$denormalizer = $this->getDenormalizerForConstructArguments();
|
||||
|
||||
$obj = new NotSerializedConstructorArgumentDummy('buz');
|
||||
$obj->setBar('xyz');
|
||||
|
||||
$this->assertEquals(
|
||||
$obj,
|
||||
$denormalizer->denormalize(['bar' => 'xyz'],
|
||||
NotSerializedConstructorArgumentDummy::class,
|
||||
null,
|
||||
['default_constructor_arguments' => [
|
||||
NotSerializedConstructorArgumentDummy::class => ['foo' => 'buz'],
|
||||
]]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function testConstructorWithMissingData()
|
||||
{
|
||||
$data = [
|
||||
'foo' => 10,
|
||||
];
|
||||
|
||||
$normalizer = $this->getDenormalizerForConstructArguments();
|
||||
|
||||
$this->expectException(MissingConstructorArgumentsException::class);
|
||||
$this->expectExceptionMessage('Cannot create an instance of '.ConstructorArgumentsObject::class.' from serialized data because its constructor requires parameter "bar" to be present.');
|
||||
$normalizer->denormalize($data, ConstructorArgumentsObject::class);
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests\Normalizer\Features;
|
||||
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\GroupDummy;
|
||||
|
||||
/**
|
||||
* Test AbstractNormalizer::GROUPS.
|
||||
*/
|
||||
trait GroupsTestTrait
|
||||
{
|
||||
abstract protected function getNormalizerForGroups(): NormalizerInterface;
|
||||
|
||||
abstract protected function getDenormalizerForGroups(): DenormalizerInterface;
|
||||
|
||||
public function testGroupsNormalize()
|
||||
{
|
||||
$normalizer = $this->getNormalizerForGroups();
|
||||
|
||||
$obj = new GroupDummy();
|
||||
$obj->setFoo('foo');
|
||||
$obj->setBar('bar');
|
||||
$obj->setFooBar('fooBar');
|
||||
$obj->setSymfony('symfony');
|
||||
$obj->setKevin('kevin');
|
||||
$obj->setCoopTilleuls('coopTilleuls');
|
||||
|
||||
$this->assertEquals([
|
||||
'bar' => 'bar',
|
||||
], $normalizer->normalize($obj, null, ['groups' => ['c']]));
|
||||
|
||||
$this->assertEquals([
|
||||
'symfony' => 'symfony',
|
||||
'foo' => 'foo',
|
||||
'fooBar' => 'fooBar',
|
||||
'bar' => 'bar',
|
||||
'kevin' => 'kevin',
|
||||
'coopTilleuls' => 'coopTilleuls',
|
||||
], $normalizer->normalize($obj, null, ['groups' => ['a', 'c']]));
|
||||
}
|
||||
|
||||
public function testGroupsDenormalize()
|
||||
{
|
||||
$normalizer = $this->getDenormalizerForGroups();
|
||||
|
||||
$obj = new GroupDummy();
|
||||
$obj->setFoo('foo');
|
||||
|
||||
$data = ['foo' => 'foo', 'bar' => 'bar'];
|
||||
|
||||
$normalized = $normalizer->denormalize(
|
||||
$data,
|
||||
GroupDummy::class,
|
||||
null,
|
||||
['groups' => ['a']]
|
||||
);
|
||||
$this->assertEquals($obj, $normalized);
|
||||
|
||||
$obj->setBar('bar');
|
||||
|
||||
$normalized = $normalizer->denormalize(
|
||||
$data,
|
||||
GroupDummy::class,
|
||||
null,
|
||||
['groups' => ['a', 'b']]
|
||||
);
|
||||
$this->assertEquals($obj, $normalized);
|
||||
}
|
||||
|
||||
public function testNormalizeNoPropertyInGroup()
|
||||
{
|
||||
$normalizer = $this->getNormalizerForGroups();
|
||||
|
||||
$obj = new GroupDummy();
|
||||
$obj->setFoo('foo');
|
||||
|
||||
$this->assertEquals([], $normalizer->normalize($obj, null, ['groups' => ['notExist']]));
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests\Normalizer\Features;
|
||||
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
/**
|
||||
* Test AbstractNormalizer::IGNORED_ATTRIBUTES.
|
||||
*/
|
||||
trait IgnoredAttributesTestTrait
|
||||
{
|
||||
abstract protected function getNormalizerForIgnoredAttributes(): NormalizerInterface;
|
||||
|
||||
abstract protected function getDenormalizerForIgnoredAttributes(): DenormalizerInterface;
|
||||
|
||||
public function testIgnoredAttributesNormalize()
|
||||
{
|
||||
$normalizer = $this->getNormalizerForIgnoredAttributes();
|
||||
|
||||
$objectInner = new ObjectInner();
|
||||
$objectInner->foo = 'innerFoo';
|
||||
$objectInner->bar = 'innerBar';
|
||||
|
||||
$objectOuter = new ObjectOuter();
|
||||
$objectOuter->foo = 'foo';
|
||||
$objectOuter->bar = 'bar';
|
||||
$objectOuter->setInner($objectInner);
|
||||
|
||||
$context = ['ignored_attributes' => ['bar']];
|
||||
$this->assertEquals(
|
||||
[
|
||||
'foo' => 'foo',
|
||||
'inner' => ['foo' => 'innerFoo'],
|
||||
'date' => null,
|
||||
'inners' => null,
|
||||
],
|
||||
$normalizer->normalize($objectOuter, null, $context)
|
||||
);
|
||||
|
||||
$this->markTestIncomplete('AbstractObjectNormalizer::getAttributes caches attributes by class instead of by class+context, reusing the normalizer with different config therefore fails. This is being fixed in https://github.com/symfony/symfony/pull/30907');
|
||||
|
||||
$context = ['ignored_attributes' => ['foo', 'inner']];
|
||||
$this->assertEquals(
|
||||
[
|
||||
'bar' => 'bar',
|
||||
'date' => null,
|
||||
'inners' => null,
|
||||
],
|
||||
$normalizer->normalize($objectOuter, null, $context)
|
||||
);
|
||||
}
|
||||
|
||||
public function testIgnoredAttributesContextDenormalize()
|
||||
{
|
||||
$normalizer = $this->getDenormalizerForIgnoredAttributes();
|
||||
|
||||
$objectOuter = new ObjectOuter();
|
||||
$objectOuter->bar = 'bar';
|
||||
|
||||
$context = ['ignored_attributes' => ['foo', 'inner']];
|
||||
$this->assertEquals($objectOuter, $normalizer->denormalize(
|
||||
[
|
||||
'foo' => 'foo',
|
||||
'bar' => 'bar',
|
||||
'inner' => ['foo' => 'innerFoo', 'bar' => 'innerBar'],
|
||||
], ObjectOuter::class, null, $context));
|
||||
}
|
||||
|
||||
public function testIgnoredAttributesContextDenormalizeInherit()
|
||||
{
|
||||
$normalizer = $this->getDenormalizerForIgnoredAttributes();
|
||||
|
||||
$objectInner = new ObjectInner();
|
||||
$objectInner->foo = 'innerFoo';
|
||||
|
||||
$objectOuter = new ObjectOuter();
|
||||
$objectOuter->foo = 'foo';
|
||||
$objectOuter->setInner($objectInner);
|
||||
|
||||
$context = ['ignored_attributes' => ['bar']];
|
||||
$this->assertEquals($objectOuter, $normalizer->denormalize(
|
||||
[
|
||||
'foo' => 'foo',
|
||||
'bar' => 'bar',
|
||||
'inner' => ['foo' => 'innerFoo', 'bar' => 'innerBar'],
|
||||
], ObjectOuter::class, null, $context));
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests\Normalizer\Features;
|
||||
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\MaxDepthDummy;
|
||||
|
||||
/**
|
||||
* Covers AbstractObjectNormalizer::ENABLE_MAX_DEPTH and AbstractObjectNormalizer::MAX_DEPTH_HANDLER.
|
||||
*/
|
||||
trait MaxDepthTestTrait
|
||||
{
|
||||
abstract protected function getNormalizerForMaxDepth(): NormalizerInterface;
|
||||
|
||||
public function testMaxDepth()
|
||||
{
|
||||
$normalizer = $this->getNormalizerForMaxDepth();
|
||||
|
||||
$level1 = new MaxDepthDummy();
|
||||
$level1->bar = 'level1';
|
||||
|
||||
$level2 = new MaxDepthDummy();
|
||||
$level2->bar = 'level2';
|
||||
$level1->child = $level2;
|
||||
|
||||
$level3 = new MaxDepthDummy();
|
||||
$level3->bar = 'level3';
|
||||
$level2->child = $level3;
|
||||
|
||||
$level4 = new MaxDepthDummy();
|
||||
$level4->bar = 'level4';
|
||||
$level3->child = $level4;
|
||||
|
||||
$result = $normalizer->normalize($level1, null, ['enable_max_depth' => true]);
|
||||
|
||||
$expected = [
|
||||
'bar' => 'level1',
|
||||
'child' => [
|
||||
'bar' => 'level2',
|
||||
'child' => [
|
||||
'bar' => 'level3',
|
||||
'child' => [
|
||||
'child' => null,
|
||||
],
|
||||
],
|
||||
'foo' => null,
|
||||
],
|
||||
'foo' => null,
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
public function testMaxDepthHandler()
|
||||
{
|
||||
$level1 = new MaxDepthDummy();
|
||||
$level1->bar = 'level1';
|
||||
|
||||
$level2 = new MaxDepthDummy();
|
||||
$level2->bar = 'level2';
|
||||
$level1->child = $level2;
|
||||
|
||||
$level3 = new MaxDepthDummy();
|
||||
$level3->bar = 'level3';
|
||||
$level2->child = $level3;
|
||||
|
||||
$level4 = new MaxDepthDummy();
|
||||
$level4->bar = 'level4';
|
||||
$level3->child = $level4;
|
||||
|
||||
$handlerCalled = false;
|
||||
$maxDepthHandler = function ($object, $parentObject, $attributeName, $format, $context) use (&$handlerCalled) {
|
||||
if ('foo' === $attributeName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->assertSame('level4', $object);
|
||||
$this->assertInstanceOf(MaxDepthDummy::class, $parentObject);
|
||||
$this->assertSame('bar', $attributeName);
|
||||
$this->assertSame('test', $format);
|
||||
$this->assertArrayHasKey('enable_max_depth', $context);
|
||||
|
||||
$handlerCalled = true;
|
||||
|
||||
return 'handler';
|
||||
};
|
||||
|
||||
$normalizer = $this->getNormalizerForMaxDepth();
|
||||
$normalizer->normalize($level1, 'test', [
|
||||
'enable_max_depth' => true,
|
||||
'max_depth_handler' => $maxDepthHandler,
|
||||
]);
|
||||
|
||||
$this->assertTrue($handlerCalled);
|
||||
}
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests\Normalizer\Features;
|
||||
|
||||
class ObjectDummy
|
||||
{
|
||||
protected $foo;
|
||||
public $bar;
|
||||
private $baz;
|
||||
protected $camelCase;
|
||||
protected $object;
|
||||
|
||||
public function getFoo()
|
||||
{
|
||||
return $this->foo;
|
||||
}
|
||||
|
||||
public function setFoo($foo)
|
||||
{
|
||||
$this->foo = $foo;
|
||||
}
|
||||
|
||||
public function isBaz()
|
||||
{
|
||||
return $this->baz;
|
||||
}
|
||||
|
||||
public function setBaz($baz)
|
||||
{
|
||||
$this->baz = $baz;
|
||||
}
|
||||
|
||||
public function getFooBar()
|
||||
{
|
||||
return $this->foo.$this->bar;
|
||||
}
|
||||
|
||||
public function getCamelCase()
|
||||
{
|
||||
return $this->camelCase;
|
||||
}
|
||||
|
||||
public function setCamelCase($camelCase)
|
||||
{
|
||||
$this->camelCase = $camelCase;
|
||||
}
|
||||
|
||||
public function otherMethod()
|
||||
{
|
||||
throw new \RuntimeException('Dummy::otherMethod() should not be called');
|
||||
}
|
||||
|
||||
public function setObject($object)
|
||||
{
|
||||
$this->object = $object;
|
||||
}
|
||||
|
||||
public function getObject()
|
||||
{
|
||||
return $this->object;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests\Normalizer\Features;
|
||||
|
||||
class ObjectInner
|
||||
{
|
||||
public $foo;
|
||||
public $bar;
|
||||
|
||||
public function getBar()
|
||||
{
|
||||
return $this->bar;
|
||||
}
|
||||
|
||||
public function setBar($bar): void
|
||||
{
|
||||
$this->bar = $bar;
|
||||
}
|
||||
|
||||
public function getFoo()
|
||||
{
|
||||
return $this->foo;
|
||||
}
|
||||
|
||||
public function setFoo($foo): void
|
||||
{
|
||||
$this->foo = $foo;
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests\Normalizer\Features;
|
||||
|
||||
class ObjectOuter
|
||||
{
|
||||
public $foo;
|
||||
public $bar;
|
||||
private $inner;
|
||||
private $date;
|
||||
|
||||
/**
|
||||
* @var ObjectInner[]
|
||||
*/
|
||||
private $inners;
|
||||
|
||||
public function getFoo()
|
||||
{
|
||||
return $this->foo;
|
||||
}
|
||||
|
||||
public function setFoo($foo): void
|
||||
{
|
||||
$this->foo = $foo;
|
||||
}
|
||||
|
||||
public function getBar()
|
||||
{
|
||||
return $this->bar;
|
||||
}
|
||||
|
||||
public function setBar($bar): void
|
||||
{
|
||||
$this->bar = $bar;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ObjectInner
|
||||
*/
|
||||
public function getInner()
|
||||
{
|
||||
return $this->inner;
|
||||
}
|
||||
|
||||
public function setInner(ObjectInner $inner)
|
||||
{
|
||||
$this->inner = $inner;
|
||||
}
|
||||
|
||||
public function setDate(\DateTimeInterface $date)
|
||||
{
|
||||
$this->date = $date;
|
||||
}
|
||||
|
||||
public function getDate()
|
||||
{
|
||||
return $this->date;
|
||||
}
|
||||
|
||||
public function setInners(array $inners)
|
||||
{
|
||||
$this->inners = $inners;
|
||||
}
|
||||
|
||||
public function getInners()
|
||||
{
|
||||
return $this->inners;
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests\Normalizer\Features;
|
||||
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\DeepObjectPopulateChildDummy;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\DeepObjectPopulateParentDummy;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\ProxyDummy;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\ToBeProxyfiedDummy;
|
||||
|
||||
trait ObjectToPopulateTestTrait
|
||||
{
|
||||
abstract protected function getDenormalizerForObjectToPopulate(): DenormalizerInterface;
|
||||
|
||||
public function testObjectToPopulate()
|
||||
{
|
||||
$dummy = new ObjectDummy();
|
||||
$dummy->bar = 'bar';
|
||||
|
||||
$denormalizer = $this->getDenormalizerForObjectToPopulate();
|
||||
|
||||
$obj = $denormalizer->denormalize(
|
||||
['foo' => 'foo'],
|
||||
ObjectDummy::class,
|
||||
null,
|
||||
['object_to_populate' => $dummy]
|
||||
);
|
||||
|
||||
$this->assertEquals($dummy, $obj);
|
||||
$this->assertEquals('foo', $obj->getFoo());
|
||||
$this->assertEquals('bar', $obj->bar);
|
||||
}
|
||||
|
||||
public function testObjectToPopulateWithProxy()
|
||||
{
|
||||
$proxyDummy = new ProxyDummy();
|
||||
|
||||
$context = ['object_to_populate' => $proxyDummy];
|
||||
|
||||
$denormalizer = $this->getDenormalizerForObjectToPopulate();
|
||||
$denormalizer->denormalize(['foo' => 'bar'], ToBeProxyfiedDummy::class, null, $context);
|
||||
|
||||
$this->assertSame('bar', $proxyDummy->getFoo());
|
||||
}
|
||||
|
||||
public function testObjectToPopulateNoMatch()
|
||||
{
|
||||
$this->markTestSkipped('something broken here!');
|
||||
$denormalizer = $this->getDenormalizerForObjectToPopulate();
|
||||
|
||||
$objectToPopulate = new ObjectInner();
|
||||
$objectToPopulate->foo = 'foo';
|
||||
|
||||
$outer = $denormalizer->denormalize([
|
||||
'foo' => 'foo',
|
||||
'inner' => [
|
||||
'bar' => 'bar',
|
||||
],
|
||||
], ObjectOuter::class, null, ['object_to_popuplate' => $objectToPopulate]);
|
||||
|
||||
$this->assertInstanceOf(ObjectOuter::class, $outer);
|
||||
$inner = $outer->getInner();
|
||||
$this->assertInstanceOf(ObjectInner::class, $inner);
|
||||
$this->assertNotSame($objectToPopulate, $inner);
|
||||
$this->assertSame('bar', $inner->bar);
|
||||
$this->assertNull($inner->foo);
|
||||
}
|
||||
|
||||
public function testDeepObjectToPopulate()
|
||||
{
|
||||
$child = new DeepObjectPopulateChildDummy();
|
||||
$child->bar = 'bar-old';
|
||||
$child->foo = 'foo-old';
|
||||
|
||||
$parent = new DeepObjectPopulateParentDummy();
|
||||
$parent->setChild($child);
|
||||
|
||||
$context = [
|
||||
'object_to_populate' => $parent,
|
||||
'deep_object_to_populate' => true,
|
||||
];
|
||||
|
||||
$normalizer = $this->getDenormalizerForObjectToPopulate();
|
||||
|
||||
$newChild = new DeepObjectPopulateChildDummy();
|
||||
$newChild->bar = 'bar-new';
|
||||
$newChild->foo = 'foo-old';
|
||||
|
||||
$normalizer->denormalize([
|
||||
'child' => [
|
||||
'bar' => 'bar-new',
|
||||
],
|
||||
], DeepObjectPopulateParentDummy::class, null, $context);
|
||||
|
||||
$this->assertSame('bar-new', $parent->getChild()->bar);
|
||||
$this->assertSame('foo-old', $parent->getChild()->foo);
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests\Normalizer\Features;
|
||||
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
/**
|
||||
* Test AbstractObjectNormalizer::SKIP_NULL_VALUES.
|
||||
*/
|
||||
trait SkipNullValuesTestTrait
|
||||
{
|
||||
abstract protected function getNormalizerForSkipNullValues(): NormalizerInterface;
|
||||
|
||||
public function testSkipNullValues()
|
||||
{
|
||||
$dummy = new ObjectDummy();
|
||||
$dummy->bar = 'present';
|
||||
|
||||
$normalizer = $this->getNormalizerForSkipNullValues();
|
||||
$result = $normalizer->normalize($dummy, null, ['skip_null_values' => true]);
|
||||
$this->assertSame(['fooBar' => 'present', 'bar' => 'present'], $result);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests\Normalizer\Features;
|
||||
|
||||
class TypeEnforcementNumberObject
|
||||
{
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
public $number;
|
||||
|
||||
public function setNumber($number)
|
||||
{
|
||||
$this->number = $number;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Serializer\Tests\Normalizer\Features;
|
||||
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
|
||||
/**
|
||||
* Test type mismatches with a denormalizer that is aware of types.
|
||||
* Covers AbstractObjectNormalizer::DISABLE_TYPE_ENFORCEMENT.
|
||||
*/
|
||||
trait TypeEnforcementTestTrait
|
||||
{
|
||||
abstract protected function getDenormalizerForTypeEnforcement(): DenormalizerInterface;
|
||||
|
||||
public function testRejectInvalidType()
|
||||
{
|
||||
$denormalizer = $this->getDenormalizerForTypeEnforcement();
|
||||
|
||||
$this->expectException(UnexpectedValueException::class);
|
||||
$this->expectExceptionMessage('The type of the "date" attribute for class "'.ObjectOuter::class.'" must be one of "DateTimeInterface" ("string" given).');
|
||||
$denormalizer->denormalize(['date' => 'foo'], ObjectOuter::class);
|
||||
}
|
||||
|
||||
public function testRejectInvalidKey()
|
||||
{
|
||||
$denormalizer = $this->getDenormalizerForTypeEnforcement();
|
||||
|
||||
$this->expectException(UnexpectedValueException::class);
|
||||
$this->expectExceptionMessage('The type of the key "a" must be "int" ("string" given).');
|
||||
$denormalizer->denormalize(['inners' => ['a' => ['foo' => 1]]], ObjectOuter::class);
|
||||
}
|
||||
|
||||
public function testDoNotRejectInvalidTypeOnDisableTypeEnforcementContextOption()
|
||||
{
|
||||
$denormalizer = $this->getDenormalizerForTypeEnforcement();
|
||||
|
||||
$this->assertSame('foo', $denormalizer->denormalize(
|
||||
['number' => 'foo'],
|
||||
TypeEnforcementNumberObject::class,
|
||||
null,
|
||||
[AbstractObjectNormalizer::DISABLE_TYPE_ENFORCEMENT => true]
|
||||
)->number);
|
||||
}
|
||||
}
|
@ -13,21 +13,45 @@ namespace Symfony\Component\Serializer\Tests\Normalizer;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
|
||||
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
|
||||
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
|
||||
use Symfony\Component\Serializer\Exception\CircularReferenceException;
|
||||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
|
||||
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
|
||||
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
|
||||
use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter;
|
||||
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\CircularReferenceDummy;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\GroupDummy;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\MaxDepthDummy;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\SiblingHolder;
|
||||
use Symfony\Component\Serializer\Tests\Normalizer\Features\CallbacksObject;
|
||||
use Symfony\Component\Serializer\Tests\Normalizer\Features\CallbacksTestTrait;
|
||||
use Symfony\Component\Serializer\Tests\Normalizer\Features\CircularReferenceTestTrait;
|
||||
use Symfony\Component\Serializer\Tests\Normalizer\Features\ConstructorArgumentsTestTrait;
|
||||
use Symfony\Component\Serializer\Tests\Normalizer\Features\GroupsTestTrait;
|
||||
use Symfony\Component\Serializer\Tests\Normalizer\Features\IgnoredAttributesTestTrait;
|
||||
use Symfony\Component\Serializer\Tests\Normalizer\Features\MaxDepthTestTrait;
|
||||
use Symfony\Component\Serializer\Tests\Normalizer\Features\ObjectToPopulateTestTrait;
|
||||
use Symfony\Component\Serializer\Tests\Normalizer\Features\TypeEnforcementTestTrait;
|
||||
|
||||
class GetSetMethodNormalizerTest extends TestCase
|
||||
{
|
||||
use CallbacksTestTrait;
|
||||
use CircularReferenceTestTrait;
|
||||
use ConstructorArgumentsTestTrait;
|
||||
use GroupsTestTrait;
|
||||
use IgnoredAttributesTestTrait;
|
||||
use MaxDepthTestTrait;
|
||||
use ObjectToPopulateTestTrait;
|
||||
use TypeEnforcementTestTrait;
|
||||
|
||||
/**
|
||||
* @var GetSetMethodNormalizer
|
||||
*/
|
||||
@ -89,7 +113,7 @@ class GetSetMethodNormalizerTest extends TestCase
|
||||
{
|
||||
$obj = $this->normalizer->denormalize(
|
||||
['foo' => 'foo', 'bar' => 'bar', 'baz' => true, 'fooBar' => 'foobar'],
|
||||
__NAMESPACE__.'\GetSetDummy',
|
||||
GetSetDummy::class,
|
||||
'any'
|
||||
);
|
||||
$this->assertEquals('foo', $obj->getFoo());
|
||||
@ -119,14 +143,14 @@ class GetSetMethodNormalizerTest extends TestCase
|
||||
$data->foo = 'foo';
|
||||
$data->bar = 'bar';
|
||||
$data->fooBar = 'foobar';
|
||||
$obj = $this->normalizer->denormalize($data, __NAMESPACE__.'\GetSetDummy', 'any');
|
||||
$obj = $this->normalizer->denormalize($data, GetSetDummy::class, 'any');
|
||||
$this->assertEquals('foo', $obj->getFoo());
|
||||
$this->assertEquals('bar', $obj->getBar());
|
||||
}
|
||||
|
||||
public function testDenormalizeNull()
|
||||
{
|
||||
$this->assertEquals(new GetSetDummy(), $this->normalizer->denormalize(null, __NAMESPACE__.'\GetSetDummy'));
|
||||
$this->assertEquals(new GetSetDummy(), $this->normalizer->denormalize(null, GetSetDummy::class));
|
||||
}
|
||||
|
||||
public function testConstructorDenormalize()
|
||||
@ -202,62 +226,90 @@ class GetSetMethodNormalizerTest extends TestCase
|
||||
$this->assertEquals('bar', $obj->getFoo());
|
||||
}
|
||||
|
||||
public function testGroupsNormalize()
|
||||
protected function getNormalizerForCallbacks(): GetSetMethodNormalizer
|
||||
{
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
$this->normalizer = new GetSetMethodNormalizer($classMetadataFactory);
|
||||
$this->normalizer->setSerializer($this->serializer);
|
||||
|
||||
$obj = new GroupDummy();
|
||||
$obj->setFoo('foo');
|
||||
$obj->setBar('bar');
|
||||
$obj->setFooBar('fooBar');
|
||||
$obj->setSymfony('symfony');
|
||||
$obj->setKevin('kevin');
|
||||
$obj->setCoopTilleuls('coopTilleuls');
|
||||
|
||||
$this->assertEquals([
|
||||
'bar' => 'bar',
|
||||
], $this->normalizer->normalize($obj, null, [GetSetMethodNormalizer::GROUPS => ['c']]));
|
||||
|
||||
$this->assertEquals([
|
||||
'symfony' => 'symfony',
|
||||
'foo' => 'foo',
|
||||
'fooBar' => 'fooBar',
|
||||
'bar' => 'bar',
|
||||
'kevin' => 'kevin',
|
||||
'coopTilleuls' => 'coopTilleuls',
|
||||
], $this->normalizer->normalize($obj, null, [GetSetMethodNormalizer::GROUPS => ['a', 'c']]));
|
||||
return new GetSetMethodNormalizer($classMetadataFactory, new MetadataAwareNameConverter($classMetadataFactory));
|
||||
}
|
||||
|
||||
public function testGroupsDenormalize()
|
||||
/**
|
||||
* @dataProvider provideCallbacks
|
||||
*/
|
||||
public function testLegacyCallbacks($callbacks, $value, $result)
|
||||
{
|
||||
$this->normalizer->setCallbacks($callbacks);
|
||||
|
||||
$obj = new CallbacksObject($value);
|
||||
$this->assertEquals(
|
||||
$result,
|
||||
$this->normalizer->normalize($obj, 'any')
|
||||
);
|
||||
}
|
||||
|
||||
protected function getNormalizerForCircularReference(): GetSetMethodNormalizer
|
||||
{
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
$this->normalizer = new GetSetMethodNormalizer($classMetadataFactory);
|
||||
$normalizer = new GetSetMethodNormalizer($classMetadataFactory, new MetadataAwareNameConverter($classMetadataFactory));
|
||||
new Serializer([$normalizer]);
|
||||
|
||||
return $normalizer;
|
||||
}
|
||||
|
||||
protected function getSelfReferencingModel()
|
||||
{
|
||||
return new CircularReferenceDummy();
|
||||
}
|
||||
|
||||
public function testLegacyUnableToNormalizeCircularReference()
|
||||
{
|
||||
$this->normalizer->setCircularReferenceLimit(2);
|
||||
$this->serializer = new Serializer([$this->normalizer]);
|
||||
$this->normalizer->setSerializer($this->serializer);
|
||||
|
||||
$obj = new GroupDummy();
|
||||
$obj->setFoo('foo');
|
||||
$obj = new CircularReferenceDummy();
|
||||
|
||||
$toNormalize = ['foo' => 'foo', 'bar' => 'bar'];
|
||||
$this->expectException(CircularReferenceException::class);
|
||||
$this->normalizer->normalize($obj);
|
||||
}
|
||||
|
||||
$normalized = $this->normalizer->denormalize(
|
||||
$toNormalize,
|
||||
'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy',
|
||||
null,
|
||||
[GetSetMethodNormalizer::GROUPS => ['a']]
|
||||
);
|
||||
$this->assertEquals($obj, $normalized);
|
||||
public function testLegacyCircularReferenceHandler()
|
||||
{
|
||||
$handler = function ($obj) {
|
||||
return \get_class($obj);
|
||||
};
|
||||
|
||||
$obj->setBar('bar');
|
||||
$this->normalizer->setCircularReferenceHandler($handler);
|
||||
$this->serializer = new Serializer([$this->normalizer]);
|
||||
$this->normalizer->setSerializer($this->serializer);
|
||||
|
||||
$normalized = $this->normalizer->denormalize(
|
||||
$toNormalize,
|
||||
'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy',
|
||||
null,
|
||||
[GetSetMethodNormalizer::GROUPS => ['a', 'b']]
|
||||
);
|
||||
$this->assertEquals($obj, $normalized);
|
||||
$obj = new CircularReferenceDummy();
|
||||
|
||||
$expected = ['me' => CircularReferenceDummy::class];
|
||||
$this->assertEquals($expected, $this->normalizer->normalize($obj));
|
||||
}
|
||||
|
||||
protected function getDenormalizerForConstructArguments(): GetSetMethodNormalizer
|
||||
{
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
$denormalizer = new GetSetMethodNormalizer($classMetadataFactory, new MetadataAwareNameConverter($classMetadataFactory));
|
||||
new Serializer([$denormalizer]);
|
||||
|
||||
return $denormalizer;
|
||||
}
|
||||
|
||||
protected function getNormalizerForGroups(): GetSetMethodNormalizer
|
||||
{
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
|
||||
return new GetSetMethodNormalizer($classMetadataFactory);
|
||||
}
|
||||
|
||||
protected function getDenormalizerForGroups(): GetSetMethodNormalizer
|
||||
{
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
|
||||
return new GetSetMethodNormalizer($classMetadataFactory);
|
||||
}
|
||||
|
||||
public function testGroupsNormalizeWithNameConverter()
|
||||
@ -302,74 +354,62 @@ class GetSetMethodNormalizerTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideCallbacks
|
||||
*/
|
||||
public function testCallbacks($callbacks, $value, $result, $message)
|
||||
protected function getNormalizerForMaxDepth(): NormalizerInterface
|
||||
{
|
||||
$this->doTestCallbacks($callbacks, $value, $result, $message);
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
$normalizer = new GetSetMethodNormalizer($classMetadataFactory);
|
||||
$serializer = new Serializer([$normalizer]);
|
||||
$normalizer->setSerializer($serializer);
|
||||
|
||||
return $normalizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideCallbacks
|
||||
*/
|
||||
public function testLegacyCallbacks($callbacks, $value, $result, $message)
|
||||
protected function getDenormalizerForObjectToPopulate(): DenormalizerInterface
|
||||
{
|
||||
$this->doTestCallbacks($callbacks, $value, $result, $message, true);
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
$normalizer = new GetSetMethodNormalizer($classMetadataFactory, null, new PhpDocExtractor());
|
||||
new Serializer([$normalizer]);
|
||||
|
||||
return $normalizer;
|
||||
}
|
||||
|
||||
private function doTestCallbacks($callbacks, $value, $result, $message, bool $legacy = false)
|
||||
protected function getDenormalizerForTypeEnforcement(): DenormalizerInterface
|
||||
{
|
||||
$legacy ? $this->normalizer->setCallbacks($callbacks) : $this->createNormalizer([GetSetMethodNormalizer::CALLBACKS => $callbacks]);
|
||||
$extractor = new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]);
|
||||
$normalizer = new GetSetMethodNormalizer(null, null, $extractor);
|
||||
$serializer = new Serializer([new ArrayDenormalizer(), $normalizer]);
|
||||
$normalizer->setSerializer($serializer);
|
||||
|
||||
$obj = new GetConstructorDummy('', $value, true);
|
||||
$this->assertEquals(
|
||||
$result,
|
||||
$this->normalizer->normalize($obj, 'any'),
|
||||
$message
|
||||
);
|
||||
return $normalizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testUncallableCallbacks()
|
||||
public function testRejectInvalidKey()
|
||||
{
|
||||
$this->doTestUncallableCallbacks();
|
||||
$this->markTestSkipped('This test makes no sense with the GetSetMethodNormalizer');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testLegacyUncallableCallbacks()
|
||||
protected function getNormalizerForIgnoredAttributes(): GetSetMethodNormalizer
|
||||
{
|
||||
$this->doTestUncallableCallbacks(true);
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
$normalizer = new GetSetMethodNormalizer($classMetadataFactory, null, new PhpDocExtractor());
|
||||
new Serializer([$normalizer]);
|
||||
|
||||
return $normalizer;
|
||||
}
|
||||
|
||||
private function doTestUncallableCallbacks(bool $legacy = false)
|
||||
protected function getDenormalizerForIgnoredAttributes(): GetSetMethodNormalizer
|
||||
{
|
||||
$callbacks = ['bar' => null];
|
||||
$legacy ? $this->normalizer->setCallbacks($callbacks) : $this->createNormalizer([GetSetMethodNormalizer::CALLBACKS => $callbacks]);
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
$normalizer = new GetSetMethodNormalizer($classMetadataFactory, null, new PhpDocExtractor());
|
||||
new Serializer([$normalizer]);
|
||||
|
||||
$obj = new GetConstructorDummy('baz', 'quux', true);
|
||||
|
||||
$this->normalizer->normalize($obj, 'any');
|
||||
}
|
||||
|
||||
public function testIgnoredAttributes()
|
||||
{
|
||||
$this->doTestIgnoredAttributes();
|
||||
return $normalizer;
|
||||
}
|
||||
|
||||
public function testLegacyIgnoredAttributes()
|
||||
{
|
||||
$this->doTestIgnoredAttributes(true);
|
||||
}
|
||||
|
||||
private function doTestIgnoredAttributes(bool $legacy = false)
|
||||
{
|
||||
$ignoredAttributes = ['foo', 'bar', 'baz', 'camelCase', 'object'];
|
||||
$legacy ? $this->normalizer->setIgnoredAttributes($ignoredAttributes) : $this->createNormalizer([GetSetMethodNormalizer::IGNORED_ATTRIBUTES => $ignoredAttributes]);
|
||||
$this->normalizer->setIgnoredAttributes($ignoredAttributes);
|
||||
|
||||
$obj = new GetSetDummy();
|
||||
$obj->setFoo('foo');
|
||||
@ -382,66 +422,6 @@ class GetSetMethodNormalizerTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function provideCallbacks()
|
||||
{
|
||||
return [
|
||||
[
|
||||
[
|
||||
'bar' => function ($bar) {
|
||||
return 'baz';
|
||||
},
|
||||
],
|
||||
'baz',
|
||||
['foo' => '', 'bar' => 'baz', 'baz' => true],
|
||||
'Change a string',
|
||||
],
|
||||
[
|
||||
[
|
||||
'bar' => function ($bar) {
|
||||
},
|
||||
],
|
||||
'baz',
|
||||
['foo' => '', 'bar' => null, 'baz' => true],
|
||||
'Null an item',
|
||||
],
|
||||
[
|
||||
[
|
||||
'bar' => function ($bar) {
|
||||
return $bar->format('d-m-Y H:i:s');
|
||||
},
|
||||
],
|
||||
new \DateTime('2011-09-10 06:30:00'),
|
||||
['foo' => '', 'bar' => '10-09-2011 06:30:00', 'baz' => true],
|
||||
'Format a date',
|
||||
],
|
||||
[
|
||||
[
|
||||
'bar' => function ($bars) {
|
||||
$foos = '';
|
||||
foreach ($bars as $bar) {
|
||||
$foos .= $bar->getFoo();
|
||||
}
|
||||
|
||||
return $foos;
|
||||
},
|
||||
],
|
||||
[new GetConstructorDummy('baz', '', false), new GetConstructorDummy('quux', '', false)],
|
||||
['foo' => '', 'bar' => 'bazquux', 'baz' => true],
|
||||
'Collect a property',
|
||||
],
|
||||
[
|
||||
[
|
||||
'bar' => function ($bars) {
|
||||
return \count($bars);
|
||||
},
|
||||
],
|
||||
[new GetConstructorDummy('baz', '', false), new GetConstructorDummy('quux', '', false)],
|
||||
['foo' => '', 'bar' => 2, 'baz' => true],
|
||||
'Count a property',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Serializer\Exception\LogicException
|
||||
* @expectedExceptionMessage Cannot normalize attribute "object" because the injected serializer is not a normalizer
|
||||
@ -458,32 +438,6 @@ class GetSetMethodNormalizerTest extends TestCase
|
||||
$this->normalizer->normalize($obj, 'any');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Serializer\Exception\CircularReferenceException
|
||||
*/
|
||||
public function testUnableToNormalizeCircularReference()
|
||||
{
|
||||
$this->doTestUnableToNormalizeCircularReference();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Serializer\Exception\CircularReferenceException
|
||||
*/
|
||||
public function testLegacyUnableToNormalizeCircularReference()
|
||||
{
|
||||
$this->doTestUnableToNormalizeCircularReference(true);
|
||||
}
|
||||
|
||||
private function doTestUnableToNormalizeCircularReference(bool $legacy = false)
|
||||
{
|
||||
$legacy ? $this->normalizer->setCircularReferenceLimit(2) : $this->createNormalizer([GetSetMethodNormalizer::CIRCULAR_REFERENCE_LIMIT => 2]);
|
||||
$this->serializer = new Serializer([$this->normalizer]);
|
||||
$this->normalizer->setSerializer($this->serializer);
|
||||
|
||||
$obj = new CircularReferenceDummy();
|
||||
$this->normalizer->normalize($obj);
|
||||
}
|
||||
|
||||
public function testSiblingReference()
|
||||
{
|
||||
$serializer = new Serializer([$this->normalizer]);
|
||||
@ -499,60 +453,17 @@ class GetSetMethodNormalizerTest extends TestCase
|
||||
$this->assertEquals($expected, $this->normalizer->normalize($siblingHolder));
|
||||
}
|
||||
|
||||
public function testCircularReferenceHandler()
|
||||
{
|
||||
$this->doTestCircularReferenceHandler();
|
||||
}
|
||||
|
||||
public function testLegacyCircularReferenceHandler()
|
||||
{
|
||||
$this->doTestCircularReferenceHandler(true);
|
||||
}
|
||||
|
||||
private function doTestCircularReferenceHandler(bool $legacy = false)
|
||||
{
|
||||
$handler = function ($obj) {
|
||||
return \get_class($obj);
|
||||
};
|
||||
|
||||
$legacy ? $this->normalizer->setCircularReferenceHandler($handler) : $this->createNormalizer([GetSetMethodNormalizer::CIRCULAR_REFERENCE_HANDLER => $handler]);
|
||||
$this->serializer = new Serializer([$this->normalizer]);
|
||||
$this->normalizer->setSerializer($this->serializer);
|
||||
|
||||
$obj = new CircularReferenceDummy();
|
||||
|
||||
$expected = ['me' => 'Symfony\Component\Serializer\Tests\Fixtures\CircularReferenceDummy'];
|
||||
$this->assertEquals($expected, $this->normalizer->normalize($obj));
|
||||
}
|
||||
|
||||
public function testObjectToPopulate()
|
||||
{
|
||||
$dummy = new GetSetDummy();
|
||||
$dummy->setFoo('foo');
|
||||
|
||||
$obj = $this->normalizer->denormalize(
|
||||
['bar' => 'bar'],
|
||||
__NAMESPACE__.'\GetSetDummy',
|
||||
null,
|
||||
[GetSetMethodNormalizer::OBJECT_TO_POPULATE => $dummy]
|
||||
);
|
||||
|
||||
$this->assertEquals($dummy, $obj);
|
||||
$this->assertEquals('foo', $obj->getFoo());
|
||||
$this->assertEquals('bar', $obj->getBar());
|
||||
}
|
||||
|
||||
public function testDenormalizeNonExistingAttribute()
|
||||
{
|
||||
$this->assertEquals(
|
||||
new GetSetDummy(),
|
||||
$this->normalizer->denormalize(['non_existing' => true], __NAMESPACE__.'\GetSetDummy')
|
||||
$this->normalizer->denormalize(['non_existing' => true], GetSetDummy::class)
|
||||
);
|
||||
}
|
||||
|
||||
public function testDenormalizeShouldNotSetStaticAttribute()
|
||||
{
|
||||
$obj = $this->normalizer->denormalize(['staticObject' => true], __NAMESPACE__.'\GetSetDummy');
|
||||
$obj = $this->normalizer->denormalize(['staticObject' => true], GetSetDummy::class);
|
||||
|
||||
$this->assertEquals(new GetSetDummy(), $obj);
|
||||
$this->assertNull(GetSetDummy::getStaticObject());
|
||||
@ -590,46 +501,6 @@ class GetSetMethodNormalizerTest extends TestCase
|
||||
$this->normalizer->normalize($obj, 'any')
|
||||
);
|
||||
}
|
||||
|
||||
public function testMaxDepth()
|
||||
{
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
$this->normalizer = new GetSetMethodNormalizer($classMetadataFactory);
|
||||
$serializer = new Serializer([$this->normalizer]);
|
||||
$this->normalizer->setSerializer($serializer);
|
||||
|
||||
$level1 = new MaxDepthDummy();
|
||||
$level1->bar = 'level1';
|
||||
|
||||
$level2 = new MaxDepthDummy();
|
||||
$level2->bar = 'level2';
|
||||
$level1->child = $level2;
|
||||
|
||||
$level3 = new MaxDepthDummy();
|
||||
$level3->bar = 'level3';
|
||||
$level2->child = $level3;
|
||||
|
||||
$level4 = new MaxDepthDummy();
|
||||
$level4->bar = 'level4';
|
||||
$level3->child = $level4;
|
||||
|
||||
$result = $serializer->normalize($level1, null, [GetSetMethodNormalizer::ENABLE_MAX_DEPTH => true]);
|
||||
|
||||
$expected = [
|
||||
'bar' => 'level1',
|
||||
'child' => [
|
||||
'bar' => 'level2',
|
||||
'child' => [
|
||||
'bar' => 'level3',
|
||||
'child' => [
|
||||
'child' => null,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
}
|
||||
|
||||
class GetSetDummy
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -13,24 +13,50 @@ namespace Symfony\Component\Serializer\Tests\Normalizer;
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
|
||||
use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor;
|
||||
use Symfony\Component\PropertyInfo\PropertyInfoExtractor;
|
||||
use Symfony\Component\Serializer\Exception\CircularReferenceException;
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
|
||||
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
|
||||
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
|
||||
use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter;
|
||||
use Symfony\Component\Serializer\Normalizer\ArrayDenormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
use Symfony\Component\Serializer\SerializerInterface;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\GroupDummy;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\GroupDummyChild;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\MaxDepthDummy;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\PropertyCircularReferenceDummy;
|
||||
use Symfony\Component\Serializer\Tests\Fixtures\PropertySiblingHolder;
|
||||
use Symfony\Component\Serializer\Tests\Normalizer\Features\CallbacksObject;
|
||||
use Symfony\Component\Serializer\Tests\Normalizer\Features\CallbacksTestTrait;
|
||||
use Symfony\Component\Serializer\Tests\Normalizer\Features\CircularReferenceTestTrait;
|
||||
use Symfony\Component\Serializer\Tests\Normalizer\Features\ConstructorArgumentsTestTrait;
|
||||
use Symfony\Component\Serializer\Tests\Normalizer\Features\GroupsTestTrait;
|
||||
use Symfony\Component\Serializer\Tests\Normalizer\Features\IgnoredAttributesTestTrait;
|
||||
use Symfony\Component\Serializer\Tests\Normalizer\Features\MaxDepthTestTrait;
|
||||
use Symfony\Component\Serializer\Tests\Normalizer\Features\ObjectToPopulateTestTrait;
|
||||
use Symfony\Component\Serializer\Tests\Normalizer\Features\TypeEnforcementTestTrait;
|
||||
|
||||
class PropertyNormalizerTest extends TestCase
|
||||
{
|
||||
use CallbacksTestTrait;
|
||||
use CircularReferenceTestTrait;
|
||||
use ConstructorArgumentsTestTrait;
|
||||
use GroupsTestTrait;
|
||||
use IgnoredAttributesTestTrait;
|
||||
use MaxDepthTestTrait;
|
||||
use ObjectToPopulateTestTrait;
|
||||
use TypeEnforcementTestTrait;
|
||||
|
||||
/**
|
||||
* @var PropertyNormalizer
|
||||
*/
|
||||
private $normalizer;
|
||||
|
||||
/**
|
||||
* @var SerializerInterface
|
||||
*/
|
||||
@ -121,146 +147,111 @@ class PropertyNormalizerTest extends TestCase
|
||||
$this->assertEquals('bar', $obj->getBar());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideCallbacks
|
||||
*/
|
||||
public function testCallbacks($callbacks, $value, $result, $message)
|
||||
protected function getNormalizerForCallbacks(): PropertyNormalizer
|
||||
{
|
||||
$this->doTestCallbacks($callbacks, $value, $result, $message);
|
||||
return new PropertyNormalizer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideCallbacks
|
||||
*/
|
||||
public function testLegacyCallbacks($callbacks, $value, $result, $message)
|
||||
public function testLegacyCallbacks($callbacks, $value, $result)
|
||||
{
|
||||
$this->doTestCallbacks($callbacks, $value, $result, $message, true);
|
||||
}
|
||||
$this->normalizer->setCallbacks($callbacks);
|
||||
|
||||
private function doTestCallbacks($callbacks, $value, $result, $message, bool $legacy = false)
|
||||
{
|
||||
$legacy ? $this->normalizer->setCallbacks($callbacks) : $this->createNormalizer([PropertyNormalizer::CALLBACKS => $callbacks]);
|
||||
|
||||
$obj = new PropertyConstructorDummy('', $value);
|
||||
$obj = new CallbacksObject($value);
|
||||
|
||||
$this->assertEquals(
|
||||
$result,
|
||||
$this->normalizer->normalize($obj, 'any'),
|
||||
$message
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testUncallableCallbacks()
|
||||
{
|
||||
$this->doTestUncallableCallbacks();
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testLegacyUncallableCallbacks()
|
||||
{
|
||||
$this->doTestUncallableCallbacks(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
private function doTestUncallableCallbacks(bool $legacy = false)
|
||||
{
|
||||
$callbacks = ['bar' => null];
|
||||
$legacy ? $this->normalizer->setCallbacks($callbacks) : $this->createNormalizer([PropertyNormalizer::CALLBACKS => $callbacks]);
|
||||
|
||||
$obj = new PropertyConstructorDummy('baz', 'quux');
|
||||
|
||||
$this->normalizer->normalize($obj, 'any');
|
||||
}
|
||||
|
||||
public function testIgnoredAttributes()
|
||||
{
|
||||
$this->doTestIgnoredAttributes();
|
||||
}
|
||||
|
||||
public function testLegacyIgnoredAttributes()
|
||||
{
|
||||
$this->doTestIgnoredAttributes(true);
|
||||
}
|
||||
|
||||
private function doTestIgnoredAttributes(bool $legacy = false)
|
||||
{
|
||||
$ignoredAttributes = ['foo', 'bar', 'camelCase'];
|
||||
$legacy ? $this->normalizer->setIgnoredAttributes($ignoredAttributes) : $this->createNormalizer([PropertyNormalizer::IGNORED_ATTRIBUTES => $ignoredAttributes]);
|
||||
|
||||
$obj = new PropertyDummy();
|
||||
$obj->foo = 'foo';
|
||||
$obj->setBar('bar');
|
||||
|
||||
$this->assertEquals(
|
||||
[],
|
||||
$this->normalizer->normalize($obj, 'any')
|
||||
);
|
||||
}
|
||||
|
||||
public function testGroupsNormalize()
|
||||
/**
|
||||
* @dataProvider provideInvalidCallbacks
|
||||
*/
|
||||
public function testLegacyUncallableCallbacks($callbacks)
|
||||
{
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
$this->normalizer = new PropertyNormalizer($classMetadataFactory);
|
||||
$this->normalizer->setSerializer($this->serializer);
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
|
||||
$obj = new GroupDummy();
|
||||
$obj->setFoo('foo');
|
||||
$obj->setBar('bar');
|
||||
$obj->setFooBar('fooBar');
|
||||
$obj->setSymfony('symfony');
|
||||
$obj->setKevin('kevin');
|
||||
$obj->setCoopTilleuls('coopTilleuls');
|
||||
|
||||
$this->assertEquals([
|
||||
'bar' => 'bar',
|
||||
], $this->normalizer->normalize($obj, null, [PropertyNormalizer::GROUPS => ['c']]));
|
||||
|
||||
// The PropertyNormalizer is also able to hydrate properties from parent classes
|
||||
$this->assertEquals([
|
||||
'symfony' => 'symfony',
|
||||
'foo' => 'foo',
|
||||
'fooBar' => 'fooBar',
|
||||
'bar' => 'bar',
|
||||
'kevin' => 'kevin',
|
||||
'coopTilleuls' => 'coopTilleuls',
|
||||
], $this->normalizer->normalize($obj, null, [PropertyNormalizer::GROUPS => ['a', 'c']]));
|
||||
$this->normalizer->setCallbacks($callbacks);
|
||||
}
|
||||
|
||||
public function testGroupsDenormalize()
|
||||
protected function getNormalizerForCircularReference(): PropertyNormalizer
|
||||
{
|
||||
$normalizer = new PropertyNormalizer();
|
||||
new Serializer([$normalizer]);
|
||||
|
||||
return $normalizer;
|
||||
}
|
||||
|
||||
protected function getSelfReferencingModel()
|
||||
{
|
||||
return new PropertyCircularReferenceDummy();
|
||||
}
|
||||
|
||||
public function testLegacyUnableToNormalizeCircularReference()
|
||||
{
|
||||
$this->normalizer->setCircularReferenceLimit(2);
|
||||
new Serializer([$this->normalizer]);
|
||||
|
||||
$obj = new PropertyCircularReferenceDummy();
|
||||
|
||||
$this->expectException(CircularReferenceException::class);
|
||||
$this->normalizer->normalize($obj);
|
||||
}
|
||||
|
||||
public function testSiblingReference()
|
||||
{
|
||||
$serializer = new Serializer([$this->normalizer]);
|
||||
$this->normalizer->setSerializer($serializer);
|
||||
|
||||
$siblingHolder = new PropertySiblingHolder();
|
||||
|
||||
$expected = [
|
||||
'sibling0' => ['coopTilleuls' => 'Les-Tilleuls.coop'],
|
||||
'sibling1' => ['coopTilleuls' => 'Les-Tilleuls.coop'],
|
||||
'sibling2' => ['coopTilleuls' => 'Les-Tilleuls.coop'],
|
||||
];
|
||||
$this->assertEquals($expected, $this->normalizer->normalize($siblingHolder));
|
||||
}
|
||||
|
||||
public function testLegacyCircularReferenceHandler()
|
||||
{
|
||||
$this->normalizer->setCircularReferenceHandler(function ($obj) {
|
||||
return \get_class($obj);
|
||||
});
|
||||
|
||||
new Serializer([$this->normalizer]);
|
||||
|
||||
$obj = new PropertyCircularReferenceDummy();
|
||||
|
||||
$expected = ['me' => PropertyCircularReferenceDummy::class];
|
||||
$this->assertEquals($expected, $this->normalizer->normalize($obj));
|
||||
}
|
||||
|
||||
protected function getDenormalizerForConstructArguments(): PropertyNormalizer
|
||||
{
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
$this->normalizer = new PropertyNormalizer($classMetadataFactory);
|
||||
$this->normalizer->setSerializer($this->serializer);
|
||||
$denormalizer = new PropertyNormalizer($classMetadataFactory, new MetadataAwareNameConverter($classMetadataFactory));
|
||||
$serializer = new Serializer([$denormalizer]);
|
||||
$denormalizer->setSerializer($serializer);
|
||||
|
||||
$obj = new GroupDummy();
|
||||
$obj->setFoo('foo');
|
||||
return $denormalizer;
|
||||
}
|
||||
|
||||
$toNormalize = ['foo' => 'foo', 'bar' => 'bar'];
|
||||
protected function getNormalizerForGroups(): PropertyNormalizer
|
||||
{
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
|
||||
$normalized = $this->normalizer->denormalize(
|
||||
$toNormalize,
|
||||
'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy',
|
||||
null,
|
||||
[PropertyNormalizer::GROUPS => ['a']]
|
||||
);
|
||||
$this->assertEquals($obj, $normalized);
|
||||
return new PropertyNormalizer($classMetadataFactory);
|
||||
}
|
||||
|
||||
$obj->setBar('bar');
|
||||
protected function getDenormalizerForGroups(): PropertyNormalizer
|
||||
{
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
|
||||
$normalized = $this->normalizer->denormalize(
|
||||
$toNormalize,
|
||||
'Symfony\Component\Serializer\Tests\Fixtures\GroupDummy',
|
||||
null,
|
||||
[PropertyNormalizer::GROUPS => ['a', 'b']]
|
||||
);
|
||||
$this->assertEquals($obj, $normalized);
|
||||
return new PropertyNormalizer($classMetadataFactory);
|
||||
}
|
||||
|
||||
public function testGroupsNormalizeWithNameConverter()
|
||||
@ -305,132 +296,71 @@ class PropertyNormalizerTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function provideCallbacks()
|
||||
protected function getDenormalizerForIgnoredAttributes(): PropertyNormalizer
|
||||
{
|
||||
return [
|
||||
[
|
||||
[
|
||||
'bar' => function ($bar) {
|
||||
return 'baz';
|
||||
},
|
||||
],
|
||||
'baz',
|
||||
['foo' => '', 'bar' => 'baz'],
|
||||
'Change a string',
|
||||
],
|
||||
[
|
||||
[
|
||||
'bar' => function ($bar) {
|
||||
},
|
||||
],
|
||||
'baz',
|
||||
['foo' => '', 'bar' => null],
|
||||
'Null an item',
|
||||
],
|
||||
[
|
||||
[
|
||||
'bar' => function ($bar) {
|
||||
return $bar->format('d-m-Y H:i:s');
|
||||
},
|
||||
],
|
||||
new \DateTime('2011-09-10 06:30:00'),
|
||||
['foo' => '', 'bar' => '10-09-2011 06:30:00'],
|
||||
'Format a date',
|
||||
],
|
||||
[
|
||||
[
|
||||
'bar' => function ($bars) {
|
||||
$foos = '';
|
||||
foreach ($bars as $bar) {
|
||||
$foos .= $bar->getFoo();
|
||||
}
|
||||
$normalizer = new PropertyNormalizer();
|
||||
// instantiate a serializer with the normalizer to handle normalizing recursive structures
|
||||
new Serializer([$normalizer]);
|
||||
|
||||
return $foos;
|
||||
},
|
||||
],
|
||||
[new PropertyConstructorDummy('baz', ''), new PropertyConstructorDummy('quux', '')],
|
||||
['foo' => '', 'bar' => 'bazquux'],
|
||||
'Collect a property',
|
||||
],
|
||||
[
|
||||
[
|
||||
'bar' => function ($bars) {
|
||||
return \count($bars);
|
||||
},
|
||||
],
|
||||
[new PropertyConstructorDummy('baz', ''), new PropertyConstructorDummy('quux', '')],
|
||||
['foo' => '', 'bar' => 2],
|
||||
'Count a property',
|
||||
],
|
||||
];
|
||||
return $normalizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Serializer\Exception\CircularReferenceException
|
||||
*/
|
||||
public function testUnableToNormalizeCircularReference()
|
||||
protected function getNormalizerForIgnoredAttributes(): PropertyNormalizer
|
||||
{
|
||||
$this->doTestUnableToNormalizeCircularReference();
|
||||
$normalizer = new PropertyNormalizer();
|
||||
// instantiate a serializer with the normalizer to handle normalizing recursive structures
|
||||
new Serializer([$normalizer]);
|
||||
|
||||
return $normalizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Symfony\Component\Serializer\Exception\CircularReferenceException
|
||||
*/
|
||||
public function testLegacyUnableToNormalizeCircularReference()
|
||||
public function testIgnoredAttributesContextDenormalizeInherit()
|
||||
{
|
||||
$this->doTestUnableToNormalizeCircularReference(true);
|
||||
$this->markTestSkipped('This has not been tested previously - did not manage to make the test work');
|
||||
}
|
||||
|
||||
private function doTestUnableToNormalizeCircularReference(bool $legacy = false)
|
||||
public function testLegacyIgnoredAttributes()
|
||||
{
|
||||
$legacy ? $this->normalizer->setCircularReferenceLimit(2) : $this->createNormalizer([PropertyNormalizer::CIRCULAR_REFERENCE_LIMIT => 2]);
|
||||
$this->serializer = new Serializer([$this->normalizer]);
|
||||
$this->normalizer->setSerializer($this->serializer);
|
||||
$ignoredAttributes = ['foo', 'bar', 'camelCase'];
|
||||
$this->normalizer->setIgnoredAttributes($ignoredAttributes);
|
||||
|
||||
$obj = new PropertyCircularReferenceDummy();
|
||||
$obj = new PropertyDummy();
|
||||
$obj->foo = 'foo';
|
||||
$obj->setBar('bar');
|
||||
|
||||
$this->normalizer->normalize($obj);
|
||||
$this->assertEquals(
|
||||
[],
|
||||
$this->normalizer->normalize($obj, 'any')
|
||||
);
|
||||
}
|
||||
|
||||
public function testSiblingReference()
|
||||
protected function getNormalizerForMaxDepth(): PropertyNormalizer
|
||||
{
|
||||
$serializer = new Serializer([$this->normalizer]);
|
||||
$this->normalizer->setSerializer($serializer);
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
$normalizer = new PropertyNormalizer($classMetadataFactory);
|
||||
$serializer = new Serializer([$normalizer]);
|
||||
$normalizer->setSerializer($serializer);
|
||||
|
||||
$siblingHolder = new PropertySiblingHolder();
|
||||
|
||||
$expected = [
|
||||
'sibling0' => ['coopTilleuls' => 'Les-Tilleuls.coop'],
|
||||
'sibling1' => ['coopTilleuls' => 'Les-Tilleuls.coop'],
|
||||
'sibling2' => ['coopTilleuls' => 'Les-Tilleuls.coop'],
|
||||
];
|
||||
$this->assertEquals($expected, $this->normalizer->normalize($siblingHolder));
|
||||
return $normalizer;
|
||||
}
|
||||
|
||||
public function testCircularReferenceHandler()
|
||||
protected function getDenormalizerForObjectToPopulate(): PropertyNormalizer
|
||||
{
|
||||
$this->doTestCircularReferenceHandler();
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
$normalizer = new PropertyNormalizer($classMetadataFactory, null, new PhpDocExtractor());
|
||||
new Serializer([$normalizer]);
|
||||
|
||||
return $normalizer;
|
||||
}
|
||||
|
||||
public function testLegacyCircularReferenceHandler()
|
||||
protected function getDenormalizerForTypeEnforcement(): DenormalizerInterface
|
||||
{
|
||||
$this->doTestCircularReferenceHandler(true);
|
||||
}
|
||||
$extractor = new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]);
|
||||
$normalizer = new PropertyNormalizer(null, null, $extractor);
|
||||
$serializer = new Serializer([new ArrayDenormalizer(), $normalizer]);
|
||||
$normalizer->setSerializer($serializer);
|
||||
|
||||
private function doTestCircularReferenceHandler(bool $legacy = false)
|
||||
{
|
||||
$handler = function ($obj) {
|
||||
return \get_class($obj);
|
||||
};
|
||||
$legacy ? $this->normalizer->setCircularReferenceHandler($handler) : $this->createNormalizer([PropertyNormalizer::CIRCULAR_REFERENCE_HANDLER => $handler]);
|
||||
|
||||
$this->serializer = new Serializer([$this->normalizer]);
|
||||
$this->normalizer->setSerializer($this->serializer);
|
||||
|
||||
$obj = new PropertyCircularReferenceDummy();
|
||||
|
||||
$expected = ['me' => 'Symfony\Component\Serializer\Tests\Fixtures\PropertyCircularReferenceDummy'];
|
||||
$this->assertEquals($expected, $this->normalizer->normalize($obj));
|
||||
return $normalizer;
|
||||
}
|
||||
|
||||
public function testDenormalizeNonExistingAttribute()
|
||||
@ -475,42 +405,6 @@ class PropertyNormalizerTest extends TestCase
|
||||
$this->assertFalse($this->normalizer->supportsNormalization(new StaticPropertyDummy()));
|
||||
}
|
||||
|
||||
public function testMaxDepth()
|
||||
{
|
||||
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
|
||||
$this->normalizer = new PropertyNormalizer($classMetadataFactory);
|
||||
$serializer = new Serializer([$this->normalizer]);
|
||||
$this->normalizer->setSerializer($serializer);
|
||||
|
||||
$level1 = new MaxDepthDummy();
|
||||
$level1->foo = 'level1';
|
||||
|
||||
$level2 = new MaxDepthDummy();
|
||||
$level2->foo = 'level2';
|
||||
$level1->child = $level2;
|
||||
|
||||
$level3 = new MaxDepthDummy();
|
||||
$level3->foo = 'level3';
|
||||
$level2->child = $level3;
|
||||
|
||||
$result = $serializer->normalize($level1, null, [PropertyNormalizer::ENABLE_MAX_DEPTH => true]);
|
||||
|
||||
$expected = [
|
||||
'foo' => 'level1',
|
||||
'child' => [
|
||||
'foo' => 'level2',
|
||||
'child' => [
|
||||
'child' => null,
|
||||
'bar' => null,
|
||||
],
|
||||
'bar' => null,
|
||||
],
|
||||
'bar' => null,
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
public function testInheritedPropertiesSupport()
|
||||
{
|
||||
$this->assertTrue($this->normalizer->supportsNormalization(new PropertyChildDummy()));
|
||||
|
Reference in New Issue
Block a user