Skip to content

[Serializer] Discrepancy between NormalizerInterface and it's implementation #59495

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

Closed
Kefisu opened this issue Jan 13, 2025 · 0 comments · Fixed by #59496
Closed

[Serializer] Discrepancy between NormalizerInterface and it's implementation #59495

Kefisu opened this issue Jan 13, 2025 · 0 comments · Fixed by #59496

Comments

@Kefisu
Copy link
Contributor

Kefisu commented Jan 13, 2025

Description

The "Problem"

Right now there is a discrepancy between the Symfony\Component\Serializer\Normalizer\NormalizerInterface and it's implementations within the Symfony codebase.

If you look at the signature of the NormalizerInterface.normalize method it looks as follows:
public function normalize(mixed $data, ?string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null;

Whereas most implementing classes are implemented like this:

AbstractObjectNormalizer:
public function normalize(mixed $object, ?string $format = null, array $context = []): array|string|int|float|bool|\ArrayObject|null

DateIntervalNormalizer:
public function normalize(mixed $object, ?string $format = null, array $context = []): string

And the list goes on..

Whilst PHP allows this behavior to exists and doesn't raise any errors for it, with the introduction of named parameters in PHP 8.0 this discrepancy now has the possibility to throw PHP errors such as:
Error: Unknown named parameter $data.

Acknowledgement

Whilst reading through the existing issues I came upon #54880 where it was discussed that the BC does not cover this change or would increase the risk of merging changes back into branches.

However, in it's current implementation it is simply impossible to use named parameters when type hinting the NormalizerInterface and not running the risk of unexpected PHP errors.

Conclusion

Therefore I would like to submit this as a feature ("not a bug") type of change to the 7.3/7.4 branch to solve this discrepancy and reducing the risk of unintentional errors for developers using Symfony.

Linked to this issue is a PR that changes all instances where the NormalizerInterface is implemented but does not follow the Interfaces' signature.

Example

The following is a change to the ObjectNormalizerTest that shows that using named parameters on the interface with an implementation of the ObjectNormalizer PHP will throw an error

<?php

namespace Symfony\Component\Serializer\Tests\Normalizer;

class ObjectNormalizerTest extends TestCase
{
    private ObjectNormalizer $normalizer;
    private NormalizerInterface $normalizerInterface;
    private SerializerInterface&NormalizerInterface&MockObject $serializer;

    protected function setUp(): void
    {
        $this->createNormalizer();

        $this->normalizerInterface = $this->normalizer;
    }

    private function createNormalizer(array $defaultContext = [], ?ClassMetadataFactoryInterface $classMetadataFactory = null): void
    {
        $this->serializer = $this->createMock(ObjectSerializerNormalizer::class);
        $this->normalizer = new ObjectNormalizer($classMetadataFactory, null, null, null, null, null, $defaultContext);
        $this->normalizer->setSerializer($this->serializer);
    }

    // In the current releases this throws an error
    public function testNamedArgumentsOnInterfaceWillThrowError(): void
    {
        $this->normalizerInterface->normalize(
            data: new ObjectDummy(),
            format: 'json',
            context: []
        );
    }
}
php ./phpunit src/Symfony/Component/Serializer
...
Time: 00:00.302, Memory: 39.02 MB

There was 1 error:

1) Symfony\Component\Serializer\Tests\Normalizer\ObjectNormalizerTest::testNamedArgumentsOnInterfaceWillThrowError
Error: Unknown named parameter $data

And after changing all implementations:

php ./phpunit src/Symfony/Component/Serializer

............................................................. 1098 / 1167 ( 94%)
............................................................. 1159 / 1167 ( 99%)
........                                                      1167 / 1167 (100%)

Time: 00:00.365, Memory: 39.02 MB

OK, but incomplete, skipped, or risky tests!
Tests: 1167, Assertions: 2258, Skipped: 11.

Legacy deprecation notices (2)
Kefisu added a commit to Kefisu/symfony that referenced this issue Jan 13, 2025
Kefisu added a commit to Kefisu/symfony that referenced this issue Jan 13, 2025
Kefisu added a commit to Kefisu/symfony that referenced this issue Jan 13, 2025
…rrect method signature

This commit fixes symfony#59495

Rename test method name
Kefisu added a commit to Kefisu/symfony that referenced this issue Jan 13, 2025
…rrect method signature

This commit fixes symfony#59495

Rename test method name

Remove void return type from test
Kefisu added a commit to Kefisu/symfony that referenced this issue Jan 13, 2025
…rrect method signature

This commit fixes symfony#59495

Rename test method name

Remove void return type from test

Remove redundant docblock
@fabpot fabpot closed this as completed in 7336e7f Feb 7, 2025
fabpot added a commit that referenced this issue Feb 7, 2025
…zerInterface` to have the correct method signature (Kefisu)

This PR was merged into the 7.3 branch.

Discussion
----------

[Serializer] Correct all implementations of the `NormalizerInterface` to have the correct method signature

| Q             | A
| ------------- | ---
| Branch?       | 7.3
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Issues        | Fix #59495
| License       | MIT

This PR corrects the signatures of all normalizers implementing the NormalizerInterface of the Serializer component effectively changing: `function normalize($object, ... )` to `function normalize(mixed $data, ...)` as is in line with the interface.

Whilst Named Arguments is against the BC promise of Symfony, with the current implementation of the normalizers it is impossible to use named arguments with the Strategy Pattern using the Serializer component, with this change it is possible.

Commits
-------

7336e7f Correct all implementations of the NormalizerInterface to have the correct method signature
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants