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

api-platform / core / 20847864477

09 Jan 2026 09:47AM UTC coverage: 29.1% (+0.005%) from 29.095%
20847864477

Pull #7649

github

web-flow
Merge b342dd5db into d640d106b
Pull Request #7649: feat(validator): uuid/ulid parameter validation

0 of 4 new or added lines in 1 file covered. (0.0%)

15050 existing lines in 491 files now uncovered.

16996 of 58406 relevant lines covered (29.1%)

81.8 hits per line

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

100.0
/src/State/Util/HttpResponseHeadersTrait.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\Util;
15

16
use ApiPlatform\Metadata\Exception\HttpExceptionInterface;
17
use ApiPlatform\Metadata\Exception\InvalidArgumentException;
18
use ApiPlatform\Metadata\Exception\ItemNotFoundException;
19
use ApiPlatform\Metadata\Exception\RuntimeException;
20
use ApiPlatform\Metadata\HttpOperation;
21
use ApiPlatform\Metadata\IriConverterInterface;
22
use ApiPlatform\Metadata\Operation\Factory\OperationMetadataFactoryInterface;
23
use ApiPlatform\Metadata\UrlGeneratorInterface;
24
use ApiPlatform\Metadata\Util\ClassInfoTrait;
25
use ApiPlatform\Metadata\Util\CloneTrait;
26
use Symfony\Component\HttpFoundation\Request;
27
use Symfony\Component\HttpFoundation\Response;
28
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface as SymfonyHttpExceptionInterface;
29

30
/**
31
 * Shares the logic to create API Platform's headers.
32
 *
33
 * @internal
34
 */
35
trait HttpResponseHeadersTrait
36
{
37
    use ClassInfoTrait;
38
    use CloneTrait;
39
    private ?IriConverterInterface $iriConverter;
40
    private ?OperationMetadataFactoryInterface $operationMetadataFactory;
41

42
    /**
43
     * @param array<string, mixed> $context
44
     *
45
     * @return array<string, string|string[]>
46
     */
47
    private function getHeaders(Request $request, HttpOperation $operation, array $context): array
48
    {
UNCOV
49
        $status = $this->getStatus($request, $operation, $context);
1,992✔
UNCOV
50
        $headers = [
1,992✔
UNCOV
51
            'Content-Type' => \sprintf('%s; charset=utf-8', $request->getMimeType($request->getRequestFormat())),
1,992✔
UNCOV
52
            'Vary' => 'Accept',
1,992✔
UNCOV
53
            'X-Content-Type-Options' => 'nosniff',
1,992✔
UNCOV
54
            'X-Frame-Options' => 'deny',
1,992✔
UNCOV
55
        ];
1,992✔
56

UNCOV
57
        $exception = $request->attributes->get('exception');
1,992✔
UNCOV
58
        if (($exception instanceof HttpExceptionInterface || $exception instanceof SymfonyHttpExceptionInterface) && $exceptionHeaders = $exception->getHeaders()) {
1,992✔
UNCOV
59
            $headers = array_merge($headers, $exceptionHeaders);
4✔
60
        }
61

UNCOV
62
        if ($operationHeaders = $operation->getHeaders()) {
1,992✔
UNCOV
63
            $headers = array_merge($headers, $operationHeaders);
6✔
64
        }
65

UNCOV
66
        if ($sunset = $operation->getSunset()) {
1,992✔
67
            $headers['Sunset'] = (new \DateTimeImmutable($sunset))->format(\DateTimeInterface::RFC1123);
4✔
68
        }
69

UNCOV
70
        if ($acceptPatch = $operation->getAcceptPatch()) {
1,992✔
UNCOV
71
            $headers['Accept-Patch'] = $acceptPatch;
410✔
72
        }
73

UNCOV
74
        $method = $request->getMethod();
1,992✔
UNCOV
75
        $originalData = $context['original_data'] ?? null;
1,992✔
UNCOV
76
        $outputMetadata = $operation->getOutput() ?? ['class' => $operation->getClass()];
1,992✔
UNCOV
77
        $hasOutput = \is_array($outputMetadata) && \array_key_exists('class', $outputMetadata) && null !== $outputMetadata['class'];
1,992✔
UNCOV
78
        $hasData = !$hasOutput ? false : ($this->resourceClassResolver && $originalData && \is_object($originalData) && $this->resourceClassResolver->isResourceClass($this->getObjectClass($originalData)));
1,992✔
79

UNCOV
80
        if ($hasData) {
1,992✔
UNCOV
81
            $isAlternateResourceMetadata = $operation->getExtraProperties()['is_alternate_resource_metadata'] ?? false;
974✔
UNCOV
82
            $canonicalUriTemplate = $operation->getExtraProperties()['canonical_uri_template'] ?? null;
974✔
83

84
            if (
UNCOV
85
                !isset($headers['Location'])
974✔
UNCOV
86
                && 300 <= $status && $status < 400
974✔
UNCOV
87
                && ($isAlternateResourceMetadata || $canonicalUriTemplate)
974✔
88
            ) {
UNCOV
89
                $canonicalOperation = $operation;
4✔
UNCOV
90
                if ($this->operationMetadataFactory && null !== $canonicalUriTemplate) {
4✔
UNCOV
91
                    $canonicalOperation = $this->operationMetadataFactory->create($canonicalUriTemplate, $context);
2✔
92
                }
93

UNCOV
94
                if ($this->iriConverter) {
4✔
UNCOV
95
                    $headers['Location'] = $this->iriConverter->getIriFromResource($originalData, UrlGeneratorInterface::ABS_PATH, $canonicalOperation);
4✔
96
                }
97
            }
98
        }
99

UNCOV
100
        $requestParts = parse_url($request->getRequestUri());
1,992✔
UNCOV
101
        if ($this->iriConverter && !isset($headers['Content-Location'])) {
1,992✔
102
            try {
UNCOV
103
                $iri = null;
1,988✔
UNCOV
104
                if ($hasData) {
1,988✔
UNCOV
105
                    $iri = $this->iriConverter->getIriFromResource($originalData);
974✔
UNCOV
106
                } elseif ($operation->getClass()) {
1,066✔
UNCOV
107
                    $iri = $this->iriConverter->getIriFromResource($operation->getClass(), UrlGeneratorInterface::ABS_PATH, $operation);
1,048✔
108
                }
109

UNCOV
110
                if ($iri && 'GET' !== $method) {
1,854✔
UNCOV
111
                    $location = \sprintf('%s.%s', $iri, $request->getRequestFormat());
369✔
UNCOV
112
                    if (isset($requestParts['query'])) {
369✔
113
                        $location .= '?'.$requestParts['query'];
24✔
114
                    }
115

UNCOV
116
                    $headers['Content-Location'] = $location;
369✔
UNCOV
117
                    if ((Response::HTTP_CREATED === $status || (300 <= $status && $status < 400)) && 'POST' === $method && !isset($headers['Location'])) {
369✔
UNCOV
118
                        $headers['Location'] = $iri;
1,854✔
119
                    }
120
                }
UNCOV
121
            } catch (InvalidArgumentException|ItemNotFoundException|RuntimeException) {
144✔
122
            }
123
        }
124

UNCOV
125
        return $headers;
1,992✔
126
    }
127
}
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