• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 17071323229

19 Aug 2025 01:36PM UTC coverage: 0.0%. First build
17071323229

Pull #7225

github

web-flow
Merge 1e726a585 into 020385253
Pull Request #7225: feat: json streamer

0 of 307 new or added lines in 31 files covered. (0.0%)

0 of 52230 relevant lines covered (0.0%)

0.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/State/Provider/DeserializeProvider.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
declare(strict_types=1);
13

14
namespace ApiPlatform\State\Provider;
15

16
use ApiPlatform\Metadata\HttpOperation;
17
use ApiPlatform\Metadata\Operation;
18
use ApiPlatform\State\ProviderInterface;
19
use ApiPlatform\State\SerializerContextBuilderInterface;
20
use ApiPlatform\State\StopwatchAwareInterface;
21
use ApiPlatform\State\StopwatchAwareTrait;
22
use ApiPlatform\Validator\Exception\ValidationException;
23
use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
24
use Symfony\Component\Serializer\Exception\NotNormalizableValueException;
25
use Symfony\Component\Serializer\Exception\PartialDenormalizationException;
26
use Symfony\Component\Serializer\Normalizer\AbstractNormalizer;
27
use Symfony\Component\Serializer\SerializerInterface;
28
use Symfony\Component\Validator\Constraints\Type;
29
use Symfony\Component\Validator\ConstraintViolation;
30
use Symfony\Component\Validator\ConstraintViolationList;
31
use Symfony\Contracts\Translation\LocaleAwareInterface;
32
use Symfony\Contracts\Translation\TranslatorInterface;
33
use Symfony\Contracts\Translation\TranslatorTrait;
34

35
final class DeserializeProvider implements ProviderInterface, StopwatchAwareInterface
36
{
37
    use StopwatchAwareTrait;
38

39
    public function __construct(
40
        private readonly ?ProviderInterface $decorated,
41
        private readonly SerializerInterface $serializer,
42
        private readonly SerializerContextBuilderInterface $serializerContextBuilder,
43
        private ?TranslatorInterface $translator = null,
44
    ) {
45
        if (null === $this->translator) {
×
46
            $this->translator = new class implements TranslatorInterface, LocaleAwareInterface {
×
47
                use TranslatorTrait;
48
            };
×
49
            $this->translator->setLocale('en');
×
50
        }
51
    }
52

53
    public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
54
    {
55
        // We need request content
56
        if (!$operation instanceof HttpOperation || !($request = $context['request'] ?? null)) {
×
57
            return $this->decorated?->provide($operation, $uriVariables, $context);
×
58
        }
59

60
        $data = $this->decorated ? $this->decorated->provide($operation, $uriVariables, $context) : $request->attributes->get('data');
×
61

NEW
62
        if (!$operation->canDeserialize() || $context['request']->attributes->has('deserialized')) {
×
63
            return $data;
×
64
        }
65

66
        $this->stopwatch?->start('api_platform.provider.deserialize');
×
67

68
        $contentType = $request->headers->get('CONTENT_TYPE');
×
69
        if (null === $contentType || '' === $contentType) {
×
70
            throw new UnsupportedMediaTypeHttpException('The "Content-Type" header must exist.');
×
71
        }
72

73
        $serializerContext = $this->serializerContextBuilder->createFromRequest($request, false, [
×
74
            'resource_class' => $operation->getClass(),
×
75
            'operation' => $operation,
×
76
        ]);
×
77

78
        $serializerContext['uri_variables'] = $uriVariables;
×
79

80
        if (!$format = $request->attributes->get('input_format') ?? null) {
×
81
            throw new UnsupportedMediaTypeHttpException('Format not supported.');
×
82
        }
83

84
        if (null === ($serializerContext[SerializerContextBuilderInterface::ASSIGN_OBJECT_TO_POPULATE] ?? null)) {
×
85
            $method = $operation->getMethod();
×
86
            $assignObjectToPopulate = 'POST' === $method
×
87
                || 'PATCH' === $method
×
88
                || ('PUT' === $method && !($operation->getExtraProperties()['standard_put'] ?? true));
×
89

90
            if ($assignObjectToPopulate) {
×
91
                $serializerContext[SerializerContextBuilderInterface::ASSIGN_OBJECT_TO_POPULATE] = true;
×
92
                trigger_deprecation('api-platform/core', '5.0', 'To assign an object to populate you should set "%s" in your denormalizationContext, not defining it is deprecated.', SerializerContextBuilderInterface::ASSIGN_OBJECT_TO_POPULATE);
×
93
            }
94
        }
95

96
        if (null !== $data && ($serializerContext[SerializerContextBuilderInterface::ASSIGN_OBJECT_TO_POPULATE] ?? false)) {
×
97
            $serializerContext[AbstractNormalizer::OBJECT_TO_POPULATE] = $data;
×
98
        }
99

100
        unset($serializerContext[SerializerContextBuilderInterface::ASSIGN_OBJECT_TO_POPULATE]);
×
101

102
        try {
103
            $data = $this->serializer->deserialize((string) $request->getContent(), $serializerContext['deserializer_type'] ?? $operation->getClass(), $format, $serializerContext);
×
104
        } catch (PartialDenormalizationException $e) {
×
105
            if (!class_exists(ConstraintViolationList::class)) {
×
106
                throw $e;
×
107
            }
108

109
            $violations = new ConstraintViolationList();
×
110
            foreach ($e->getErrors() as $exception) {
×
111
                if (!$exception instanceof NotNormalizableValueException) {
×
112
                    continue;
×
113
                }
114
                $expectedTypes = $this->normalizeExpectedTypes($exception->getExpectedTypes());
×
115
                $message = (new Type($expectedTypes))->message;
×
116
                $parameters = [];
×
117
                if ($exception->canUseMessageForUser()) {
×
118
                    $parameters['hint'] = $exception->getMessage();
×
119
                }
120
                $violations->add(new ConstraintViolation($this->translator->trans($message, ['{{ type }}' => implode('|', $expectedTypes)], 'validators'), $message, $parameters, null, $exception->getPath(), null, null, (string) Type::INVALID_TYPE_ERROR));
×
121
            }
122
            if (0 !== \count($violations)) {
×
123
                throw new ValidationException($violations);
×
124
            }
125
        }
126

127
        $this->stopwatch?->stop('api_platform.provider.deserialize');
×
128

129
        return $data;
×
130
    }
131

132
    private function normalizeExpectedTypes(?array $expectedTypes = null): array
133
    {
134
        $normalizedTypes = [];
×
135

136
        foreach ($expectedTypes ?? [] as $expectedType) {
×
137
            $normalizedType = $expectedType;
×
138

139
            if (class_exists($expectedType) || interface_exists($expectedType)) {
×
140
                $classReflection = new \ReflectionClass($expectedType);
×
141
                $normalizedType = $classReflection->getShortName();
×
142
            }
143

144
            $normalizedTypes[] = $normalizedType;
×
145
        }
146

147
        return $normalizedTypes;
×
148
    }
149
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc