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

api-platform / core / 6067528200

04 Sep 2023 12:12AM UTC coverage: 36.875% (-21.9%) from 58.794%
6067528200

Pull #5791

github

web-flow
Merge 64157e578 into d09cfc9d2
Pull Request #5791: fix: strip down any sql function name

3096 of 3096 new or added lines in 205 files covered. (100.0%)

9926 of 26918 relevant lines covered (36.87%)

6.5 hits per line

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

76.06
/src/Symfony/EventListener/AddFormatListener.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\Symfony\EventListener;
15

16
use ApiPlatform\Api\FormatMatcher;
17
use ApiPlatform\Metadata\Error as ErrorOperation;
18
use ApiPlatform\Metadata\HttpOperation;
19
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
20
use ApiPlatform\Util\OperationRequestInitiatorTrait;
21
use Negotiation\Exception\InvalidArgument;
22
use Negotiation\Negotiator;
23
use Symfony\Component\HttpFoundation\Request;
24
use Symfony\Component\HttpKernel\Event\RequestEvent;
25
use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
26
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
27

28
/**
29
 * Chooses the format to use according to the Accept header and supported formats.
30
 *
31
 * @author Kévin Dunglas <dunglas@gmail.com>
32
 */
33
final class AddFormatListener
34
{
35
    use OperationRequestInitiatorTrait;
36

37
    public function __construct(private readonly Negotiator $negotiator, ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory = null, private readonly array $formats = [], private readonly array $errorFormats = [])
38
    {
39
        $this->resourceMetadataCollectionFactory = $resourceMetadataCollectionFactory;
114✔
40
    }
41

42
    /**
43
     * Sets the applicable format to the HttpFoundation Request.
44
     *
45
     * @throws NotFoundHttpException
46
     * @throws NotAcceptableHttpException
47
     */
48
    public function onKernelRequest(RequestEvent $event): void
49
    {
50
        $request = $event->getRequest();
114✔
51
        $operation = $this->initializeOperation($request);
114✔
52

53
        if ('api_platform.symfony.main_controller' === $operation?->getController()) {
114✔
54
            return;
28✔
55
        }
56

57
        if ($operation instanceof ErrorOperation) {
90✔
58
            return;
×
59
        }
60

61
        if (!($request->attributes->has('_api_resource_class')
90✔
62
            || $request->attributes->getBoolean('_api_respond', false)
90✔
63
            || $request->attributes->getBoolean('_graphql', false)
90✔
64
        )) {
65
            return;
15✔
66
        }
67

68
        $formats = $operation?->getOutputFormats() ?? $this->formats;
83✔
69

70
        $this->addRequestFormats($request, $formats);
83✔
71

72
        // Empty strings must be converted to null because the Symfony router doesn't support parameter typing before 3.2 (_format)
73
        if (null === $routeFormat = $request->attributes->get('_format') ?: null) {
83✔
74
            $flattenedMimeTypes = $this->flattenMimeTypes($formats);
71✔
75
            $mimeTypes = array_keys($flattenedMimeTypes);
71✔
76
        } elseif (!isset($formats[$routeFormat])) {
12✔
77
            if (!$request->attributes->get('data') instanceof \Exception) {
3✔
78
                throw new NotFoundHttpException(sprintf('Format "%s" is not supported', $routeFormat));
3✔
79
            }
80
            $this->setRequestErrorFormat($operation, $request);
×
81

82
            return;
×
83
        } else {
84
            $mimeTypes = Request::getMimeTypes($routeFormat);
9✔
85
            $flattenedMimeTypes = $this->flattenMimeTypes([$routeFormat => $mimeTypes]);
9✔
86
        }
87

88
        // First, try to guess the format from the Accept header
89
        /** @var string|null $accept */
90
        $accept = $request->headers->get('Accept');
80✔
91

92
        if (null !== $accept) {
80✔
93
            $mediaType = null;
62✔
94
            try {
95
                $mediaType = $this->negotiator->getBest($accept, $mimeTypes);
62✔
96
            } catch (InvalidArgument) {
3✔
97
                throw $this->getNotAcceptableHttpException($accept, $flattenedMimeTypes);
3✔
98
            }
99

100
            if (null === $mediaType) {
59✔
101
                if (!$request->attributes->get('data') instanceof \Exception) {
9✔
102
                    throw $this->getNotAcceptableHttpException($accept, $flattenedMimeTypes);
9✔
103
                }
104

105
                $this->setRequestErrorFormat($operation, $request);
×
106

107
                return;
×
108
            }
109
            $formatMatcher = new FormatMatcher($formats);
50✔
110
            $request->setRequestFormat($formatMatcher->getFormat($mediaType->getType()));
50✔
111

112
            return;
50✔
113
        }
114

115
        // Then use the Symfony request format if available and applicable
116
        $requestFormat = $request->getRequestFormat('') ?: null;
18✔
117
        if (null !== $requestFormat) {
18✔
118
            $mimeType = $request->getMimeType($requestFormat);
12✔
119

120
            if (isset($flattenedMimeTypes[$mimeType])) {
12✔
121
                return;
9✔
122
            }
123

124
            if ($request->attributes->get('data') instanceof \Exception) {
3✔
125
                $this->setRequestErrorFormat($operation, $request);
×
126

127
                return;
×
128
            }
129

130
            throw $this->getNotAcceptableHttpException($mimeType, $flattenedMimeTypes);
3✔
131
        }
132

133
        // Finally, if no Accept header nor Symfony request format is set, return the default format
134
        foreach ($formats as $format => $mimeType) {
6✔
135
            $request->setRequestFormat($format);
6✔
136

137
            return;
6✔
138
        }
139
    }
140

141
    /**
142
     * Adds the supported formats to the request.
143
     *
144
     * This is necessary for {@see Request::getMimeType} and {@see Request::getMimeTypes} to work.
145
     */
146
    private function addRequestFormats(Request $request, array $formats): void
147
    {
148
        foreach ($formats as $format => $mimeTypes) {
83✔
149
            $request->setFormat($format, (array) $mimeTypes);
83✔
150
        }
151
    }
152

153
    /**
154
     * Retries the flattened list of MIME types.
155
     */
156
    private function flattenMimeTypes(array $formats): array
157
    {
158
        $flattenedMimeTypes = [];
80✔
159
        foreach ($formats as $format => $mimeTypes) {
80✔
160
            foreach ($mimeTypes as $mimeType) {
80✔
161
                $flattenedMimeTypes[$mimeType] = $format;
80✔
162
            }
163
        }
164

165
        return $flattenedMimeTypes;
80✔
166
    }
167

168
    /**
169
     * Retrieves an instance of NotAcceptableHttpException.
170
     */
171
    private function getNotAcceptableHttpException(string $accept, array $mimeTypes): NotAcceptableHttpException
172
    {
173
        return new NotAcceptableHttpException(sprintf(
15✔
174
            'Requested format "%s" is not supported. Supported MIME types are "%s".',
15✔
175
            $accept,
15✔
176
            implode('", "', array_keys($mimeTypes))
15✔
177
        ));
15✔
178
    }
179

180
    public function setRequestErrorFormat(?HttpOperation $operation, Request $request): void
181
    {
182
        $errorResourceFormats = array_merge($operation?->getOutputFormats() ?? [], $operation?->getFormats() ?? [], $this->errorFormats);
×
183

184
        $flattened = $this->flattenMimeTypes($errorResourceFormats);
×
185
        if ($flattened[$accept = $request->headers->get('Accept')] ?? false) {
×
186
            $request->setRequestFormat($flattened[$accept]);
×
187

188
            return;
×
189
        }
190

191
        if (isset($errorResourceFormats['jsonproblem'])) {
×
192
            $request->setRequestFormat('jsonproblem');
×
193
            $request->setFormat('jsonproblem', $errorResourceFormats['jsonproblem']);
×
194

195
            return;
×
196
        }
197

198
        $request->setRequestFormat(array_key_first($errorResourceFormats));
×
199
    }
200
}
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

© 2026 Coveralls, Inc