Skip to content

Commit

Permalink
[Serializer][PropertyInfo][Validator] TypeInfo 7.2 compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
mtarld committed Nov 14, 2024
1 parent 6066de1 commit 7afcfbb
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 10 deletions.
72 changes: 63 additions & 9 deletions Normalizer/AbstractObjectNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@
use Symfony\Component\Serializer\NameConverter\NameConverterInterface;
use Symfony\Component\TypeInfo\Exception\LogicException as TypeInfoLogicException;
use Symfony\Component\TypeInfo\Type;
use Symfony\Component\TypeInfo\Type\BuiltinType;
use Symfony\Component\TypeInfo\Type\CollectionType;
use Symfony\Component\TypeInfo\Type\IntersectionType;
use Symfony\Component\TypeInfo\Type\NullableType;
use Symfony\Component\TypeInfo\Type\ObjectType;
use Symfony\Component\TypeInfo\Type\UnionType;
use Symfony\Component\TypeInfo\Type\WrappingTypeInterface;
use Symfony\Component\TypeInfo\TypeIdentifier;

/**
Expand Down Expand Up @@ -644,7 +647,14 @@ private function validateAndDenormalizeLegacy(array $types, string $currentClass
private function validateAndDenormalize(Type $type, string $currentClass, string $attribute, mixed $data, ?string $format, array $context): mixed
{
$expectedTypes = [];
$isUnionType = $type->asNonNullable() instanceof UnionType;

// BC layer for type-info < 7.2
if (method_exists(Type::class, 'asNonNullable')) {
$isUnionType = $type->asNonNullable() instanceof UnionType;
} else {
$isUnionType = $type instanceof UnionType;
}

$e = null;
$extraAttributesException = null;
$missingConstructorArgumentsException = null;
Expand All @@ -667,12 +677,23 @@ private function validateAndDenormalize(Type $type, string $currentClass, string
$collectionValueType = $t->getCollectionValueType();
}

$t = $t->getBaseType();
// BC layer for type-info < 7.2
if (method_exists(Type::class, 'getBaseType')) {
$t = $t->getBaseType();
} else {
while ($t instanceof WrappingTypeInterface) {
$t = $t->getWrappedType();
}
}

// Fix a collection that contains the only one element
// This is special to xml format only
if ('xml' === $format && $collectionValueType && !$collectionValueType->isA(TypeIdentifier::MIXED) && (!\is_array($data) || !\is_int(key($data)))) {
$data = [$data];
if ('xml' === $format && $collectionValueType && (!\is_array($data) || !\is_int(key($data)))) {
// BC layer for type-info < 7.2
$isMixedType = method_exists(Type::class, 'isA') ? $collectionValueType->isA(TypeIdentifier::MIXED) : $collectionValueType->isIdentifiedBy(TypeIdentifier::MIXED);
if (!$isMixedType) {
$data = [$data];
}
}

// This try-catch should cover all NotNormalizableValueException (and all return branches after the first
Expand All @@ -695,7 +716,10 @@ private function validateAndDenormalize(Type $type, string $currentClass, string
return '';
}

$isNullable = $isNullable ?: $type->isNullable();
// BC layer for type-info < 7.2
if (method_exists(Type::class, 'isNullable')) {
$isNullable = $isNullable ?: $type->isNullable();
}
}

switch ($typeIdentifier) {
Expand Down Expand Up @@ -732,7 +756,16 @@ private function validateAndDenormalize(Type $type, string $currentClass, string

if ($collectionValueType) {
try {
$collectionValueBaseType = $collectionValueType->getBaseType();
$collectionValueBaseType = $collectionValueType;

// BC layer for type-info < 7.2
if (!interface_exists(WrappingTypeInterface::class)) {
$collectionValueBaseType = $collectionValueType->getBaseType();
} else {
while ($collectionValueBaseType instanceof WrappingTypeInterface) {
$collectionValueBaseType = $collectionValueBaseType->getWrappedType();
}
}
} catch (TypeInfoLogicException) {
$collectionValueBaseType = Type::mixed();
}
Expand All @@ -742,15 +775,29 @@ private function validateAndDenormalize(Type $type, string $currentClass, string
$class = $collectionValueBaseType->getClassName().'[]';
$context['key_type'] = $collectionKeyType;
$context['value_type'] = $collectionValueType;
} elseif (TypeIdentifier::ARRAY === $collectionValueBaseType->getTypeIdentifier()) {
} elseif (
// BC layer for type-info < 7.2
!class_exists(NullableType::class) && TypeIdentifier::ARRAY === $collectionValueBaseType->getTypeIdentifier()
|| $collectionValueBaseType instanceof BuiltinType && TypeIdentifier::ARRAY === $collectionValueBaseType->getTypeIdentifier()
) {
// get inner type for any nested array
$innerType = $collectionValueType;
if ($innerType instanceof NullableType) {
$innerType = $innerType->getWrappedType();
}

// note that it will break for any other builtinType
$dimensions = '[]';
while ($innerType instanceof CollectionType) {
$dimensions .= '[]';
$innerType = $innerType->getCollectionValueType();
if ($innerType instanceof NullableType) {
$innerType = $innerType->getWrappedType();
}
}

while ($innerType instanceof WrappingTypeInterface) {
$innerType = $innerType->getWrappedType();
}

if ($innerType instanceof ObjectType) {
Expand Down Expand Up @@ -862,8 +909,15 @@ private function validateAndDenormalize(Type $type, string $currentClass, string
throw $missingConstructorArgumentsException;
}

if (!$isUnionType && $e) {
throw $e;
// BC layer for type-info < 7.2
if (!class_exists(NullableType::class)) {
if (!$isUnionType && $e) {
throw $e;
}
} else {
if ($e && !($type instanceof UnionType && !$type instanceof NullableType)) {
throw $e;
}
}

if ($context[self::DISABLE_TYPE_ENFORCEMENT] ?? $this->defaultContext[self::DISABLE_TYPE_ENFORCEMENT] ?? false) {
Expand Down
12 changes: 11 additions & 1 deletion Normalizer/ArrayDenormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
use Symfony\Component\TypeInfo\Type;
use Symfony\Component\TypeInfo\Type\BuiltinType;
use Symfony\Component\TypeInfo\Type\UnionType;
use Symfony\Component\TypeInfo\TypeIdentifier;

/**
* Denormalizes arrays of objects.
Expand Down Expand Up @@ -59,7 +61,15 @@ public function denormalize(mixed $data, string $type, ?string $format = null, a
$typeIdentifiers = [];
if (null !== $keyType = ($context['key_type'] ?? null)) {
if ($keyType instanceof Type) {
$typeIdentifiers = array_map(fn (Type $t): string => $t->getBaseType()->getTypeIdentifier()->value, $keyType instanceof UnionType ? $keyType->getTypes() : [$keyType]);
// BC layer for type-info < 7.2
if (method_exists(Type::class, 'getBaseType')) {
$typeIdentifiers = array_map(fn (Type $t): string => $t->getBaseType()->getTypeIdentifier()->value, $keyType instanceof UnionType ? $keyType->getTypes() : [$keyType]);
} else {
/** @var list<BuiltinType<TypeIdentifier::INT>|BuiltinType<TypeIdentifier::STRING>> */
$keyTypes = $keyType instanceof UnionType ? $keyType->getTypes() : [$keyType];

$typeIdentifiers = array_map(fn (BuiltinType $t): string => $t->getTypeIdentifier()->value, $keyTypes);
}
} else {
$typeIdentifiers = array_map(fn (LegacyType $t): string => $t->getBuiltinType(), \is_array($keyType) ? $keyType : [$keyType]);
}
Expand Down

0 comments on commit 7afcfbb

Please sign in to comment.