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

api-platform / core / 17071323229

19 Aug 2025 01:36PM UTC coverage: 0.0%. First build
17071323229

Pull #7225

github

web-flow
Merge 1e726a585 into 020385253
Pull Request #7225: feat: json streamer

0 of 307 new or added lines in 31 files covered. (0.0%)

0 of 52230 relevant lines covered (0.0%)

0.0 hits per line

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

0.0
/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\FullStack;
28
use Symfony\Bundle\MakerBundle\MakerBundle;
29
use Symfony\Bundle\MercureBundle\MercureBundle;
30
use Symfony\Bundle\TwigBundle\TwigBundle;
31
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
32
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
33
use Symfony\Component\Config\Definition\ConfigurationInterface;
34
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
35
use Symfony\Component\HttpFoundation\Response;
36
use Symfony\Component\JsonStreamer\JsonStreamWriter;
37
use Symfony\Component\Messenger\MessageBusInterface;
38
use Symfony\Component\Serializer\Exception\ExceptionInterface as SerializerExceptionInterface;
39
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
40
use Symfony\Component\Yaml\Yaml;
41

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

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

66
                    return $v;
×
67
                })
×
68
            ->end()
×
69
            ->children()
×
70
                ->scalarNode('title')
×
71
                    ->info('The title of the API.')
×
72
                    ->cannotBeEmpty()
×
73
                    ->defaultValue('')
×
74
                ->end()
×
75
                ->scalarNode('description')
×
76
                    ->info('The description of the API.')
×
77
                    ->cannotBeEmpty()
×
78
                    ->defaultValue('')
×
79
                ->end()
×
80
                ->scalarNode('version')
×
81
                    ->info('The version of the API.')
×
82
                    ->cannotBeEmpty()
×
83
                    ->defaultValue('0.0.0')
×
84
                ->end()
×
85
                ->booleanNode('show_webby')->defaultTrue()->info('If true, show Webby on the documentation page')->end()
×
86
                ->booleanNode('use_symfony_listeners')->defaultFalse()->info(sprintf('Uses Symfony event listeners instead of the %s.', MainController::class))->end()
×
87
                ->scalarNode('name_converter')->defaultNull()->info('Specify a name converter to use.')->end()
×
88
                ->scalarNode('asset_package')->defaultNull()->info('Specify an asset package name to use.')->end()
×
89
                ->scalarNode('path_segment_name_generator')->defaultValue('api_platform.metadata.path_segment_name_generator.underscore')->info('Specify a path name generator to use.')->end()
×
90
                ->scalarNode('inflector')->defaultValue('api_platform.metadata.inflector')->info('Specify an inflector to use.')->end()
×
91
                ->arrayNode('validator')
×
92
                    ->addDefaultsIfNotSet()
×
93
                    ->children()
×
94
                        ->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()
×
95
                        ->booleanNode('query_parameter_validation')
×
96
                            ->defaultValue(true)
×
97
                            ->setDeprecated('api-platform/symfony', '4.2', 'Will be removed in API Platform 5.0.')
×
98
                        ->end()
×
99
                    ->end()
×
100
                ->end()
×
101
                ->arrayNode('eager_loading')
×
102
                    ->canBeDisabled()
×
103
                    ->addDefaultsIfNotSet()
×
104
                    ->children()
×
105
                        ->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()
×
106
                        ->integerNode('max_joins')->defaultValue(30)->info('Max number of joined relations before EagerLoading throws a RuntimeException')->end()
×
107
                        ->booleanNode('force_eager')->defaultTrue()->info('Force join on every relation. If disabled, it will only join relations having the EAGER fetch mode.')->end()
×
108
                    ->end()
×
109
                ->end()
×
110
                ->booleanNode('handle_symfony_errors')->defaultFalse()->info('Allows to handle symfony exceptions.')->end()
×
111
                ->booleanNode('enable_swagger')->defaultTrue()->info('Enable the Swagger documentation and export.')->end()
×
NEW
112
                ->booleanNode('enable_json_streamer')->defaultValue(class_exists(JsonStreamWriter::class))->info('Enable json streamer.')->end()
×
113
                ->booleanNode('enable_swagger_ui')->defaultValue(class_exists(TwigBundle::class))->info('Enable Swagger UI')->end()
×
114
                ->booleanNode('enable_re_doc')->defaultValue(class_exists(TwigBundle::class))->info('Enable ReDoc')->end()
×
115
                ->booleanNode('enable_entrypoint')->defaultTrue()->info('Enable the entrypoint')->end()
×
116
                ->booleanNode('enable_docs')->defaultTrue()->info('Enable the docs')->end()
×
117
                ->booleanNode('enable_profiler')->defaultTrue()->info('Enable the data collector and the WebProfilerBundle integration.')->end()
×
118
                ->booleanNode('enable_link_security')->defaultFalse()->info('Enable security for Links (sub resources)')->end()
×
119
                ->arrayNode('collection')
×
120
                    ->addDefaultsIfNotSet()
×
121
                    ->children()
×
122
                        ->scalarNode('exists_parameter_name')->defaultValue('exists')->cannotBeEmpty()->info('The name of the query parameter to filter on nullable field values.')->end()
×
123
                        ->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
×
124
                        ->scalarNode('order_parameter_name')->defaultValue('order')->cannotBeEmpty()->info('The name of the query parameter to order results.')->end()
×
125
                        ->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()
×
126
                        ->arrayNode('pagination')
×
127
                            ->canBeDisabled()
×
128
                            ->addDefaultsIfNotSet()
×
129
                            ->children()
×
130
                                ->scalarNode('page_parameter_name')->defaultValue('page')->cannotBeEmpty()->info('The default name of the parameter handling the page number.')->end()
×
131
                                ->scalarNode('enabled_parameter_name')->defaultValue('pagination')->cannotBeEmpty()->info('The name of the query parameter to enable or disable pagination.')->end()
×
132
                                ->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()
×
133
                                ->scalarNode('partial_parameter_name')->defaultValue('partial')->cannotBeEmpty()->info('The name of the query parameter to enable or disable partial pagination.')->end()
×
134
                            ->end()
×
135
                        ->end()
×
136
                    ->end()
×
137
                ->end()
×
138
                ->arrayNode('mapping')
×
139
                    ->addDefaultsIfNotSet()
×
140
                    ->children()
×
141
                        ->arrayNode('imports')
×
142
                            ->prototype('scalar')->end()
×
143
                        ->end()
×
144
                        ->arrayNode('paths')
×
145
                            ->prototype('scalar')->end()
×
146
                        ->end()
×
147
                    ->end()
×
148
                ->end()
×
149
                ->arrayNode('resource_class_directories')
×
150
                    ->prototype('scalar')->end()
×
151
                    ->setDeprecated('api-platform/symfony', '4.1', 'The "resource_class_directories" configuration is deprecated, classes using #[ApiResource] attribute are autoconfigured by the dependency injection container.')
×
152
                ->end()
×
153
                ->arrayNode('serializer')
×
154
                    ->addDefaultsIfNotSet()
×
155
                    ->children()
×
156
                        ->booleanNode('hydra_prefix')->defaultFalse()->info('Use the "hydra:" prefix.')->end()
×
157
                    ->end()
×
158
                ->end()
×
159
            ->end();
×
160

161
        $this->addDoctrineOrmSection($rootNode);
×
162
        $this->addDoctrineMongoDbOdmSection($rootNode);
×
163
        $this->addOAuthSection($rootNode);
×
164
        $this->addGraphQlSection($rootNode);
×
165
        $this->addSwaggerSection($rootNode);
×
166
        $this->addHttpCacheSection($rootNode);
×
167
        $this->addMercureSection($rootNode);
×
168
        $this->addMessengerSection($rootNode);
×
169
        $this->addElasticsearchSection($rootNode);
×
170
        $this->addOpenApiSection($rootNode);
×
171
        $this->addMakerSection($rootNode);
×
172

173
        $this->addExceptionToStatusSection($rootNode);
×
174

175
        $this->addFormatSection($rootNode, 'formats', [
×
176
            'jsonld' => ['mime_types' => ['application/ld+json']],
×
177
        ]);
×
178
        $this->addFormatSection($rootNode, 'patch_formats', [
×
179
            'json' => ['mime_types' => ['application/merge-patch+json']],
×
180
        ]);
×
181

182
        $defaultDocFormats = [
×
183
            'jsonld' => ['mime_types' => ['application/ld+json']],
×
184
            'jsonopenapi' => ['mime_types' => ['application/vnd.openapi+json']],
×
185
            'html' => ['mime_types' => ['text/html']],
×
186
        ];
×
187

188
        if (class_exists(Yaml::class)) {
×
189
            $defaultDocFormats['yamlopenapi'] = ['mime_types' => ['application/vnd.openapi+yaml']];
×
190
        }
191

192
        $this->addFormatSection($rootNode, 'docs_formats', $defaultDocFormats);
×
193

194
        $this->addFormatSection($rootNode, 'error_formats', [
×
195
            'jsonld' => ['mime_types' => ['application/ld+json']],
×
196
            'jsonproblem' => ['mime_types' => ['application/problem+json']],
×
197
            'json' => ['mime_types' => ['application/problem+json', 'application/json']],
×
198
        ]);
×
199
        $rootNode
×
200
            ->children()
×
201
                ->arrayNode('jsonschema_formats')
×
202
                    ->scalarPrototype()->end()
×
203
                    ->defaultValue([])
×
204
                    ->info('The JSON formats to compute the JSON Schemas for.')
×
205
                ->end()
×
206
            ->end();
×
207

208
        $this->addDefaultsSection($rootNode);
×
209

210
        return $treeBuilder;
×
211
    }
212

213
    private function addDoctrineOrmSection(ArrayNodeDefinition $rootNode): void
214
    {
215
        $rootNode
×
216
            ->children()
×
217
                ->arrayNode('doctrine')
×
218
                    ->{class_exists(DoctrineBundle::class) && interface_exists(EntityManagerInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}()
×
219
                ->end()
×
220
            ->end();
×
221
    }
222

223
    private function addDoctrineMongoDbOdmSection(ArrayNodeDefinition $rootNode): void
224
    {
225
        $rootNode
×
226
            ->children()
×
227
                ->arrayNode('doctrine_mongodb_odm')
×
228
                    ->{class_exists(DoctrineMongoDBBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
×
229
                ->end()
×
230
            ->end();
×
231
    }
232

233
    private function addOAuthSection(ArrayNodeDefinition $rootNode): void
234
    {
235
        $rootNode
×
236
            ->children()
×
237
                ->arrayNode('oauth')
×
238
                    ->canBeEnabled()
×
239
                    ->addDefaultsIfNotSet()
×
240
                    ->children()
×
241
                        ->scalarNode('clientId')->defaultValue('')->info('The oauth client id.')->end()
×
242
                        ->scalarNode('clientSecret')
×
243
                            ->defaultValue('')
×
244
                            ->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')
×
245
                        ->end()
×
246
                        ->booleanNode('pkce')->defaultFalse()->info('Enable the oauth PKCE.')->end()
×
247
                        ->scalarNode('type')->defaultValue('oauth2')->info('The oauth type.')->end()
×
248
                        ->scalarNode('flow')->defaultValue('application')->info('The oauth flow grant type.')->end()
×
249
                        ->scalarNode('tokenUrl')->defaultValue('')->info('The oauth token url.')->end()
×
250
                        ->scalarNode('authorizationUrl')->defaultValue('')->info('The oauth authentication url.')->end()
×
251
                        ->scalarNode('refreshUrl')->defaultValue('')->info('The oauth refresh url.')->end()
×
252
                        ->arrayNode('scopes')
×
253
                            ->prototype('scalar')->end()
×
254
                        ->end()
×
255
                    ->end()
×
256
                ->end()
×
257
            ->end();
×
258
    }
259

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

296
    private function addSwaggerSection(ArrayNodeDefinition $rootNode): void
297
    {
298
        $supportedVersions = [3];
×
299

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

315
                                    foreach ($v as &$version) {
×
316
                                        $version = (int) $version;
×
317
                                    }
318

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

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

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

458
    private function addMessengerSection(ArrayNodeDefinition $rootNode): void
459
    {
460
        $rootNode
×
461
            ->children()
×
462
                ->arrayNode('messenger')
×
463
                    ->{!class_exists(FullStack::class) && interface_exists(MessageBusInterface::class) ? 'canBeDisabled' : 'canBeEnabled'}()
×
464
                ->end()
×
465
            ->end();
×
466
    }
467

468
    private function addElasticsearchSection(ArrayNodeDefinition $rootNode): void
469
    {
470
        $rootNode
×
471
            ->children()
×
472
                ->arrayNode('elasticsearch')
×
473
                    ->canBeEnabled()
×
474
                    ->addDefaultsIfNotSet()
×
475
                    ->children()
×
476
                        ->booleanNode('enabled')
×
477
                            ->defaultFalse()
×
478
                            ->validate()
×
479
                                ->ifTrue()
×
480
                                ->then(static function (bool $v): bool {
×
481
                                    if (
482
                                        // ES v7
483
                                        !class_exists(\Elasticsearch\Client::class)
×
484
                                        // ES v8 and up
485
                                        && !class_exists(\Elastic\Elasticsearch\Client::class)
×
486
                                    ) {
487
                                        throw new InvalidConfigurationException('The elasticsearch/elasticsearch package is required for Elasticsearch support.');
×
488
                                    }
489

490
                                    return $v;
×
491
                                })
×
492
                            ->end()
×
493
                        ->end()
×
494
                        ->arrayNode('hosts')
×
495
                            ->beforeNormalization()->castToArray()->end()
×
496
                            ->defaultValue([])
×
497
                            ->prototype('scalar')->end()
×
498
                        ->end()
×
499
                    ->end()
×
500
                ->end()
×
501
            ->end();
×
502
    }
503

504
    private function addOpenApiSection(ArrayNodeDefinition $rootNode): void
505
    {
506
        $rootNode
×
507
            ->children()
×
508
                ->arrayNode('openapi')
×
509
                    ->addDefaultsIfNotSet()
×
510
                        ->children()
×
511
                        ->arrayNode('contact')
×
512
                        ->addDefaultsIfNotSet()
×
513
                            ->children()
×
514
                                ->scalarNode('name')->defaultNull()->info('The identifying name of the contact person/organization.')->end()
×
515
                                ->scalarNode('url')->defaultNull()->info('The URL pointing to the contact information. MUST be in the format of a URL.')->end()
×
516
                                ->scalarNode('email')->defaultNull()->info('The email address of the contact person/organization. MUST be in the format of an email address.')->end()
×
517
                            ->end()
×
518
                        ->end()
×
519
                        ->scalarNode('termsOfService')->defaultNull()->info('A URL to the Terms of Service for the API. MUST be in the format of a URL.')->end()
×
520
                        ->arrayNode('tags')
×
521
                            ->info('Global OpenApi tags overriding the default computed tags if specified.')
×
522
                            ->prototype('array')
×
523
                                ->children()
×
524
                                    ->scalarNode('name')->isRequired()->end()
×
525
                                    ->scalarNode('description')->defaultNull()->end()
×
526
                                ->end()
×
527
                            ->end()
×
528
                        ->end()
×
529
                        ->arrayNode('license')
×
530
                        ->addDefaultsIfNotSet()
×
531
                            ->children()
×
532
                                ->scalarNode('name')->defaultNull()->info('The license name used for the API.')->end()
×
533
                                ->scalarNode('url')->defaultNull()->info('URL to the license used for the API. MUST be in the format of a URL.')->end()
×
534
                                ->scalarNode('identifier')->defaultNull()->info('An SPDX license expression for the API. The identifier field is mutually exclusive of the url field.')->end()
×
535
                            ->end()
×
536
                        ->end()
×
537
                        ->variableNode('swagger_ui_extra_configuration')
×
538
                            ->defaultValue([])
×
539
                            ->validate()
×
540
                                ->ifTrue(static fn ($v): bool => false === \is_array($v))
×
541
                                ->thenInvalid('The swagger_ui_extra_configuration parameter must be an array.')
×
542
                            ->end()
×
543
                            ->info('To pass extra configuration to Swagger UI, like docExpansion or filter.')
×
544
                        ->end()
×
545
                        ->booleanNode('overrideResponses')->defaultTrue()->info('Whether API Platform adds automatic responses to the OpenAPI documentation.')->end()
×
546
                        ->scalarNode('error_resource_class')->defaultNull()->info('The class used to represent errors in the OpenAPI documentation.')->end()
×
547
                        ->scalarNode('validation_error_resource_class')->defaultNull()->info('The class used to represent validation errors in the OpenAPI documentation.')->end()
×
548
                    ->end()
×
549
                ->end()
×
550
            ->end();
×
551
    }
552

553
    /**
554
     * @throws InvalidConfigurationException
555
     */
556
    private function addExceptionToStatusSection(ArrayNodeDefinition $rootNode): void
557
    {
558
        $rootNode
×
559
            ->children()
×
560
                ->arrayNode('exception_to_status')
×
561
                    ->defaultValue([
×
562
                        SerializerExceptionInterface::class => Response::HTTP_BAD_REQUEST,
×
563
                        InvalidArgumentException::class => Response::HTTP_BAD_REQUEST,
×
564
                        OptimisticLockException::class => Response::HTTP_CONFLICT,
×
565
                    ])
×
566
                    ->info('The list of exceptions mapped to their HTTP status code.')
×
567
                    ->normalizeKeys(false)
×
568
                    ->useAttributeAsKey('exception_class')
×
569
                    ->prototype('integer')->end()
×
570
                    ->validate()
×
571
                        ->ifArray()
×
572
                        ->then(static function (array $exceptionToStatus): array {
×
573
                            foreach ($exceptionToStatus as $httpStatusCode) {
×
574
                                if ($httpStatusCode < 100 || $httpStatusCode >= 600) {
×
575
                                    throw new InvalidConfigurationException(sprintf('The HTTP status code "%s" is not valid.', $httpStatusCode));
×
576
                                }
577
                            }
578

579
                            return $exceptionToStatus;
×
580
                        })
×
581
                    ->end()
×
582
                ->end()
×
583
            ->end();
×
584
    }
585

586
    private function addFormatSection(ArrayNodeDefinition $rootNode, string $key, array $defaultValue): void
587
    {
588
        $rootNode
×
589
            ->children()
×
590
                ->arrayNode($key)
×
591
                    ->defaultValue($defaultValue)
×
592
                    ->info('The list of enabled formats. The first one will be the default.')
×
593
                    ->normalizeKeys(false)
×
594
                    ->useAttributeAsKey('format')
×
595
                    ->beforeNormalization()
×
596
                        ->ifArray()
×
597
                        ->then(static function ($v) {
×
598
                            foreach ($v as $format => $value) {
×
599
                                if (isset($value['mime_types'])) {
×
600
                                    continue;
×
601
                                }
602

603
                                $v[$format] = ['mime_types' => $value];
×
604
                            }
605

606
                            return $v;
×
607
                        })
×
608
                    ->end()
×
609
                    ->prototype('array')
×
610
                        ->children()
×
611
                            ->arrayNode('mime_types')->prototype('scalar')->end()->end()
×
612
                        ->end()
×
613
                    ->end()
×
614
                ->end()
×
615
            ->end();
×
616
    }
617

618
    private function addDefaultsSection(ArrayNodeDefinition $rootNode): void
619
    {
620
        $nameConverter = new CamelCaseToSnakeCaseNameConverter();
×
621
        $defaultsNode = $rootNode->children()->arrayNode('defaults');
×
622

623
        $defaultsNode
×
624
            ->ignoreExtraKeys(false)
×
625
            ->beforeNormalization()
×
626
            ->always(static function (array $defaults) use ($nameConverter): array {
×
627
                $normalizedDefaults = [];
×
628
                foreach ($defaults as $option => $value) {
×
629
                    $option = $nameConverter->normalize($option);
×
630
                    $normalizedDefaults[$option] = $value;
×
631
                }
632

633
                return $normalizedDefaults;
×
634
            });
×
635

636
        $this->defineDefault($defaultsNode, new \ReflectionClass(ApiResource::class), $nameConverter);
×
637
        $this->defineDefault($defaultsNode, new \ReflectionClass(Put::class), $nameConverter);
×
638
        $this->defineDefault($defaultsNode, new \ReflectionClass(Post::class), $nameConverter);
×
639
    }
640

641
    private function addMakerSection(ArrayNodeDefinition $rootNode): void
642
    {
643
        $rootNode
×
644
            ->children()
×
645
                ->arrayNode('maker')
×
646
                    ->{class_exists(MakerBundle::class) ? 'canBeDisabled' : 'canBeEnabled'}()
×
647
                ->end()
×
648
            ->end();
×
649
    }
650

651
    private function defineDefault(ArrayNodeDefinition $defaultsNode, \ReflectionClass $reflectionClass, CamelCaseToSnakeCaseNameConverter $nameConverter): void
652
    {
653
        foreach ($reflectionClass->getConstructor()->getParameters() as $parameter) {
×
654
            $defaultsNode->children()->variableNode($nameConverter->normalize($parameter->getName()));
×
655
        }
656
    }
657
}
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