From 1da883f9318cd054c5d80d3cd38364404e30a656 Mon Sep 17 00:00:00 2001 From: dualfroz Date: Sat, 10 Oct 2026 11:43:47 +0200 Subject: [PATCH] fix(state): avoid deprecated PartialDenormalizationException::getErrors() --- phpstan.neon.dist | 1 + src/State/Provider/DeserializeProvider.php | 3 +- .../Provider/DeserializeProviderTest.php | 43 +++++++++++++++++++ .../EnumDenormalizationValidationTest.php | 12 ------ .../NullOnNonNullablePropertyTest.php | 8 ---- 5 files changed, 46 insertions(+), 21 deletions(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 62abec7..1c03aa2 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -102,6 +102,7 @@ parameters: - "#Call to function method_exists\\(\\) with 'Symfony\\\\\\\\Component\\\\\\\\Serializer\\\\\\\\Serializer' and 'getSupportedTypes' will always evaluate to true\\.#" - "#Call to function method_exists\\(\\) with Symfony\\\\Component\\\\Serializer\\\\Normalizer\\\\NormalizerInterface and 'getSupportedTypes' will always evaluate to true\\.#" - "#Call to function method_exists\\(\\) with Doctrine\\\\ODM\\\\MongoDB\\\\Mapping\\\\ClassMetadata\\|Doctrine\\\\ORM\\\\Mapping\\\\ClassMetadata and 'isChangeTrackingDef…' will always evaluate to true\\.#" + - "#Call to function method_exists\\(\\) with Symfony\\\\Component\\\\Serializer\\\\Exception\\\\PartialDenormalizationException and 'getNotNormalizableV…' will always evaluate to true\\.#" # See https://github.com/phpstan/phpstan-symfony/issues/27 - diff --git a/src/State/Provider/DeserializeProvider.php b/src/State/Provider/DeserializeProvider.php index e59ce9e..cb804ef 100644 --- a/src/State/Provider/DeserializeProvider.php +++ b/src/State/Provider/DeserializeProvider.php @@ -107,7 +107,8 @@ final class DeserializeProvider implements ProviderInterface, StopwatchAwareInte } $violations = new ConstraintViolationList(); - foreach ($e->getErrors() as $exception) { + $errors = method_exists($e, 'getNotNormalizableValueErrors') ? $e->getNotNormalizableValueErrors() : $e->getErrors(); + foreach ($errors as $exception) { if (!$exception instanceof NotNormalizableValueException) { continue; } diff --git a/src/State/Tests/Provider/DeserializeProviderTest.php b/src/State/Tests/Provider/DeserializeProviderTest.php index 65ae14a..248bfd7 100644 --- a/src/State/Tests/Provider/DeserializeProviderTest.php +++ b/src/State/Tests/Provider/DeserializeProviderTest.php @@ -365,4 +365,47 @@ class DeserializeProviderTest extends TestCase $request->attributes->set('input_format', 'format'); $provider->provide($operation, ['id' => 1], ['request' => $request]); } + + /** + * @see https://github.com/api-platform/core/issues/8650 + */ + public function testDeserializeCollectsPartialDenormalizationErrorsWithoutDeprecation(): void + { + $operation = new Post(deserialize: true, class: \stdClass::class); + $decorated = $this->createStub(ProviderInterface::class); + $decorated->method('provide')->willReturn(null); + + $exception = NotNormalizableValueException::createForUnexpectedDataType('Internal error detail', 42, ['string'], 'name', false); + $partialException = new PartialDenormalizationException(null, [$exception]); + + $serializerContextBuilder = $this->createStub(SerializerContextBuilderInterface::class); + $serializerContextBuilder->method('createFromRequest')->willReturn([SerializerContextBuilderInterface::ASSIGN_OBJECT_TO_POPULATE => false]); + $serializer = $this->createStub(SerializerInterface::class); + $serializer->method('deserialize')->willThrowException($partialException); + + $provider = new DeserializeProvider($decorated, $serializer, $serializerContextBuilder); + $request = new Request(content: '{"name":42}'); + $request->headers->set('CONTENT_TYPE', 'application/json'); + $request->attributes->set('input_format', 'json'); + + $deprecations = []; + set_error_handler(static function (int $type, string $message) use (&$deprecations): bool { + $deprecations[] = $message; + + return true; + }, \E_USER_DEPRECATED); + + try { + $provider->provide($operation, [], ['request' => $request]); + $this->fail('Expected ValidationException'); + } catch (ValidationException $e) { + $violations = $e->getConstraintViolationList(); + } finally { + restore_error_handler(); + } + + $this->assertSame([], $deprecations); + $this->assertCount(1, $violations); + $this->assertSame('name', $violations[0]->getPropertyPath()); + } } diff --git a/tests/Functional/EnumDenormalizationValidationTest.php b/tests/Functional/EnumDenormalizationValidationTest.php index 51f81eb..15c5ee6 100644 --- a/tests/Functional/EnumDenormalizationValidationTest.php +++ b/tests/Functional/EnumDenormalizationValidationTest.php @@ -17,8 +17,6 @@ use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\EnumValidationResource; use ApiPlatform\Tests\SetupClassResourcesTrait; use Composer\InstalledVersions; -use Composer\Semver\VersionParser; -use PHPUnit\Framework\Attributes\IgnoreDeprecations; /** * @see https://github.com/api-platform/core/issues/8183 @@ -67,13 +65,8 @@ final class EnumDenormalizationValidationTest extends ApiTestCase $this->assertNotNull($genderViolation, 'Expected a constraint violation on "gender" property.'); } - #[IgnoreDeprecations] public function testInvalidBackedEnumValueWithCollectDenormalizationErrors(): void { - if (InstalledVersions::satisfies(new VersionParser(), 'symfony/serializer', '>=8.1')) { - $this->expectUserDeprecationMessage('Since symfony/serializer 8.1: The "Symfony\Component\Serializer\Exception\PartialDenormalizationException::getErrors()" method is deprecated, use "Symfony\Component\Serializer\Exception\PartialDenormalizationException::getNotNormalizableValueErrors()" instead.'); - } - $response = static::createClient()->request('POST', '/enum_validation_resources_collect', [ 'headers' => ['Content-Type' => 'application/ld+json'], 'json' => ['gender' => 'unknown'], @@ -87,13 +80,8 @@ final class EnumDenormalizationValidationTest extends ApiTestCase /** * @see https://github.com/api-platform/core/issues/8388 */ - #[IgnoreDeprecations] public function testWrongTypeForBackedEnumReportsAcceptedScalarTypes(): void { - if (InstalledVersions::satisfies(new VersionParser(), 'symfony/serializer', '>=8.1')) { - $this->expectUserDeprecationMessage('Since symfony/serializer 8.1: The "Symfony\Component\Serializer\Exception\PartialDenormalizationException::getErrors()" method is deprecated, use "Symfony\Component\Serializer\Exception\PartialDenormalizationException::getNotNormalizableValueErrors()" instead.'); - } - $response = static::createClient()->request('POST', '/enum_validation_resources_collect', [ 'headers' => ['Content-Type' => 'application/ld+json'], 'json' => ['gender' => true], diff --git a/tests/Functional/NullOnNonNullablePropertyTest.php b/tests/Functional/NullOnNonNullablePropertyTest.php index 2203c64..c35a57f 100644 --- a/tests/Functional/NullOnNonNullablePropertyTest.php +++ b/tests/Functional/NullOnNonNullablePropertyTest.php @@ -16,9 +16,6 @@ namespace ApiPlatform\Tests\Functional; use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; use ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\NullOnNonNullableProperty\NullOnNonNullableResource; use ApiPlatform\Tests\SetupClassResourcesTrait; -use Composer\InstalledVersions; -use Composer\Semver\VersionParser; -use PHPUnit\Framework\Attributes\IgnoreDeprecations; /** @see https://github.com/symfony/symfony/issues/64159 */ final class NullOnNonNullablePropertyTest extends ApiTestCase @@ -48,13 +45,8 @@ final class NullOnNonNullablePropertyTest extends ApiTestCase $this->assertStringContainsString('Expected argument of type "string", "null" given at property path "name"', $body['hydra:description'] ?? $body['detail'] ?? ''); } - #[IgnoreDeprecations] public function testNullOnNonNullablePropertyReturns422WhenCollectingErrors(): void { - if (InstalledVersions::satisfies(new VersionParser(), 'symfony/serializer', '>=8.1')) { - $this->expectUserDeprecationMessage('Since symfony/serializer 8.1: The "Symfony\Component\Serializer\Exception\PartialDenormalizationException::getErrors()" method is deprecated, use "Symfony\Component\Serializer\Exception\PartialDenormalizationException::getNotNormalizableValueErrors()" instead.'); - } - $response = self::createClient()->request('POST', '/null_on_non_nullable_resources_collect', [ 'headers' => ['Content-Type' => 'application/ld+json'], 'json' => ['name' => null],