Skip to content

[Serializer] Improve denormalization of backed enums #50201

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: 7.4
Choose a base branch
from

Conversation

rodion-k
Copy link

@rodion-k rodion-k commented May 1, 2023

Q A
Branch? 7.1
Bug fix? no
New feature? yes
Deprecations? no
Tickets --
License MIT
Doc PR --

The goal is to improve error handling when denormalizing an enum.

Currently, if a string value is given when an integer is expected for the BackedEnum then such an error is thrown:
TypeError: TestEnum::from(): Argument #1 ($value) must be of type int, string given

This PR adds validation for such cases.

Also in case of an invalid enum case, the NotNormalizableValueException is thrown.

@carsonbot
Copy link

Hey!

I see that this is your first PR. That is great! Welcome!

Symfony has a contribution guide which I suggest you to read.

In short:

  • Always add tests
  • Keep backward compatibility (see https://symfony.com/bc).
  • Bug fixes must be submitted against the lowest maintained branch where they apply (see https://symfony.com/releases)
  • Features and deprecations must be submitted against the 6.3 branch.

Review the GitHub status checks of your pull request and try to solve the reported issues. If some tests are failing, try to see if they are failing because of this change.

When two Symfony core team members approve this change, it will be merged and you will become an official Symfony contributor!
If this PR is merged in a lower version branch, it will be merged up to all maintained branches within a few days.

I am going to sit back now and wait for the reviews.

Cheers!

Carsonbot

if (!\is_int($data) && !\is_string($data)) {
throw NotNormalizableValueException::createForUnexpectedDataType('The data is neither an integer nor a string, you should pass an integer or a string that can be parsed as an enumeration case of type '.$type.'.', $data, [Type::BUILTIN_TYPE_INT, Type::BUILTIN_TYPE_STRING], $context['deserialization_path'] ?? null, true);
$backingType = (new ReflectionEnum($type))->getBackingType()->getName();
$givenType = get_debug_type($data);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$givenType = get_debug_type($data);
$givenType = \get_type($data);

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you mean gettype function, it returns "integer" and ReflectionNamedType::getName returns "int', so get_debug_type fits better

}

try {
return $type::from($data);
} catch (\ValueError $e) {
throw new InvalidArgumentException('The data must belong to a backed enumeration of type '.$type);
throw NotNormalizableValueException::createForUnexpectedDataType('The data must belong to a backed enumeration of type '.$type, $data, [$backingType], $context['deserialization_path'] ?? null, true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this change basically reverts #47128
see #50192 also

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#47128 was resolving #46977 issue which is covered by the \Symfony\Component\Serializer\Tests\SerializerTest::testCollectDenormalizationErrorsWithWrongEnum test and it works as expected. It doesn't throw TypeError but gives PartialDenormalizationException as expected.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed with @nicolas-grekas here, it seems to revert the previous work.
If you take a look at #46977 (for instance #47128 (comment)), and #48821, you'll see that the InvalidArgumentException is intentional.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throwing InvalidArgumentException instead of NotNormalizableValueException undoes the feature done by #42502

I first viewed #43047 (comment)
Then I was happy that may be #40830 + #42502 would be a solution to my problem. But then #47128 undid that functionality again. It was partially reverted back to an NotNormalizableValueException in #50192

The whole point of having it as an NotNormalizableValueException is that you should be able to serialize DTOs with enums and then collect all the errors at the end in an PartialDenormalizationException. You should take care not to ruin that functionality.

@rodion-k rodion-k force-pushed the enum-denormalization branch from 9804c36 to 90ffb90 Compare May 3, 2023 10:58
@nicolas-grekas nicolas-grekas modified the milestones: 6.3, 6.4 May 23, 2023
Comment on lines +24 to +25
* Make `BackedEnumNormalizer` throw `NotNormalizableValueException` when the backed type doesn't match
* Make `BackedEnumNormalizer` throw `NotNormalizableValueException` when the value does not belong to a backed enumeration
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Make `BackedEnumNormalizer` throw `NotNormalizableValueException` when the backed type doesn't match
* Make `BackedEnumNormalizer` throw `NotNormalizableValueException` when the value does not belong to a backed enumeration
* Make `BackedEnumNormalizer` throw `NotNormalizableValueException` when the backed type doesn't match or when the value does not belong to a backed enumeration

@nicolas-grekas nicolas-grekas added ❄️ Feature Freeze Important Pull Requests to finish before the next Symfony "feature freeze" and removed ❄️ Feature Freeze Important Pull Requests to finish before the next Symfony "feature freeze" labels Oct 16, 2023
@nicolas-grekas nicolas-grekas modified the milestones: 6.4, 7.1 Nov 15, 2023
@xabbuh xabbuh modified the milestones: 7.1, 7.2 May 15, 2024
@fabpot fabpot modified the milestones: 7.2, 7.3 Nov 20, 2024
@fabpot fabpot modified the milestones: 7.3, 7.4 May 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants