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

api-platform / core / 7582727293

19 Jan 2024 10:45AM UTC coverage: 61.96% (+2.8%) from 59.207%
7582727293

push

github

web-flow
feat(symfony): request and view kernel listeners (#6102)

133 of 266 new or added lines in 19 files covered. (50.0%)

447 existing lines in 32 files now uncovered.

17435 of 28139 relevant lines covered (61.96%)

32.39 hits per line

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

71.67
/src/Symfony/EventListener/RespondListener.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\IriConverterInterface as LegacyIriConverterInterface;
17
use ApiPlatform\Metadata\Exception\HttpExceptionInterface;
18
use ApiPlatform\Metadata\IriConverterInterface;
19
use ApiPlatform\Metadata\Put;
20
use ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface;
21
use ApiPlatform\Metadata\UrlGeneratorInterface;
22
use ApiPlatform\State\ProcessorInterface;
23
use ApiPlatform\State\Util\OperationRequestInitiatorTrait;
24
use ApiPlatform\Symfony\Util\RequestAttributesExtractor;
25
use Symfony\Component\HttpFoundation\Response;
26
use Symfony\Component\HttpKernel\Event\ViewEvent;
27

28
/**
29
 * Builds the response object.
30
 *
31
 * @author Kévin Dunglas <dunglas@gmail.com>
32
 */
33
final class RespondListener
34
{
35
    use OperationRequestInitiatorTrait;
36

37
    public const METHOD_TO_CODE = [
38
        'POST' => Response::HTTP_CREATED,
39
        'DELETE' => Response::HTTP_NO_CONTENT,
40
    ];
41

42
    private null|IriConverterInterface|LegacyIriConverterInterface $iriConverter = null;
43
    private ?ProcessorInterface $processor = null;
44

45
    public function __construct(ResourceMetadataCollectionFactoryInterface $resourceMetadataFactory = null, IriConverterInterface|LegacyIriConverterInterface|ProcessorInterface $iriConverter = null)
46
    {
47
        if ($iriConverter instanceof ProcessorInterface) {
44✔
NEW
48
            $this->processor = $iriConverter;
×
49
        } else {
50
            trigger_deprecation('api-platform/core', '3.3', 'Use a "%s" as second argument in "%s" instead of "%s".', ProcessorInterface::class, self::class, IriConverterInterface::class);
44✔
51
            $this->iriConverter = $iriConverter;
44✔
52
        }
53

54
        $this->resourceMetadataCollectionFactory = $resourceMetadataFactory;
44✔
55
    }
56

57
    /**
58
     * Creates a Response to send to the client according to the requested format.
59
     */
60
    public function onKernelView(ViewEvent $event): void
61
    {
62
        $request = $event->getRequest();
44✔
63
        $controllerResult = $event->getControllerResult();
44✔
64
        $operation = $this->initializeOperation($request);
44✔
65

66
        $attributes = RequestAttributesExtractor::extractAttributes($request);
44✔
67
        if (!($attributes['respond'] ?? $request->attributes->getBoolean('_api_respond'))) {
44✔
68
            return;
8✔
69
        }
70

71
        if ($operation && $this->processor instanceof ProcessorInterface) {
36✔
NEW
72
            $uriVariables = $request->attributes->get('_api_uri_variables') ?? [];
×
NEW
73
            $response = $this->processor->process($controllerResult, $operation, $uriVariables, [
×
NEW
74
                'request' => $request,
×
NEW
75
                'uri_variables' => $uriVariables,
×
NEW
76
                'resource_class' => $operation->getClass(),
×
NEW
77
                'original_data' => $request->attributes->get('original_data'),
×
NEW
78
            ]);
×
79

NEW
80
            $event->setResponse($response);
×
81

NEW
82
            return;
×
83
        }
84

85
        // TODO: the code below needs to be removed in 4.x
86
        if ('api_platform.symfony.main_controller' === $operation?->getController() || $request->attributes->get('_api_platform_disable_listeners')) {
36✔
87
            return;
×
88
        }
89

90
        $attributes = RequestAttributesExtractor::extractAttributes($request);
36✔
91

92
        if ($controllerResult instanceof Response && ($attributes['respond'] ?? false)) {
36✔
93
            $event->setResponse($controllerResult);
4✔
94

95
            return;
4✔
96
        }
97

98
        if ($controllerResult instanceof Response || !($attributes['respond'] ?? $request->attributes->getBoolean('_api_respond'))) {
32✔
UNCOV
99
            return;
×
100
        }
101

102
        $headers = [
32✔
103
            'Content-Type' => sprintf('%s; charset=utf-8', $request->getMimeType($request->getRequestFormat())),
32✔
104
            'Vary' => 'Accept',
32✔
105
            'X-Content-Type-Options' => 'nosniff',
32✔
106
            'X-Frame-Options' => 'deny',
32✔
107
        ];
32✔
108

109
        $status = $operation?->getStatus();
32✔
110

111
        if ($sunset = $operation?->getSunset()) {
32✔
112
            $headers['Sunset'] = (new \DateTimeImmutable($sunset))->format(\DateTime::RFC1123);
4✔
113
        }
114

115
        if ($acceptPatch = $operation?->getAcceptPatch()) {
32✔
116
            $headers['Accept-Patch'] = $acceptPatch;
×
117
        }
118

119
        $method = $request->getMethod();
32✔
120
        if (
121
            $this->iriConverter
32✔
122
            && $operation
123
            && ($operation->getExtraProperties()['is_alternate_resource_metadata'] ?? false)
32✔
124
            && 301 === $operation->getStatus()
32✔
125
        ) {
126
            $status = 301;
×
127
            $headers['Location'] = $this->iriConverter->getIriFromResource($request->attributes->get('data'), UrlGeneratorInterface::ABS_PATH, $operation);
×
128
        } elseif ('PUT' === $method && !($attributes['previous_data'] ?? null) && null === $status && ($operation instanceof Put && ($operation->getAllowCreate() ?? false))) {
32✔
129
            $status = Response::HTTP_CREATED;
×
130
        }
131

132
        $status ??= self::METHOD_TO_CODE[$request->getMethod()] ?? Response::HTTP_OK;
32✔
133

134
        if ($request->attributes->has('_api_write_item_iri')) {
32✔
135
            $headers['Content-Location'] = $request->attributes->get('_api_write_item_iri');
12✔
136

137
            if ((Response::HTTP_CREATED === $status || (300 <= $status && $status < 400)) && 'POST' === $method) {
12✔
138
                $headers['Location'] = $request->attributes->get('_api_write_item_iri');
8✔
139
            }
140
        }
141

142
        if (($exception = $request->attributes->get('data')) instanceof HttpExceptionInterface) {
32✔
143
            $headers = array_merge($headers, $exception->getHeaders());
×
144
        }
145

146
        $event->setResponse(new Response(
32✔
147
            $controllerResult,
32✔
148
            $status,
32✔
149
            $headers
32✔
150
        ));
32✔
151
    }
152
}
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