Skip to content

Commit

Permalink
Merge branch '6.4' into 7.0
Browse files Browse the repository at this point in the history
* 6.4: (29 commits)
  fix tests
  add missing method
  fix merge
  fix test
  fix merge
  fix test
  change test to use a real ObjectManager
  [Mailer] Document the usage of custom headers in Infobip bridge
  [SecurityBundle] Add `provider` XML attribute to the authenticators it’s missing from
  [DoctrineBridge] Test reset with a true manager
  Sync php-cs-fixer config file with 7.2
  [HttpClient] Fix parsing SSE
  [Notifier] Fix thread key in GoogleChat bridge
  [HttpKernel][Security] Fix accessing session for stateless request
  [Serializer] Fix `ObjectNormalizer` with property path
  test handling of special "value" constraint option
  [PhpUnitBridge] Add missing import
  [FrameworkBundle] Fix setting default context for certain normalizers
  [57251] Missing translations for Romanian (ro)
  [ErrorHandler] Fix rendered exception code highlighting on PHP 8.3
  ...
  • Loading branch information
xabbuh committed Jun 22, 2024
2 parents 267299e + 240a7bb commit 1459271
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 6 deletions.
18 changes: 16 additions & 2 deletions Normalizer/AbstractObjectNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -552,8 +552,22 @@ private function validateAndDenormalize(array $types, string $currentClass, stri
return $data;
}

if (('is_'.$builtinType)($data)) {
return $data;
switch ($builtinType) {
case Type::BUILTIN_TYPE_ARRAY:
case Type::BUILTIN_TYPE_BOOL:
case Type::BUILTIN_TYPE_CALLABLE:
case Type::BUILTIN_TYPE_FLOAT:
case Type::BUILTIN_TYPE_INT:
case Type::BUILTIN_TYPE_ITERABLE:
case Type::BUILTIN_TYPE_NULL:
case Type::BUILTIN_TYPE_OBJECT:
case Type::BUILTIN_TYPE_RESOURCE:
case Type::BUILTIN_TYPE_STRING:
if (('is_'.$builtinType)($data)) {
return $data;
}

break;
}
} catch (NotNormalizableValueException|InvalidArgumentException $e) {
if (!$isUnionType && !$isNullable) {
Expand Down
10 changes: 7 additions & 3 deletions Normalizer/ObjectNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -177,15 +177,19 @@ protected function isAllowedAttribute($classOrObject, string $attribute, ?string

if ($context['_read_attributes'] ?? true) {
if (!isset(self::$isReadableCache[$class.$attribute])) {
self::$isReadableCache[$class.$attribute] = $this->propertyInfoExtractor->isReadable($class, $attribute) || $this->hasAttributeAccessorMethod($class, $attribute);
self::$isReadableCache[$class.$attribute] = (\is_object($classOrObject) && $this->propertyAccessor->isReadable($classOrObject, $attribute)) || $this->propertyInfoExtractor->isReadable($class, $attribute) || $this->hasAttributeAccessorMethod($class, $attribute);
}

return self::$isReadableCache[$class.$attribute];
}

if (!isset(self::$isWritableCache[$class.$attribute])) {
self::$isWritableCache[$class.$attribute] = $this->propertyInfoExtractor->isWritable($class, $attribute)
|| (($writeInfo = $this->writeInfoExtractor->getWriteInfo($class, $attribute)) && PropertyWriteInfo::TYPE_NONE !== $writeInfo->getType());
if (str_contains($attribute, '.')) {
self::$isWritableCache[$class.$attribute] = true;
} else {
self::$isWritableCache[$class.$attribute] = $this->propertyInfoExtractor->isWritable($class, $attribute)
|| (($writeInfo = $this->writeInfoExtractor->getWriteInfo($class, $attribute)) && PropertyWriteInfo::TYPE_NONE !== $writeInfo->getType());
}
}

return self::$isWritableCache[$class.$attribute];
Expand Down
2 changes: 1 addition & 1 deletion Tests/DeserializeNestedArrayOfObjectsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ class ZooWithKeyTypes
public $animalsString = [];
/** @var array<int|string, Animal> */
public $animalsUnion = [];
/** @var \stdClass<Animal> */
/** @var \Traversable<Animal> */
public $animalsGenerics = [];
}

Expand Down
5 changes: 5 additions & 0 deletions Tests/Fixtures/property-path-mapping.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Symfony\Component\Serializer\Tests\Normalizer\ObjectOuter:
attributes:
inner.foo:
serialized_name: inner_foo
groups: [ 'read' ]
62 changes: 62 additions & 0 deletions Tests/Normalizer/AbstractObjectNormalizerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1076,6 +1076,26 @@ public function testNormalizationWithMaxDepthOnStdclassObjectDoesNotThrowWarning

$this->assertSame(['string' => 'yes'], $normalized);
}

/**
* @dataProvider provideBooleanTypesData
*/
public function testDenormalizeBooleanTypesWithNotMatchingData(array $data, string $type)
{
$normalizer = new AbstractObjectNormalizerWithMetadataAndPropertyTypeExtractors();

$this->expectException(NotNormalizableValueException::class);

$normalizer->denormalize($data, $type);
}

public function provideBooleanTypesData()
{
return [
[['foo' => true], FalsePropertyDummy::class],
[['foo' => false], TruePropertyDummy::class],
];
}
}

class AbstractObjectNormalizerDummy extends AbstractObjectNormalizer
Expand Down Expand Up @@ -1344,6 +1364,18 @@ class XmlScalarDummy
public $value;
}

class FalsePropertyDummy
{
/** @var false */
public $foo;
}

class TruePropertyDummy
{
/** @var true */
public $foo;
}

class SerializerCollectionDummy implements SerializerInterface, DenormalizerInterface
{
private array $normalizers;
Expand Down Expand Up @@ -1487,3 +1519,33 @@ public function __construct(
) {
}
}

class AbstractObjectNormalizerWithMetadataAndPropertyTypeExtractors extends AbstractObjectNormalizer
{
public function __construct()
{
parent::__construct(new ClassMetadataFactory(new AttributeLoader()), null, new PropertyInfoExtractor([], [new PhpDocExtractor(), new ReflectionExtractor()]));
}

public function getSupportedTypes(?string $format): array
{
return ['*' => false];
}

protected function extractAttributes(object $object, ?string $format = null, array $context = []): array
{
return [];
}

protected function getAttributeValue(object $object, string $attribute, ?string $format = null, array $context = []): mixed
{
return null;
}

protected function setAttributeValue(object $object, string $attribute, $value, ?string $format = null, array $context = []): void
{
if (property_exists($object, $attribute)) {
$object->$attribute = $value;
}
}
}
35 changes: 35 additions & 0 deletions Tests/Normalizer/ObjectNormalizerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
use Symfony\Component\Serializer\Mapping\Loader\AttributeLoader;
use Symfony\Component\Serializer\Mapping\Loader\YamlFileLoader;
use Symfony\Component\Serializer\NameConverter\AdvancedNameConverterInterface;
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
use Symfony\Component\Serializer\NameConverter\MetadataAwareNameConverter;
Expand Down Expand Up @@ -887,6 +888,40 @@ public function testDenormalizeWithIgnoreAttributeAndPrivateProperties()

$this->assertEquals($expected, $obj);
}

public function testNormalizeWithPropertyPath()
{
$classMetadataFactory = new ClassMetadataFactory(new YamlFileLoader(__DIR__.'/../Fixtures/property-path-mapping.yaml'));
$normalizer = new ObjectNormalizer($classMetadataFactory, new MetadataAwareNameConverter($classMetadataFactory));

$dummyInner = new ObjectInner();
$dummyInner->foo = 'foo';
$dummy = new ObjectOuter();
$dummy->setInner($dummyInner);

$this->assertSame(['inner_foo' => 'foo'], $normalizer->normalize($dummy, 'json', ['groups' => 'read']));
}

public function testDenormalizeWithPropertyPath()
{
$classMetadataFactory = new ClassMetadataFactory(new YamlFileLoader(__DIR__.'/../Fixtures/property-path-mapping.yaml'));
$normalizer = new ObjectNormalizer($classMetadataFactory, new MetadataAwareNameConverter($classMetadataFactory));

$dummy = new ObjectOuter();
$dummy->setInner(new ObjectInner());

$obj = $normalizer->denormalize(['inner_foo' => 'foo'], ObjectOuter::class, 'json', [
'object_to_populate' => $dummy,
'groups' => 'read',
]);

$expectedInner = new ObjectInner();
$expectedInner->foo = 'foo';
$expected = new ObjectOuter();
$expected->setInner($expectedInner);

$this->assertEquals($expected, $obj);
}
}

class ProxyObjectDummy extends ObjectDummy
Expand Down

0 comments on commit 1459271

Please sign in to comment.