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

api-platform / core / 17063025732

19 Aug 2025 07:44AM UTC coverage: 22.236% (+0.05%) from 22.188%
17063025732

push

github

soyuka
test: skip mongodb bundle when extension is not loaded

11683 of 52542 relevant lines covered (22.24%)

24.04 hits per line

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

96.92
/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\StopwatchAwareInterface;
25
use ApiPlatform\State\StopwatchAwareTrait;
26
use ApiPlatform\State\Util\ParameterParserTrait;
27
use ApiPlatform\State\Util\RequestParser;
28
use Psr\Container\ContainerInterface;
29

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

42
    public function __construct(private readonly ?ProviderInterface $decorated = null, private readonly ?ContainerInterface $locator = null)
43
    {
44
    }
594✔
45

46
    public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
47
    {
48
        $this->stopwatch?->start('api_platform.provider.parameter');
579✔
49
        $request = $context['request'] ?? null;
579✔
50
        if ($request && null === $request->attributes->get('_api_query_parameters')) {
579✔
51
            $queryString = RequestParser::getQueryString($request);
553✔
52
            $request->attributes->set('_api_query_parameters', $queryString ? RequestParser::parseRequestParams($queryString) : []);
553✔
53
        }
54

55
        if ($request && null === $request->attributes->get('_api_header_parameters')) {
579✔
56
            $request->attributes->set('_api_header_parameters', $request->headers->all());
553✔
57
        }
58

59
        $parameters = $operation->getParameters();
579✔
60

61
        if ($operation instanceof HttpOperation && true === $operation->getStrictQueryParameterValidation()) {
579✔
62
            $keys = [];
4✔
63
            foreach ($parameters as $parameter) {
4✔
64
                $keys[] = $parameter->getKey();
4✔
65
            }
66

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

74
        $context = ['operation' => $operation] + $context;
579✔
75

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

84
            if (($default = $parameter->getSchema()['default'] ?? false) && ($value instanceof ParameterNotFound || !$value)) {
356✔
85
                $value = $default;
10✔
86
            }
87

88
            $parameter->setValue($value);
356✔
89
            $context['operation'] = $operation = $this->callParameterProvider($operation, $parameter, $values, $context);
356✔
90
        }
91

92
        if ($parameters) {
579✔
93
            $operation = $operation->withParameters($parameters);
352✔
94
        }
95

96
        if ($operation instanceof HttpOperation) {
579✔
97
            $operation = $this->handlePathParameters($operation, $uriVariables, $context);
553✔
98
        }
99

100
        $request?->attributes->set('_api_operation', $operation);
579✔
101
        $context['operation'] = $operation;
579✔
102
        $this->stopwatch?->stop('api_platform.provider.parameter');
579✔
103

104
        return $this->decorated?->provide($operation, $uriVariables, $context);
579✔
105
    }
106

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

121
            $values = $uriVariables;
212✔
122

123
            if (!\array_key_exists($key, $uriVariables)) {
212✔
124
                continue;
96✔
125
            }
126

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

133
            if (($default = $uriVariable->getSchema()['default'] ?? false) && ($value instanceof ParameterNotFound || !$value)) {
124✔
134
                $value = $default;
×
135
            }
136

137
            $uriVariable->setValue($value);
124✔
138
            if (($op = $this->callParameterProvider($operation, $uriVariable, $values, $context)) instanceof HttpOperation) {
124✔
139
                $context['operation'] = $operation = $op;
124✔
140
            }
141
        }
142

143
        return $operation;
553✔
144
    }
145

146
    /**
147
     * @param array<string,mixed> $context
148
     */
149
    private function callParameterProvider(Operation $operation, Parameter $parameter, mixed $values, array $context): Operation
150
    {
151
        if ($parameter->getValue() instanceof ParameterNotFound) {
470✔
152
            return $operation;
342✔
153
        }
154

155
        if (null === ($provider = $parameter->getProvider())) {
458✔
156
            return $operation;
436✔
157
        }
158

159
        if (\is_callable($provider)) {
32✔
160
            if (($op = $provider($parameter, $values, $context)) instanceof Operation) {
8✔
161
                $operation = $op;
6✔
162
            }
163

164
            return $operation;
6✔
165
        }
166

167
        if (\is_string($provider)) {
26✔
168
            if (!$this->locator->has($provider)) {
26✔
169
                throw new ProviderNotFoundException(\sprintf('Provider "%s" not found on operation "%s"', $provider, $operation->getName()));
×
170
            }
171

172
            $provider = $this->locator->get($provider);
26✔
173
        }
174

175
        if (($op = $provider->provide($parameter, $values, $context)) instanceof Operation) {
26✔
176
            $operation = $op;
24✔
177
        }
178

179
        return $operation;
24✔
180
    }
181
}
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