• 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

99.43
/src/Symfony/Bundle/DependencyInjection/Configuration.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\Bundle\DependencyInjection;
15

16
use ApiPlatform\Doctrine\Common\Filter\OrderFilterInterface;
17
use ApiPlatform\Metadata\ApiResource;
18
use ApiPlatform\Metadata\Exception\InvalidArgumentException;
19
use ApiPlatform\Metadata\Post;
20
use ApiPlatform\Metadata\Put;
21
use ApiPlatform\Symfony\Controller\MainController;
22
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
23
use Doctrine\Bundle\MongoDBBundle\DoctrineMongoDBBundle;
24
use Doctrine\ORM\EntityManagerInterface;
25
use Doctrine\ORM\OptimisticLockException;
26
use GraphQL\GraphQL;
27
use Symfony\Bundle\FrameworkBundle\Controller\ControllerHelper;
28
use Symfony\Bundle\FullStack;
29
use Symfony\Bundle\MakerBundle\MakerBundle;
30
use Symfony\Bundle\MercureBundle\MercureBundle;
31
use Symfony\Bundle\TwigBundle\TwigBundle;
32
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
33
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
34
use Symfony\Component\Config\Definition\ConfigurationInterface;
35
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
36
use Symfony\Component\HttpFoundation\Response;
37
use Symfony\Component\JsonStreamer\JsonStreamWriter;
38
use Symfony\Component\Messenger\MessageBusInterface;
39
use Symfony\Component\Serializer\Exception\ExceptionInterface as SerializerExceptionInterface;
40
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
41
use Symfony\Component\Yaml\Yaml;
42

43
/**
44
 * The configuration of the bundle.
45
 *
46
 * @author Kévin Dunglas <dunglas@gmail.com>
47
 * @author Baptiste Meyer <baptiste.meyer@gmail.com>
48
 */
49
final class Configuration implements ConfigurationInterface
50
{
51
    /**
52
     * {@inheritdoc}
53
     */
54
    public function getConfigTreeBuilder(): TreeBuilder
55
    {
UNCOV
56
        $treeBuilder = new TreeBuilder('api_platform');
58✔
UNCOV
57
        $rootNode = $treeBuilder->getRootNode();
58✔
58

UNCOV
59
        $rootNode
58✔
UNCOV
60
            ->beforeNormalization()
58✔
UNCOV
61
                ->ifTrue(static function ($v) {
58✔
UNCOV
62
                    return false === ($v['enable_swagger'] ?? null);
58✔
UNCOV
63
                })
58✔
UNCOV
64
                ->then(static function ($v) {
58✔
UNCOV
65
                    $v['swagger']['versions'] = [];
2✔
66

UNCOV
67
                    return $v;
2✔
UNCOV
68
                })
58✔
UNCOV
69
            ->end()
58✔
UNCOV
70
            ->children()
58✔
UNCOV
71
                ->scalarNode('title')
58✔
UNCOV
72
                    ->info('The title of the API.')
58✔
UNCOV
73
                    ->cannotBeEmpty()
58✔
UNCOV
74
                    ->defaultValue('')
58✔
UNCOV
75
                ->end()
58✔
UNCOV
76
                ->scalarNode('description')
58✔
UNCOV
77
                    ->info('The description of the API.')
58✔
UNCOV
78
                    ->cannotBeEmpty()
58✔
UNCOV
79
                    ->defaultValue('')
58✔
UNCOV
80
                ->end()
58✔
UNCOV
81
                ->scalarNode('version')
58✔
UNCOV
82
                    ->info('The version of the API.')
58✔
UNCOV
83
                    ->cannotBeEmpty()
58✔
UNCOV
84
                    ->defaultValue('0.0.0')
58✔
UNCOV
85
                ->end()
58✔
UNCOV
86
                ->booleanNode('show_webby')->defaultTrue()->info('If true, show Webby on the documentation page')->end()
58✔
UNCOV
87
                ->booleanNode('use_symfony_listeners')->defaultFalse()->info(sprintf('Uses Symfony event listeners instead of the %s.', MainController::class))->end()
58✔
UNCOV
88
                ->scalarNode('name_converter')->defaultNull()->info('Specify a name converter to use.')->end()
58✔
UNCOV
89
                ->scalarNode('asset_package')->defaultNull()->info('Specify an asset package name to use.')->end()
58✔
UNCOV
90
                ->scalarNode('path_segment_name_generator')->defaultValue('api_platform.metadata.path_segment_name_generator.underscore')->info('Specify a path name generator to use.')->end()
58✔
UNCOV
91
                ->scalarNode('inflector')->defaultValue('api_platform.metadata.inflector')->info('Specify an inflector to use.')->end()
58✔
UNCOV
92
                ->arrayNode('validator')
58✔
UNCOV
93
                    ->addDefaultsIfNotSet()
58✔
UNCOV
94
                    ->children()
58✔
UNCOV
95
                        ->variableNode('serialize_payload_fields')->defaultValue([])->info('Set to null to serialize all payload fields when a validation error is thrown, or set the fields you want to include explicitly.')->end()
58✔
UNCOV
96
                        ->booleanNode('query_parameter_validation')
58✔
UNCOV
97
                            ->defaultValue(true)
58✔
UNCOV
98
                            ->setDeprecated('api-platform/symfony', '4.2', 'Will be removed in API Platform 5.0.')
58✔
UNCOV
99
                        ->end()
58✔
UNCOV
100
                    ->end()
58✔
UNCOV
101
                ->end()
58✔
UNCOV
102
                ->arrayNode('eager_loading')
58✔
UNCOV
103
                    ->canBeDisabled()
58✔
UNCOV
104
                    ->addDefaultsIfNotSet()
58✔
UNCOV
105
                    ->children()
58✔
UNCOV
106
                        ->booleanNode('fetch_partial')->defaultFalse()->info('Fetch only partial data according to serialization groups. If enabled, Doctrine ORM entities will not work as expected if any of the other fields are used.')->end()
58✔
UNCOV
107
                        ->integerNode('max_joins')->defaultValue(30)->info('Max number of joined relations before EagerLoading throws a RuntimeException')->end()
58✔
UNCOV
108
                        ->booleanNode('force_eager')->defaultTrue()->info('Force join on every relation. If disabled, it will only join relations having the EAGER fetch mode.')->end()
58✔
UNCOV
109
                    ->end()
58✔
UNCOV
110
                ->end()
58✔
UNCOV
111
                ->booleanNode('handle_symfony_errors')->defaultFalse()->info('Allows to handle symfony exceptions.')->end()
58✔
UNCOV
112
                ->booleanNode('enable_swagger')->defaultTrue()->info('Enable the Swagger documentation and export.')->end()
58✔
UNCOV
113
                ->booleanNode('enable_json_streamer')->defaultValue(class_exists(ControllerHelper::class) && class_exists(JsonStreamWriter::class))->info('Enable json streamer.')->end()
58✔
UNCOV
114
                ->booleanNode('enable_swagger_ui')->defaultValue(class_exists(TwigBundle::class))->info('Enable Swagger UI')->end()
58✔
UNCOV
115
                ->booleanNode('enable_re_doc')->defaultValue(class_exists(TwigBundle::class))->info('Enable ReDoc')->end()
58✔
UNCOV
116
                ->booleanNode('enable_entrypoint')->defaultTrue()->info('Enable the entrypoint')->end()
58✔
UNCOV
117
                ->booleanNode('enable_docs')->defaultTrue()->info('Enable the docs')->end()
58✔
UNCOV
118
                ->booleanNode('enable_profiler')->defaultTrue()->info('Enable the data collector and the WebProfilerBundle integration.')->end()
58✔
UNCOV
119
                ->booleanNode('enable_phpdoc_parser')->defaultTrue()->info('Enable resource metadata collector using PHPStan PhpDocParser.')->end()
58✔
UNCOV
120
                ->booleanNode('enable_link_security')
58✔
UNCOV
121
                    ->defaultTrue()
58✔
UNCOV
122
                    ->info('Enable security for Links (sub resources).')
58✔
UNCOV
123
                    ->setDeprecated('api-platform/symfony', '4.2', 'This option is always enabled and will be removed in API Platform 5.0.')
58✔
UNCOV
124
                ->end()
58✔
UNCOV
125
                ->arrayNode('collection')
58✔
UNCOV
126
                    ->addDefaultsIfNotSet()
58✔
UNCOV
127
                    ->children()
58✔
UNCOV
128
                        ->scalarNode('exists_parameter_name')->defaultValue('exists')->cannotBeEmpty()->info('The name of the query parameter to filter on nullable field values.')->end()
58✔
UNCOV
129
                        ->scalarNode('order')->defaultValue('ASC')->info('The default order of results.')->end() // Default ORDER is required for postgresql and mysql >= 5.7 when using LIMIT/OFFSET request
58✔
UNCOV
130
                        ->scalarNode('order_parameter_name')->defaultValue('order')->cannotBeEmpty()->info('The name of the query parameter to order results.')->end()
58✔
UNCOV
131
                        ->enumNode('order_nulls_comparison')->defaultNull()->values(interface_exists(OrderFilterInterface::class) ? array_merge(array_keys(OrderFilterInterface::NULLS_DIRECTION_MAP), [null]) : [null])->info('The nulls comparison strategy.')->end()
58✔
UNCOV
132
                        ->arrayNode('pagination')
58✔
UNCOV
133
                            ->canBeDisabled()
58✔
UNCOV
134
                            ->addDefaultsIfNotSet()
58✔
UNCOV
135
                            ->children()
58✔
UNCOV
136
                                ->scalarNode('page_parameter_name')->defaultValue('page')->cannotBeEmpty()->info('The default name of the parameter handling the page number.')->end()
58✔
UNCOV
137
                                ->scalarNode('enabled_parameter_name')->defaultValue('pagination')->cannotBeEmpty()->info('The name of the query parameter to enable or disable pagination.')->end()
58✔
UNCOV
138
                                ->scalarNode('items_per_page_parameter_name')->defaultValue('itemsPerPage')->cannotBeEmpty()->info('The name of the query parameter to set the number of items per page.')->end()
58✔
UNCOV
139
                                ->scalarNode('partial_parameter_name')->defaultValue('partial')->cannotBeEmpty()->info('The name of the query parameter to enable or disable partial pagination.')->end()
58✔
UNCOV
140
                            ->end()
58✔
UNCOV
141
                        ->end()
58✔
UNCOV
142
                    ->end()
58✔
UNCOV
143
                ->end()
58✔
UNCOV
144
                ->arrayNode('mapping')
58✔
UNCOV
145
                    ->addDefaultsIfNotSet()
58✔
UNCOV
146
                    ->children()
58✔
UNCOV
147
                        ->arrayNode('imports')
58✔
UNCOV
148
                            ->prototype('scalar')->end()
58✔
UNCOV
149
                        ->end()
58✔
UNCOV
150
                        ->arrayNode('paths')
58✔
UNCOV
151
                            ->prototype('scalar')->end()
58✔
UNCOV
152
                        ->end()
58✔
UNCOV
153
                    ->end()
58✔
UNCOV
154
                ->end()
58✔
UNCOV
155
                ->arrayNode('resource_class_directories')
58✔
UNCOV
156
                    ->prototype('scalar')->end()
58✔
UNCOV
157
                    ->setDeprecated('api-platform/symfony', '4.1', 'The "resource_class_directories" configuration is deprecated, classes using #[ApiResource] attribute are autoconfigured by the dependency injection container.')
58✔
UNCOV
158
                ->end()
58✔
UNCOV
159
                ->arrayNode('serializer')
58✔
UNCOV
160
                    ->addDefaultsIfNotSet()
58✔
UNCOV
161
                    ->children()
58✔
UNCOV
162
                        ->booleanNode('hydra_prefix')->defaultFalse()->info('Use the "hydra:" prefix.')->end()
58✔
UNCOV
163
                    ->end()
58✔
UNCOV
164
                ->end()
58✔
UNCOV
165
            ->end();
58✔
166

UNCOV
167
        $this->addDoctrineOrmSection($rootNode);
58✔
UNCOV
168
        $this->addDoctrineMongoDbOdmSection($rootNode);
58✔
UNCOV
169
        $this->addOAuthSection($rootNode);
58✔
UNCOV
170
        $this->addGraphQlSection($rootNode);
58✔
UNCOV
171
        $this->addSwaggerSection($rootNode);
58✔
UNCOV
172
        $this->addHttpCacheSection($rootNode);
58✔
UNCOV
173
        $this->addMercureSection($rootNode);
58✔
UNCOV
174
        $this->addMessengerSection($rootNode);
58✔
UNCOV
175
        $this->addElasticsearchSection($rootNode);
58✔
UNCOV
176
        $this->addOpenApiSection($rootNode);
58✔
UNCOV
177
        $this->addMakerSection($rootNode);
58✔
178

UNCOV
179
        $this->addExceptionToStatusSection($rootNode);
58✔
180

UNCOV
181
        $this->addFormatSection($rootNode, 'formats', [
58✔
UNCOV
182
            'jsonld' => ['mime_types' => ['application/ld+json']],
58✔
UNCOV
183
        ]);
58✔
UNCOV
184
        $this->addFormatSection($rootNode, 'patch_formats', [
58✔
UNCOV
185
            'json' => ['mime_types' => ['application/merge-patch+json']],
58✔
UNCOV
186
        ]);
58✔
187

UNCOV
188
        $defaultDocFormats = [
58✔
UNCOV
189
            'jsonld' => ['mime_types' => ['application/ld+json']],
58✔
UNCOV
190
            'jsonopenapi' => ['mime_types' => ['application/vnd.openapi+json']],
58✔
UNCOV
191
            'html' => ['mime_types' => ['text/html']],
58✔
UNCOV
192
        ];
58✔
193

UNCOV
194
        if (class_exists(Yaml::class)) {
58✔
UNCOV
195
            $defaultDocFormats['yamlopenapi'] = ['mime_types' => ['application/vnd.openapi+yaml']];
58✔
196
        }
197

UNCOV
198
        $this->addFormatSection($rootNode, 'docs_formats', $defaultDocFormats);
58✔
199

UNCOV
200
        $this->addFormatSection($rootNode, 'error_formats', [
58✔
UNCOV
201
            'jsonld' => ['mime_types' => ['application/ld+json']],
58✔
UNCOV
202
            'jsonproblem' => ['mime_types' => ['application/problem+json']],
58✔
UNCOV
203
            'json' => ['mime_types' => ['application/problem+json', 'application/json']],
58✔
UNCOV
204
        ]);
58✔
UNCOV
205
        $rootNode
58✔
UNCOV
206
            ->children()
58✔
UNCOV
207
                ->arrayNode('jsonschema_formats')
58✔
UNCOV
208
                    ->scalarPrototype()->end()
58✔
UNCOV
209
                    ->defaultValue([])
58✔
UNCOV
210
                    ->info('The JSON formats to compute the JSON Schemas for.')
58✔
UNCOV
211
                ->end()
58✔
UNCOV
212
            ->end();
58✔
213

UNCOV
214
        $this->addDefaultsSection($rootNode);
58✔
215

UNCOV
216
        return $treeBuilder;
58✔
217
    }
218

219
    private function addDoctrineOrmSection(ArrayNodeDefinition $rootNode): void
220
    {
UNCOV
221
        $rootNode
58✔
UNCOV
222
            ->children()
58✔
UNCOV
223
                ->arrayNode('doctrine')
58✔
UNCOV
224
                    ->{class_exists(DoctrineBundle::class) && interface_exists(EntityManagerInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}()
58✔
UNCOV
225
                ->end()
58✔
UNCOV
226
            ->end();
58✔
227
    }
228

229
    private function addDoctrineMongoDbOdmSection(ArrayNodeDefinition $rootNode): void
230
    {
UNCOV
231
        $rootNode
58✔
UNCOV
232
            ->children()
58✔
UNCOV
233
                ->arrayNode('doctrine_mongodb_odm')
58✔
UNCOV
234
                    ->{class_exists(DoctrineMongoDBBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
58✔
UNCOV
235
                ->end()
58✔
UNCOV
236
            ->end();
58✔
237
    }
238

239
    private function addOAuthSection(ArrayNodeDefinition $rootNode): void
240
    {
UNCOV
241
        $rootNode
58✔
UNCOV
242
            ->children()
58✔
UNCOV
243
                ->arrayNode('oauth')
58✔
UNCOV
244
                    ->canBeEnabled()
58✔
UNCOV
245
                    ->addDefaultsIfNotSet()
58✔
UNCOV
246
                    ->children()
58✔
UNCOV
247
                        ->scalarNode('clientId')->defaultValue('')->info('The oauth client id.')->end()
58✔
UNCOV
248
                        ->scalarNode('clientSecret')
58✔
UNCOV
249
                            ->defaultValue('')
58✔
UNCOV
250
                            ->info('The OAuth client secret. Never use this parameter in your production environment. It exposes crucial security information. This feature is intended for dev/test environments only. Enable "oauth.pkce" instead')
58✔
UNCOV
251
                        ->end()
58✔
UNCOV
252
                        ->booleanNode('pkce')->defaultFalse()->info('Enable the oauth PKCE.')->end()
58✔
UNCOV
253
                        ->scalarNode('type')->defaultValue('oauth2')->info('The oauth type.')->end()
58✔
UNCOV
254
                        ->scalarNode('flow')->defaultValue('application')->info('The oauth flow grant type.')->end()
58✔
UNCOV
255
                        ->scalarNode('tokenUrl')->defaultValue('')->info('The oauth token url.')->end()
58✔
UNCOV
256
                        ->scalarNode('authorizationUrl')->defaultValue('')->info('The oauth authentication url.')->end()
58✔
UNCOV
257
                        ->scalarNode('refreshUrl')->defaultValue('')->info('The oauth refresh url.')->end()
58✔
UNCOV
258
                        ->arrayNode('scopes')
58✔
UNCOV
259
                            ->prototype('scalar')->end()
58✔
UNCOV
260
                        ->end()
58✔
UNCOV
261
                    ->end()
58✔
UNCOV
262
                ->end()
58✔
UNCOV
263
            ->end();
58✔
264
    }
265

266
    private function addGraphQlSection(ArrayNodeDefinition $rootNode): void
267
    {
UNCOV
268
        $rootNode
58✔
UNCOV
269
            ->children()
58✔
UNCOV
270
                ->arrayNode('graphql')
58✔
UNCOV
271
                    ->{class_exists(GraphQL::class) ? 'canBeDisabled' : 'canBeEnabled'}()
58✔
UNCOV
272
                    ->addDefaultsIfNotSet()
58✔
UNCOV
273
                    ->children()
58✔
UNCOV
274
                        ->scalarNode('default_ide')->defaultValue('graphiql')->end()
58✔
UNCOV
275
                        ->arrayNode('graphiql')
58✔
UNCOV
276
                            ->{class_exists(GraphQL::class) && class_exists(TwigBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
58✔
UNCOV
277
                        ->end()
58✔
UNCOV
278
                        ->arrayNode('introspection')
58✔
UNCOV
279
                            ->canBeDisabled()
58✔
UNCOV
280
                        ->end()
58✔
UNCOV
281
                        ->integerNode('max_query_depth')->defaultValue(20)
58✔
UNCOV
282
                        ->end()
58✔
UNCOV
283
                        ->arrayNode('graphql_playground')
58✔
UNCOV
284
                            ->setDeprecated('api-platform/core', '4.0')
58✔
UNCOV
285
                        ->end()
58✔
UNCOV
286
                        ->integerNode('max_query_complexity')->defaultValue(500)
58✔
UNCOV
287
                        ->end()
58✔
UNCOV
288
                        ->scalarNode('nesting_separator')->defaultValue('_')->info('The separator to use to filter nested fields.')->end()
58✔
UNCOV
289
                        ->arrayNode('collection')
58✔
UNCOV
290
                            ->addDefaultsIfNotSet()
58✔
UNCOV
291
                            ->children()
58✔
UNCOV
292
                                ->arrayNode('pagination')
58✔
UNCOV
293
                                    ->canBeDisabled()
58✔
UNCOV
294
                                ->end()
58✔
UNCOV
295
                            ->end()
58✔
UNCOV
296
                        ->end()
58✔
UNCOV
297
                    ->end()
58✔
UNCOV
298
                ->end()
58✔
UNCOV
299
            ->end();
58✔
300
    }
301

302
    private function addSwaggerSection(ArrayNodeDefinition $rootNode): void
303
    {
UNCOV
304
        $supportedVersions = [3];
58✔
305

UNCOV
306
        $rootNode
58✔
UNCOV
307
            ->children()
58✔
UNCOV
308
                ->arrayNode('swagger')
58✔
UNCOV
309
                    ->addDefaultsIfNotSet()
58✔
UNCOV
310
                    ->children()
58✔
UNCOV
311
                        ->booleanNode('persist_authorization')->defaultValue(false)->info('Persist the SwaggerUI Authorization in the localStorage.')->end()
58✔
UNCOV
312
                        ->arrayNode('versions')
58✔
UNCOV
313
                            ->info('The active versions of OpenAPI to be exported or used in Swagger UI. The first value is the default.')
58✔
UNCOV
314
                            ->defaultValue($supportedVersions)
58✔
UNCOV
315
                            ->beforeNormalization()
58✔
UNCOV
316
                                ->always(static function ($v): array {
58✔
UNCOV
317
                                    if (!\is_array($v)) {
4✔
318
                                        $v = [$v];
×
319
                                    }
320

UNCOV
321
                                    foreach ($v as &$version) {
4✔
UNCOV
322
                                        $version = (int) $version;
2✔
323
                                    }
324

UNCOV
325
                                    return $v;
4✔
UNCOV
326
                                })
58✔
UNCOV
327
                            ->end()
58✔
UNCOV
328
                            ->validate()
58✔
UNCOV
329
                                ->ifTrue(static fn ($v): bool => $v !== array_intersect($v, $supportedVersions))
58✔
UNCOV
330
                                ->thenInvalid(sprintf('Only the versions %s are supported. Got %s.', implode(' and ', $supportedVersions), '%s'))
58✔
UNCOV
331
                            ->end()
58✔
UNCOV
332
                            ->prototype('scalar')->end()
58✔
UNCOV
333
                        ->end()
58✔
UNCOV
334
                        ->arrayNode('api_keys')
58✔
UNCOV
335
                            ->useAttributeAsKey('key')
58✔
UNCOV
336
                            ->validate()
58✔
UNCOV
337
                                ->ifTrue(static fn ($v): bool => (bool) array_filter(array_keys($v), fn ($item) => !preg_match('/^[a-zA-Z0-9._-]+$/', $item)))
58✔
UNCOV
338
                                ->thenInvalid('The api keys "key" is not valid according to the pattern enforced by OpenAPI 3.1 ^[a-zA-Z0-9._-]+$.')
58✔
UNCOV
339
                            ->end()
58✔
UNCOV
340
                            ->prototype('array')
58✔
UNCOV
341
                                ->children()
58✔
UNCOV
342
                                    ->scalarNode('name')
58✔
UNCOV
343
                                        ->info('The name of the header or query parameter containing the api key.')
58✔
UNCOV
344
                                    ->end()
58✔
UNCOV
345
                                    ->enumNode('type')
58✔
UNCOV
346
                                        ->info('Whether the api key should be a query parameter or a header.')
58✔
UNCOV
347
                                        ->values(['query', 'header'])
58✔
UNCOV
348
                                    ->end()
58✔
UNCOV
349
                                ->end()
58✔
UNCOV
350
                            ->end()
58✔
UNCOV
351
                        ->end()
58✔
UNCOV
352
                        ->arrayNode('http_auth')
58✔
UNCOV
353
                            ->info('Creates http security schemes for OpenAPI.')
58✔
UNCOV
354
                            ->useAttributeAsKey('key')
58✔
UNCOV
355
                            ->validate()
58✔
UNCOV
356
                                ->ifTrue(static fn ($v): bool => (bool) array_filter(array_keys($v), fn ($item) => !preg_match('/^[a-zA-Z0-9._-]+$/', $item)))
58✔
UNCOV
357
                                ->thenInvalid('The api keys "key" is not valid according to the pattern enforced by OpenAPI 3.1 ^[a-zA-Z0-9._-]+$.')
58✔
UNCOV
358
                            ->end()
58✔
UNCOV
359
                            ->prototype('array')
58✔
UNCOV
360
                                ->children()
58✔
UNCOV
361
                                    ->scalarNode('scheme')
58✔
UNCOV
362
                                        ->info('The OpenAPI HTTP auth scheme, for example "bearer"')
58✔
UNCOV
363
                                    ->end()
58✔
UNCOV
364
                                    ->scalarNode('bearerFormat')
58✔
UNCOV
365
                                        ->info('The OpenAPI HTTP bearer format')
58✔
UNCOV
366
                                    ->end()
58✔
UNCOV
367
                                ->end()
58✔
UNCOV
368
                            ->end()
58✔
UNCOV
369
                        ->end()
58✔
UNCOV
370
                        ->variableNode('swagger_ui_extra_configuration')
58✔
UNCOV
371
                            ->defaultValue([])
58✔
UNCOV
372
                            ->validate()
58✔
UNCOV
373
                                ->ifTrue(static fn ($v): bool => false === \is_array($v))
58✔
UNCOV
374
                                ->thenInvalid('The swagger_ui_extra_configuration parameter must be an array.')
58✔
UNCOV
375
                            ->end()
58✔
UNCOV
376
                            ->info('To pass extra configuration to Swagger UI, like docExpansion or filter.')
58✔
UNCOV
377
                        ->end()
58✔
UNCOV
378
                    ->end()
58✔
UNCOV
379
                ->end()
58✔
UNCOV
380
            ->end();
58✔
381
    }
382

383
    private function addHttpCacheSection(ArrayNodeDefinition $rootNode): void
384
    {
UNCOV
385
        $rootNode
58✔
UNCOV
386
            ->children()
58✔
UNCOV
387
                ->arrayNode('http_cache')
58✔
UNCOV
388
                    ->addDefaultsIfNotSet()
58✔
UNCOV
389
                    ->children()
58✔
UNCOV
390
                        ->booleanNode('public')->defaultNull()->info('To make all responses public by default.')->end()
58✔
UNCOV
391
                        ->arrayNode('invalidation')
58✔
UNCOV
392
                            ->info('Enable the tags-based cache invalidation system.')
58✔
UNCOV
393
                            ->canBeEnabled()
58✔
UNCOV
394
                            ->children()
58✔
UNCOV
395
                                ->arrayNode('varnish_urls')
58✔
UNCOV
396
                                    ->setDeprecated('api-platform/core', '3.0', 'The "varnish_urls" configuration is deprecated, use "urls" or "scoped_clients".')
58✔
UNCOV
397
                                    ->defaultValue([])
58✔
UNCOV
398
                                    ->prototype('scalar')->end()
58✔
UNCOV
399
                                    ->info('URLs of the Varnish servers to purge using cache tags when a resource is updated.')
58✔
UNCOV
400
                                ->end()
58✔
UNCOV
401
                                ->arrayNode('urls')
58✔
UNCOV
402
                                    ->defaultValue([])
58✔
UNCOV
403
                                    ->prototype('scalar')->end()
58✔
UNCOV
404
                                    ->info('URLs of the Varnish servers to purge using cache tags when a resource is updated.')
58✔
UNCOV
405
                                ->end()
58✔
UNCOV
406
                                ->arrayNode('scoped_clients')
58✔
UNCOV
407
                                    ->defaultValue([])
58✔
UNCOV
408
                                    ->prototype('scalar')->end()
58✔
UNCOV
409
                                    ->info('Service names of scoped client to use by the cache purger.')
58✔
UNCOV
410
                                ->end()
58✔
UNCOV
411
                                ->integerNode('max_header_length')
58✔
UNCOV
412
                                    ->defaultValue(7500)
58✔
UNCOV
413
                                    ->info('Max header length supported by the cache server.')
58✔
UNCOV
414
                                ->end()
58✔
UNCOV
415
                                ->variableNode('request_options')
58✔
UNCOV
416
                                    ->defaultValue([])
58✔
UNCOV
417
                                    ->validate()
58✔
UNCOV
418
                                        ->ifTrue(static fn ($v): bool => false === \is_array($v))
58✔
UNCOV
419
                                        ->thenInvalid('The request_options parameter must be an array.')
58✔
UNCOV
420
                                    ->end()
58✔
UNCOV
421
                                    ->info('To pass options to the client charged with the request.')
58✔
UNCOV
422
                                ->end()
58✔
UNCOV
423
                                ->scalarNode('purger')
58✔
UNCOV
424
                                    ->defaultValue('api_platform.http_cache.purger.varnish')
58✔
UNCOV
425
                                    ->info('Specify a purger to use (available values: "api_platform.http_cache.purger.varnish.ban", "api_platform.http_cache.purger.varnish.xkey", "api_platform.http_cache.purger.souin").')
58✔
UNCOV
426
                                ->end()
58✔
UNCOV
427
                                ->arrayNode('xkey')
58✔
UNCOV
428
                                    ->setDeprecated('api-platform/core', '3.0', 'The "xkey" configuration is deprecated, use your own purger to customize surrogate keys or the appropriate paramters.')
58✔
UNCOV
429
                                    ->addDefaultsIfNotSet()
58✔
UNCOV
430
                                    ->children()
58✔
UNCOV
431
                                        ->scalarNode('glue')
58✔
UNCOV
432
                                        ->defaultValue(' ')
58✔
UNCOV
433
                                        ->info('xkey glue between keys')
58✔
UNCOV
434
                                        ->end()
58✔
UNCOV
435
                                    ->end()
58✔
UNCOV
436
                                ->end()
58✔
UNCOV
437
                            ->end()
58✔
UNCOV
438
                        ->end()
58✔
UNCOV
439
                    ->end()
58✔
UNCOV
440
                ->end()
58✔
UNCOV
441
            ->end();
58✔
442
    }
443

444
    private function addMercureSection(ArrayNodeDefinition $rootNode): void
445
    {
UNCOV
446
        $rootNode
58✔
UNCOV
447
            ->children()
58✔
UNCOV
448
                ->arrayNode('mercure')
58✔
UNCOV
449
                    ->{class_exists(MercureBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
58✔
UNCOV
450
                    ->children()
58✔
UNCOV
451
                        ->scalarNode('hub_url')
58✔
UNCOV
452
                            ->defaultNull()
58✔
UNCOV
453
                            ->info('The URL sent in the Link HTTP header. If not set, will default to the URL for MercureBundle\'s default hub.')
58✔
UNCOV
454
                        ->end()
58✔
UNCOV
455
                        ->booleanNode('include_type')
58✔
UNCOV
456
                            ->defaultFalse()
58✔
UNCOV
457
                            ->info('Always include @type in updates (including delete ones).')
58✔
UNCOV
458
                        ->end()
58✔
UNCOV
459
                    ->end()
58✔
UNCOV
460
                ->end()
58✔
UNCOV
461
            ->end();
58✔
462
    }
463

464
    private function addMessengerSection(ArrayNodeDefinition $rootNode): void
465
    {
UNCOV
466
        $rootNode
58✔
UNCOV
467
            ->children()
58✔
UNCOV
468
                ->arrayNode('messenger')
58✔
UNCOV
469
                    ->{!class_exists(FullStack::class) && interface_exists(MessageBusInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}()
58✔
UNCOV
470
                ->end()
58✔
UNCOV
471
            ->end();
58✔
472
    }
473

474
    private function addElasticsearchSection(ArrayNodeDefinition $rootNode): void
475
    {
UNCOV
476
        $rootNode
58✔
UNCOV
477
            ->children()
58✔
UNCOV
478
                ->arrayNode('elasticsearch')
58✔
UNCOV
479
                    ->canBeEnabled()
58✔
UNCOV
480
                    ->addDefaultsIfNotSet()
58✔
UNCOV
481
                    ->validate()
58✔
UNCOV
482
                        ->ifTrue(static fn (array $v): bool => null !== ($v['ssl_ca_bundle'] ?? null) && false === ($v['ssl_verification'] ?? true))
58✔
UNCOV
483
                        ->thenInvalid('The "ssl_ca_bundle" and "ssl_verification: false" options cannot be used together. Either provide a CA bundle path or disable SSL verification, not both.')
58✔
UNCOV
484
                    ->end()
58✔
UNCOV
485
                    ->children()
58✔
UNCOV
486
                        ->booleanNode('enabled')
58✔
UNCOV
487
                            ->defaultFalse()
58✔
UNCOV
488
                            ->validate()
58✔
UNCOV
489
                                ->ifTrue()
58✔
UNCOV
490
                                ->then(static function (bool $v): bool {
58✔
491
                                    if (
492
                                        // ES v7
UNCOV
493
                                        !class_exists(\Elasticsearch\Client::class)
8✔
494
                                        // ES v8 and up
UNCOV
495
                                        && !class_exists(\Elastic\Elasticsearch\Client::class)
8✔
496
                                    ) {
497
                                        throw new InvalidConfigurationException('The elasticsearch/elasticsearch package is required for Elasticsearch support.');
×
498
                                    }
499

UNCOV
500
                                    return $v;
8✔
UNCOV
501
                                })
58✔
UNCOV
502
                            ->end()
58✔
UNCOV
503
                        ->end()
58✔
UNCOV
504
                        ->arrayNode('hosts')
58✔
UNCOV
505
                            ->beforeNormalization()->castToArray()->end()
58✔
UNCOV
506
                            ->defaultValue([])
58✔
UNCOV
507
                            ->prototype('scalar')->end()
58✔
UNCOV
508
                        ->end()
58✔
UNCOV
509
                        ->scalarNode('ssl_ca_bundle')
58✔
UNCOV
510
                            ->defaultNull()
58✔
UNCOV
511
                            ->info('Path to the SSL CA bundle file for Elasticsearch SSL verification.')
58✔
UNCOV
512
                        ->end()
58✔
UNCOV
513
                        ->booleanNode('ssl_verification')
58✔
UNCOV
514
                            ->defaultTrue()
58✔
UNCOV
515
                            ->info('Enable or disable SSL verification for Elasticsearch connections.')
58✔
UNCOV
516
                        ->end()
58✔
UNCOV
517
                    ->end()
58✔
UNCOV
518
                ->end()
58✔
UNCOV
519
            ->end();
58✔
520
    }
521

522
    private function addOpenApiSection(ArrayNodeDefinition $rootNode): void
523
    {
UNCOV
524
        $rootNode
58✔
UNCOV
525
            ->children()
58✔
UNCOV
526
                ->arrayNode('openapi')
58✔
UNCOV
527
                    ->addDefaultsIfNotSet()
58✔
UNCOV
528
                        ->children()
58✔
UNCOV
529
                        ->arrayNode('contact')
58✔
UNCOV
530
                        ->addDefaultsIfNotSet()
58✔
UNCOV
531
                            ->children()
58✔
UNCOV
532
                                ->scalarNode('name')->defaultNull()->info('The identifying name of the contact person/organization.')->end()
58✔
UNCOV
533
                                ->scalarNode('url')->defaultNull()->info('The URL pointing to the contact information. MUST be in the format of a URL.')->end()
58✔
UNCOV
534
                                ->scalarNode('email')->defaultNull()->info('The email address of the contact person/organization. MUST be in the format of an email address.')->end()
58✔
UNCOV
535
                            ->end()
58✔
UNCOV
536
                        ->end()
58✔
UNCOV
537
                        ->scalarNode('termsOfService')->defaultNull()->info('A URL to the Terms of Service for the API. MUST be in the format of a URL.')->end()
58✔
UNCOV
538
                        ->arrayNode('tags')
58✔
UNCOV
539
                            ->info('Global OpenApi tags overriding the default computed tags if specified.')
58✔
UNCOV
540
                            ->prototype('array')
58✔
UNCOV
541
                                ->children()
58✔
UNCOV
542
                                    ->scalarNode('name')->isRequired()->end()
58✔
UNCOV
543
                                    ->scalarNode('description')->defaultNull()->end()
58✔
UNCOV
544
                                ->end()
58✔
UNCOV
545
                            ->end()
58✔
UNCOV
546
                        ->end()
58✔
UNCOV
547
                        ->arrayNode('license')
58✔
UNCOV
548
                        ->addDefaultsIfNotSet()
58✔
UNCOV
549
                            ->children()
58✔
UNCOV
550
                                ->scalarNode('name')->defaultNull()->info('The license name used for the API.')->end()
58✔
UNCOV
551
                                ->scalarNode('url')->defaultNull()->info('URL to the license used for the API. MUST be in the format of a URL.')->end()
58✔
UNCOV
552
                                ->scalarNode('identifier')->defaultNull()->info('An SPDX license expression for the API. The identifier field is mutually exclusive of the url field.')->end()
58✔
UNCOV
553
                            ->end()
58✔
UNCOV
554
                        ->end()
58✔
UNCOV
555
                        ->variableNode('swagger_ui_extra_configuration')
58✔
UNCOV
556
                            ->defaultValue([])
58✔
UNCOV
557
                            ->validate()
58✔
UNCOV
558
                                ->ifTrue(static fn ($v): bool => false === \is_array($v))
58✔
UNCOV
559
                                ->thenInvalid('The swagger_ui_extra_configuration parameter must be an array.')
58✔
UNCOV
560
                            ->end()
58✔
UNCOV
561
                            ->info('To pass extra configuration to Swagger UI, like docExpansion or filter.')
58✔
UNCOV
562
                        ->end()
58✔
UNCOV
563
                        ->booleanNode('overrideResponses')->defaultTrue()->info('Whether API Platform adds automatic responses to the OpenAPI documentation.')->end()
58✔
UNCOV
564
                        ->scalarNode('error_resource_class')->defaultNull()->info('The class used to represent errors in the OpenAPI documentation.')->end()
58✔
UNCOV
565
                        ->scalarNode('validation_error_resource_class')->defaultNull()->info('The class used to represent validation errors in the OpenAPI documentation.')->end()
58✔
UNCOV
566
                    ->end()
58✔
UNCOV
567
                ->end()
58✔
UNCOV
568
            ->end();
58✔
569
    }
570

571
    /**
572
     * @throws InvalidConfigurationException
573
     */
574
    private function addExceptionToStatusSection(ArrayNodeDefinition $rootNode): void
575
    {
UNCOV
576
        $rootNode
58✔
UNCOV
577
            ->children()
58✔
UNCOV
578
                ->arrayNode('exception_to_status')
58✔
UNCOV
579
                    ->defaultValue([
58✔
UNCOV
580
                        SerializerExceptionInterface::class => Response::HTTP_BAD_REQUEST,
58✔
UNCOV
581
                        InvalidArgumentException::class => Response::HTTP_BAD_REQUEST,
58✔
UNCOV
582
                        OptimisticLockException::class => Response::HTTP_CONFLICT,
58✔
UNCOV
583
                    ])
58✔
UNCOV
584
                    ->info('The list of exceptions mapped to their HTTP status code.')
58✔
UNCOV
585
                    ->normalizeKeys(false)
58✔
UNCOV
586
                    ->useAttributeAsKey('exception_class')
58✔
UNCOV
587
                    ->prototype('integer')->end()
58✔
UNCOV
588
                    ->validate()
58✔
UNCOV
589
                        ->ifArray()
58✔
UNCOV
590
                        ->then(static function (array $exceptionToStatus): array {
58✔
UNCOV
591
                            foreach ($exceptionToStatus as $httpStatusCode) {
20✔
UNCOV
592
                                if ($httpStatusCode < 100 || $httpStatusCode >= 600) {
20✔
UNCOV
593
                                    throw new InvalidConfigurationException(sprintf('The HTTP status code "%s" is not valid.', $httpStatusCode));
8✔
594
                                }
595
                            }
596

UNCOV
597
                            return $exceptionToStatus;
12✔
UNCOV
598
                        })
58✔
UNCOV
599
                    ->end()
58✔
UNCOV
600
                ->end()
58✔
UNCOV
601
            ->end();
58✔
602
    }
603

604
    private function addFormatSection(ArrayNodeDefinition $rootNode, string $key, array $defaultValue): void
605
    {
UNCOV
606
        $rootNode
58✔
UNCOV
607
            ->children()
58✔
UNCOV
608
                ->arrayNode($key)
58✔
UNCOV
609
                    ->defaultValue($defaultValue)
58✔
UNCOV
610
                    ->info('The list of enabled formats. The first one will be the default.')
58✔
UNCOV
611
                    ->normalizeKeys(false)
58✔
UNCOV
612
                    ->useAttributeAsKey('format')
58✔
UNCOV
613
                    ->beforeNormalization()
58✔
UNCOV
614
                        ->ifArray()
58✔
UNCOV
615
                        ->then(static function ($v) {
58✔
UNCOV
616
                            foreach ($v as $format => $value) {
12✔
UNCOV
617
                                if (isset($value['mime_types'])) {
12✔
618
                                    continue;
×
619
                                }
620

UNCOV
621
                                $v[$format] = ['mime_types' => $value];
12✔
622
                            }
623

UNCOV
624
                            return $v;
12✔
UNCOV
625
                        })
58✔
UNCOV
626
                    ->end()
58✔
UNCOV
627
                    ->prototype('array')
58✔
UNCOV
628
                        ->children()
58✔
UNCOV
629
                            ->arrayNode('mime_types')->prototype('scalar')->end()->end()
58✔
UNCOV
630
                        ->end()
58✔
UNCOV
631
                    ->end()
58✔
UNCOV
632
                ->end()
58✔
UNCOV
633
            ->end();
58✔
634
    }
635

636
    private function addDefaultsSection(ArrayNodeDefinition $rootNode): void
637
    {
UNCOV
638
        $nameConverter = new CamelCaseToSnakeCaseNameConverter();
58✔
UNCOV
639
        $defaultsNode = $rootNode->children()->arrayNode('defaults');
58✔
640

UNCOV
641
        $defaultsNode
58✔
UNCOV
642
            ->ignoreExtraKeys(false)
58✔
UNCOV
643
            ->beforeNormalization()
58✔
UNCOV
644
            ->always(static function (array $defaults) use ($nameConverter): array {
58✔
UNCOV
645
                $normalizedDefaults = [];
12✔
UNCOV
646
                foreach ($defaults as $option => $value) {
12✔
UNCOV
647
                    $option = $nameConverter->normalize($option);
12✔
UNCOV
648
                    $normalizedDefaults[$option] = $value;
12✔
649
                }
650

UNCOV
651
                return $normalizedDefaults;
12✔
UNCOV
652
            });
58✔
653

UNCOV
654
        $this->defineDefault($defaultsNode, new \ReflectionClass(ApiResource::class), $nameConverter);
58✔
UNCOV
655
        $this->defineDefault($defaultsNode, new \ReflectionClass(Put::class), $nameConverter);
58✔
UNCOV
656
        $this->defineDefault($defaultsNode, new \ReflectionClass(Post::class), $nameConverter);
58✔
657
    }
658

659
    private function addMakerSection(ArrayNodeDefinition $rootNode): void
660
    {
UNCOV
661
        $rootNode
58✔
UNCOV
662
            ->children()
58✔
UNCOV
663
                ->arrayNode('maker')
58✔
UNCOV
664
                    ->{class_exists(MakerBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
58✔
UNCOV
665
                    ->children()
58✔
UNCOV
666
                        ->scalarNode('namespace_prefix')->defaultValue('')->info('Add a prefix to all maker generated classes. e.g set it to "Api" to set the maker namespace to "App\\Api\\" (if the maker.root_namespace config is App). e.g. App\\Api\\State\\MyStateProcessor')->end()
58✔
UNCOV
667
                    ->end()
58✔
UNCOV
668
                ->end()
58✔
UNCOV
669
            ->end();
58✔
670
    }
671

672
    private function defineDefault(ArrayNodeDefinition $defaultsNode, \ReflectionClass $reflectionClass, CamelCaseToSnakeCaseNameConverter $nameConverter): void
673
    {
UNCOV
674
        foreach ($reflectionClass->getConstructor()->getParameters() as $parameter) {
58✔
UNCOV
675
            $defaultsNode->children()->variableNode($nameConverter->normalize($parameter->getName()));
58✔
676
        }
677
    }
678
}
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