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

api-platform / core / 15775135891

20 Jun 2025 08:42AM UTC coverage: 22.065% (+0.2%) from 21.876%
15775135891

push

github

soyuka
Merge 4.1

13 of 103 new or added lines in 10 files covered. (12.62%)

868 existing lines in 35 files now uncovered.

11487 of 52060 relevant lines covered (22.06%)

21.72 hits per line

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

96.83
/src/State/Provider/ParameterProvider.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\Metadata\Parameter;
19
use ApiPlatform\State\Exception\ParameterNotSupportedException;
20
use ApiPlatform\State\Exception\ProviderNotFoundException;
21
use ApiPlatform\State\ParameterNotFound;
22
use ApiPlatform\State\ParameterProvider\ReadLinkParameterProvider;
23
use ApiPlatform\State\ProviderInterface;
24
use ApiPlatform\State\Util\ParameterParserTrait;
25
use ApiPlatform\State\Util\RequestParser;
26
use Psr\Container\ContainerInterface;
27

28
/**
29
 * Loops over parameters to:
30
 *   - compute its values set as extra properties from the Parameter object (`_api_values`)
31
 *   - call the Parameter::provider if any and updates the Operation
32
 *
33
 * @experimental
34
 */
35
final class ParameterProvider implements ProviderInterface
36
{
37
    use ParameterParserTrait;
38

39
    public function __construct(private readonly ?ProviderInterface $decorated = null, private readonly ?ContainerInterface $locator = null)
40
    {
41
    }
502✔
42

43
    public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
44
    {
45
        $request = $context['request'] ?? null;
487✔
46
        if ($request && null === $request->attributes->get('_api_query_parameters')) {
487✔
47
            $queryString = RequestParser::getQueryString($request);
461✔
48
            $request->attributes->set('_api_query_parameters', $queryString ? RequestParser::parseRequestParams($queryString) : []);
461✔
49
        }
50

51
        if ($request && null === $request->attributes->get('_api_header_parameters')) {
487✔
52
            $request->attributes->set('_api_header_parameters', $request->headers->all());
461✔
53
        }
54

55
        $parameters = $operation->getParameters();
487✔
56

57
        if ($operation instanceof HttpOperation && true === $operation->getStrictQueryParameterValidation()) {
487✔
58
            $keys = [];
4✔
59
            foreach ($parameters as $parameter) {
4✔
60
                $keys[] = $parameter->getKey();
4✔
61
            }
62

63
            foreach (array_keys($request->attributes->get('_api_query_parameters')) as $key) {
4✔
64
                if (!\in_array($key, $keys, true)) {
4✔
65
                    throw new ParameterNotSupportedException($key);
2✔
66
                }
67
            }
68
        }
69

70
        $context = ['operation' => $operation] + $context;
487✔
71

72
        foreach ($parameters ?? [] as $parameter) {
487✔
73
            $values = $this->getParameterValues($parameter, $request, $context);
284✔
74
            $value = $this->extractParameterValues($parameter, $values);
284✔
75
            // we force API Platform's value extraction, use _api_query_parameters or _api_header_parameters if you need to set a value
76
            if (isset($parameter->getExtraProperties()['_api_values'])) {
284✔
77
                unset($parameter->getExtraProperties()['_api_values']);
20✔
78
            }
79

80
            if (($default = $parameter->getSchema()['default'] ?? false) && ($value instanceof ParameterNotFound || !$value)) {
284✔
81
                $value = $default;
10✔
82
            }
83

84
            $parameter->setValue($value);
284✔
85
            $context['operation'] = $operation = $this->callParameterProvider($operation, $parameter, $values, $context);
284✔
86
        }
87

88
        if ($parameters) {
487✔
89
            $operation = $operation->withParameters($parameters);
280✔
90
        }
91

92
        if ($operation instanceof HttpOperation) {
487✔
93
            $operation = $this->handlePathParameters($operation, $uriVariables, $context);
461✔
94
        }
95

96
        $request?->attributes->set('_api_operation', $operation);
487✔
97
        $context['operation'] = $operation;
487✔
98

99
        return $this->decorated?->provide($operation, $uriVariables, $context);
487✔
100
    }
101

102
    /**
103
     * TODO: uriVariables could be a Parameters instance, it'd make things easier.
104
     *
105
     * @param array<string, mixed> $uriVariables
106
     * @param array<string, mixed> $context
107
     */
108
    private function handlePathParameters(HttpOperation $operation, array $uriVariables, array $context): HttpOperation
109
    {
110
        foreach ($operation->getUriVariables() ?? [] as $key => $uriVariable) {
461✔
111
            $uriVariable = $uriVariable->withKey($key);
172✔
112
            if ($uriVariable->getSecurity() && !$uriVariable->getProvider()) {
172✔
113
                $uriVariable = $uriVariable->withProvider(ReadLinkParameterProvider::class);
4✔
114
            }
115

116
            $values = $uriVariables;
172✔
117

118
            if (!\array_key_exists($key, $uriVariables)) {
172✔
119
                continue;
66✔
120
            }
121

122
            $value = $uriVariables[$key];
114✔
123
            // we force API Platform's value extraction, use _api_query_parameters or _api_header_parameters if you need to set a value
124
            if (isset($uriVariable->getExtraProperties()['_api_values'])) {
114✔
125
                unset($uriVariable->getExtraProperties()['_api_values']);
2✔
126
            }
127

128
            if (($default = $uriVariable->getSchema()['default'] ?? false) && ($value instanceof ParameterNotFound || !$value)) {
114✔
UNCOV
129
                $value = $default;
×
130
            }
131

132
            $uriVariable->setValue($value);
114✔
133
            if (($op = $this->callParameterProvider($operation, $uriVariable, $values, $context)) instanceof HttpOperation) {
114✔
134
                $context['operation'] = $operation = $op;
114✔
135
            }
136
        }
137

138
        return $operation;
461✔
139
    }
140

141
    /**
142
     * @param array<string,mixed> $context
143
     */
144
    private function callParameterProvider(Operation $operation, Parameter $parameter, mixed $values, array $context): Operation
145
    {
146
        if ($parameter->getValue() instanceof ParameterNotFound) {
388✔
147
            return $operation;
270✔
148
        }
149

150
        if (null === ($provider = $parameter->getProvider())) {
384✔
151
            return $operation;
362✔
152
        }
153

154
        if (\is_callable($provider)) {
32✔
155
            if (($op = $provider($parameter, $values, $context)) instanceof Operation) {
8✔
156
                $operation = $op;
6✔
157
            }
158

159
            return $operation;
6✔
160
        }
161

162
        if (\is_string($provider)) {
26✔
163
            if (!$this->locator->has($provider)) {
26✔
UNCOV
164
                throw new ProviderNotFoundException(\sprintf('Provider "%s" not found on operation "%s"', $provider, $operation->getName()));
×
165
            }
166

167
            $provider = $this->locator->get($provider);
26✔
168
        }
169

170
        if (($op = $provider->provide($parameter, $values, $context)) instanceof Operation) {
26✔
171
            $operation = $op;
24✔
172
        }
173

174
        return $operation;
24✔
175
    }
176
}
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