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

api-platform / core / 10323454690

09 Aug 2024 05:46PM UTC coverage: 7.841%. Remained the same
10323454690

push

github

soyuka
Merge 3.4

2 of 2 new or added lines in 1 file covered. (100.0%)

1896 existing lines in 118 files now uncovered.

12688 of 161818 relevant lines covered (7.84%)

26.86 hits per line

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

97.87
/src/State/Processor/RespondProcessor.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\Processor;
15

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

30
/**
31
 * Serializes data.
32
 *
33
 * @author Kévin Dunglas <dunglas@gmail.com>
34
 */
35
final class RespondProcessor implements ProcessorInterface
36
{
37
    use ClassInfoTrait;
38
    use CloneTrait;
39

40
    public const METHOD_TO_CODE = [
41
        'POST' => Response::HTTP_CREATED,
42
        'DELETE' => Response::HTTP_NO_CONTENT,
43
    ];
44

45
    public function __construct(
46
        private ?IriConverterInterface $iriConverter = null,
47
        private readonly ?ResourceClassResolverInterface $resourceClassResolver = null,
48
        private readonly ?OperationMetadataFactoryInterface $operationMetadataFactory = null,
49
    ) {
50
    }
2,169✔
51

52
    public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = [])
53
    {
54
        if ($data instanceof Response || !$operation instanceof HttpOperation) {
2,151✔
55
            return $data;
28✔
56
        }
57

58
        if (!($request = $context['request'] ?? null)) {
2,125✔
59
            return $data;
×
60
        }
61

62
        $headers = [
2,125✔
63
            'Content-Type' => \sprintf('%s; charset=utf-8', $request->getMimeType($request->getRequestFormat())),
2,125✔
64
            'Vary' => 'Accept',
2,125✔
65
            'X-Content-Type-Options' => 'nosniff',
2,125✔
66
            'X-Frame-Options' => 'deny',
2,125✔
67
        ];
2,125✔
68

69
        $exception = $request->attributes->get('exception');
2,125✔
70
        if (($exception instanceof HttpExceptionInterface || $exception instanceof SymfonyHttpExceptionInterface) && $exceptionHeaders = $exception->getHeaders()) {
2,125✔
71
            $headers = array_merge($headers, $exceptionHeaders);
5✔
72
        }
73

74
        if ($operationHeaders = $operation->getHeaders()) {
2,125✔
75
            $headers = array_merge($headers, $operationHeaders);
6✔
76
        }
77

78
        $status = $operation->getStatus();
2,125✔
79

80
        if ($sunset = $operation->getSunset()) {
2,125✔
UNCOV
81
            $headers['Sunset'] = (new \DateTimeImmutable($sunset))->format(\DateTimeInterface::RFC1123);
6✔
82
        }
83

84
        if ($acceptPatch = $operation->getAcceptPatch()) {
2,125✔
85
            $headers['Accept-Patch'] = $acceptPatch;
556✔
86
        }
87

88
        $method = $request->getMethod();
2,125✔
89
        $originalData = $context['original_data'] ?? null;
2,125✔
90

91
        $outputMetadata = $operation->getOutput() ?? ['class' => $operation->getClass()];
2,125✔
92
        $hasOutput = \is_array($outputMetadata) && \array_key_exists('class', $outputMetadata) && null !== $outputMetadata['class'];
2,125✔
93
        $hasData = !$hasOutput ? false : ($this->resourceClassResolver && $originalData && \is_object($originalData) && $this->resourceClassResolver->isResourceClass($this->getObjectClass($originalData)));
2,125✔
94

95
        if ($hasData && $this->iriConverter) {
2,125✔
96
            if (
97
                !isset($headers['Location'])
1,185✔
98
                && 300 <= $status && $status < 400
1,185✔
99
                && (($operation->getExtraProperties()['is_alternate_resource_metadata'] ?? false) || ($operation->getExtraProperties()['canonical_uri_template'] ?? null))
1,185✔
100
            ) {
101
                $canonicalOperation = $operation;
5✔
102
                if ($this->operationMetadataFactory && null !== ($operation->getExtraProperties()['canonical_uri_template'] ?? null)) {
5✔
103
                    $canonicalOperation = $this->operationMetadataFactory->create($operation->getExtraProperties()['canonical_uri_template'], $context);
3✔
104
                }
105

106
                $headers['Location'] = $this->iriConverter->getIriFromResource($originalData, UrlGeneratorInterface::ABS_PATH, $canonicalOperation);
5✔
107
            } elseif ('PUT' === $method && !$request->attributes->get('previous_data') && null === $status && ($operation instanceof Put && ($operation->getAllowCreate() ?? false))) {
1,183✔
UNCOV
108
                $status = 201;
7✔
109
            }
110
        }
111

112
        $status ??= self::METHOD_TO_CODE[$method] ?? 200;
2,125✔
113

114
        if ($hasData && $this->iriConverter && !isset($headers['Content-Location'])) {
2,125✔
115
            $iri = $this->iriConverter->getIriFromResource($originalData);
1,185✔
116
            $headers['Content-Location'] = $iri;
1,185✔
117

118
            if ((201 === $status || (300 <= $status && $status < 400)) && 'POST' === $method && !isset($headers['Location'])) {
1,185✔
119
                $headers['Location'] = $iri;
376✔
120
            }
121
        }
122

123
        return new Response(
2,125✔
124
            $data,
2,125✔
125
            $status,
2,125✔
126
            $headers
2,125✔
127
        );
2,125✔
128
    }
129
}
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