Skip to content

[HttpKernel] Denormalize request data using the csv format when using "#[MapQueryString]" or "#[MapRequestPayload]" (except for content data) #59134

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

Merged
merged 1 commit into from
Dec 9, 2024

Conversation

ovidiuenache
Copy link
Contributor

@ovidiuenache ovidiuenache commented Dec 7, 2024

Q A
Branch? 6.4
Bug fix? yes
New feature? no
Deprecations? no
Issues #59104 #50904
License MIT

This fixes the scenario mentioned above where using #[MapQueryString] or #[MapRequestPayload] (except request content) with at least one readonly property with a type mismatch error leads to getting an object with uninitialized properties. The only properties that are set are the ones that are assigned a value via a request parameter and are NOT readonly. Moreover, if the corresponding query parameter is not provided for non-readonly property A and there is at least one other readonly property B that has a type mismatch then A will still be returned as uninitialized (even if it has a default value).

Shortly put, the instantiation fails and the values of the properties cannot be set later on.

Examples

class FilterDto {
    public function __construct(
        public readonly ?int $id = null,
        public readonly ?string $name = null,
        public ?string $description = null,
    ) {
    }
}

GET https://127.0.0.1:8000/test?id=x&name=test
id: ? ?int
name: ? ?string
description: ? ?string

GET https://127.0.0.1:8000/test?id=x&name=test&description=desc
id: ? ?int
name: ? ?string
description: "desc"

The complete list of steps to reproduce this is provided in #59104.

The reason why this happens is because we are disabling the type enforcement of the denormalizer in the Symfony\Component\HttpKernel\Controller\ArgumentResolver\RequestPayloadValueResolver class and when we eventually end up in the validateAndDenormalize method of the Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer class we ignore the type mismatch because of:

if ($context[self::DISABLE_TYPE_ENFORCEMENT] ?? $this->defaultContext[self::DISABLE_TYPE_ENFORCEMENT] ?? false) {
    return $data;
}

Thus, we get a type error when trying to create the object and we fall back to
$reflectionClass->newInstanceWithoutConstructor();

Then, based on the provided request data, we attempt to set the values of the properties but this process fails for the readonly properties so they stay uninitialized.

As discussed with @nicolas-grekas during the hackathon at SymfonyCon Vienna 2024 the solution here is to stop disabling the type enforcement of the denormalizer. However, this alone is not enough because then we won't be able to use anything but string since this is the type that comes in the request so we also need to set the denormalization format to either csv or xml.

This comment from the Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer sheds some light on why:

// In XML and CSV all basic datatypes are represented as strings, it is e.g. not possible to determine,
// if a value is meant to be a string, float, int or a boolean value from the serialized representation.
// That's why we have to transform the values, if one of these non-string basic datatypes is expected.

We avoid xml due to some special formatting that occurs so the proposed solution uses csv.

Basically, we start using type enforcement + csv format where non-string values are transformed.

@ovidiuenache
Copy link
Contributor Author

The coding standards check is failing due to existing violations in the files I've updated. It says here that I should Never fix coding standards in some existing code as it makes the code review more difficult; so I left it as is and will fix the existing flagged issues on request.

… "#[MapQueryString]" or "#[MapRequestPayload]" (except for content data)
@fabpot
Copy link
Member

fabpot commented Dec 9, 2024

Thank you @ovidiuenache.

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.

4 participants