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

api-platform / core / 10738377950

06 Sep 2024 12:29PM UTC coverage: 69.854% (-0.009%) from 69.863%
10738377950

push

github

soyuka
ci: guides components linkage

3163 of 4528 relevant lines covered (69.85%)

74.1 hits per line

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

88.68
/src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.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\Api\FilterInterface as LegacyFilterInterface;
17
use ApiPlatform\Api\QueryParameterValidator\QueryParameterValidator;
18
use ApiPlatform\Doctrine\Odm\Extension\AggregationCollectionExtensionInterface;
19
use ApiPlatform\Doctrine\Odm\Extension\AggregationItemExtensionInterface;
20
use ApiPlatform\Doctrine\Odm\Filter\AbstractFilter as DoctrineMongoDbOdmAbstractFilter;
21
use ApiPlatform\Doctrine\Odm\State\LinksHandlerInterface as OdmLinksHandlerInterface;
22
use ApiPlatform\Doctrine\Orm\Extension\EagerLoadingExtension;
23
use ApiPlatform\Doctrine\Orm\Extension\FilterEagerLoadingExtension;
24
use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface as DoctrineQueryCollectionExtensionInterface;
25
use ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface;
26
use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter as DoctrineOrmAbstractFilter;
27
use ApiPlatform\Doctrine\Orm\State\LinksHandlerInterface as OrmLinksHandlerInterface;
28
use ApiPlatform\Elasticsearch\Extension\RequestBodySearchCollectionExtensionInterface;
29
use ApiPlatform\GraphQl\Error\ErrorHandlerInterface;
30
use ApiPlatform\GraphQl\Executor;
31
use ApiPlatform\GraphQl\Resolver\MutationResolverInterface;
32
use ApiPlatform\GraphQl\Resolver\QueryCollectionResolverInterface;
33
use ApiPlatform\GraphQl\Resolver\QueryItemResolverInterface;
34
use ApiPlatform\GraphQl\Type\Definition\TypeInterface as GraphQlTypeInterface;
35
use ApiPlatform\Hydra\EventListener\AddLinkHeaderListener as HydraAddLinkHeaderListener;
36
use ApiPlatform\Metadata\ApiResource;
37
use ApiPlatform\Metadata\FilterInterface;
38
use ApiPlatform\Metadata\UriVariableTransformerInterface;
39
use ApiPlatform\Metadata\UrlGeneratorInterface;
40
use ApiPlatform\Problem\Serializer\ConstraintViolationListNormalizer;
41
use ApiPlatform\State\ApiResource\Error;
42
use ApiPlatform\State\ParameterProviderInterface;
43
use ApiPlatform\State\ProcessorInterface;
44
use ApiPlatform\State\ProviderInterface;
45
use ApiPlatform\Symfony\EventListener\AddHeadersListener;
46
use ApiPlatform\Symfony\EventListener\AddLinkHeaderListener;
47
use ApiPlatform\Symfony\EventListener\AddTagsListener;
48
use ApiPlatform\Symfony\EventListener\DenyAccessListener;
49
use ApiPlatform\Symfony\GraphQl\Resolver\Factory\DataCollectorResolverFactory;
50
use ApiPlatform\Symfony\Validator\Exception\ValidationException as SymfonyValidationException;
51
use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaRestrictionMetadataInterface;
52
use ApiPlatform\Symfony\Validator\ValidationGroupsGeneratorInterface;
53
use ApiPlatform\Validator\Exception\ValidationException;
54
use Doctrine\Persistence\ManagerRegistry;
55
use phpDocumentor\Reflection\DocBlockFactoryInterface;
56
use PHPStan\PhpDocParser\Parser\PhpDocParser;
57
use Ramsey\Uuid\Uuid;
58
use Symfony\Component\Config\FileLocator;
59
use Symfony\Component\Config\Resource\DirectoryResource;
60
use Symfony\Component\DependencyInjection\ContainerBuilder;
61
use Symfony\Component\DependencyInjection\ContainerInterface;
62
use Symfony\Component\DependencyInjection\Definition;
63
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
64
use Symfony\Component\DependencyInjection\Extension\Extension;
65
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
66
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
67
use Symfony\Component\DependencyInjection\Reference;
68
use Symfony\Component\Finder\Finder;
69
use Symfony\Component\HttpClient\ScopingHttpClient;
70
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
71
use Symfony\Component\Uid\AbstractUid;
72
use Symfony\Component\Validator\Validator\ValidatorInterface;
73
use Symfony\Component\Yaml\Yaml;
74
use Twig\Environment;
75

76
/**
77
 * The extension of this bundle.
78
 *
79
 * @author Kévin Dunglas <dunglas@gmail.com>
80
 */
81
final class ApiPlatformExtension extends Extension implements PrependExtensionInterface
82
{
83
    /**
84
     * {@inheritdoc}
85
     */
86
    public function prepend(ContainerBuilder $container): void
87
    {
88
        if (isset($container->getExtensions()['framework'])) {
4✔
89
            $container->prependExtensionConfig('framework', [
4✔
90
                'serializer' => [
4✔
91
                    'enabled' => true,
4✔
92
                ],
4✔
93
            ]);
4✔
94
            $container->prependExtensionConfig('framework', [
4✔
95
                'property_info' => [
4✔
96
                    'enabled' => true,
4✔
97
                ],
4✔
98
            ]);
4✔
99
        }
100
        if (isset($container->getExtensions()['lexik_jwt_authentication'])) {
4✔
101
            $container->prependExtensionConfig('lexik_jwt_authentication', [
×
102
                'api_platform' => [
×
103
                    'enabled' => true,
×
104
                ],
×
105
            ]);
×
106
        }
107
    }
108

109
    /**
110
     * {@inheritdoc}
111
     */
112
    public function load(array $configs, ContainerBuilder $container): void
113
    {
114
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
152✔
115

116
        $configuration = new Configuration();
152✔
117
        $config = $this->processConfiguration($configuration, $configs);
152✔
118

119
        if (null === $config['use_symfony_listeners']) {
148✔
120
            $config['use_symfony_listeners'] = true;
144✔
121
            trigger_deprecation('api-platform/core', '3.3', 'Setting the value of "use_symfony_listeners" will be mandatory in 4.0 as it will default to "false". Use "true" if you use Symfony Controllers or Event Listeners.');
144✔
122
        }
123

124
        $container->setParameter('api_platform.use_symfony_listeners', $config['use_symfony_listeners']);
148✔
125

126
        if (!$config['formats']) {
148✔
127
            trigger_deprecation('api-platform/core', '3.2', 'Setting the "formats" section will be mandatory in API Platform 4.');
×
128
            $config['formats'] = [
×
129
                'jsonld' => ['mime_types' => ['application/ld+json']],
×
130
                // Note that in API Platform 4 this will be removed as it was used for documentation only and are is now present in the docsFormats
131
                'json' => ['mime_types' => ['application/json']], // Swagger support
×
132
            ];
×
133
        }
134

135
        $formats = $this->getFormats($config['formats']);
148✔
136
        $patchFormats = $this->getFormats($config['patch_formats']);
148✔
137
        $errorFormats = $this->getFormats($config['error_formats']);
148✔
138
        $docsFormats = $this->getFormats($config['docs_formats']);
148✔
139
        $jsonSchemaFormats = $config['jsonschema_formats'];
148✔
140

141
        if (!$jsonSchemaFormats) {
148✔
142
            foreach (array_keys($formats) as $f) {
148✔
143
                // Distinct JSON-based formats must have names that start with 'json'
144
                if (str_starts_with($f, 'json')) {
148✔
145
                    $jsonSchemaFormats[$f] = true;
148✔
146
                }
147
            }
148
        }
149

150
        if (!isset($errorFormats['json'])) {
148✔
151
            $errorFormats['json'] = ['application/problem+json', 'application/json'];
148✔
152
        }
153

154
        if (!isset($errorFormats['jsonproblem'])) {
148✔
155
            $errorFormats['jsonproblem'] = ['application/problem+json'];
×
156
        }
157

158
        if ($this->isConfigEnabled($container, $config['graphql']) && !isset($formats['json'])) {
148✔
159
            trigger_deprecation('api-platform/core', '3.2', 'Add the "json" format to the configuration to use GraphQL.');
4✔
160
            $formats['json'] = ['application/json'];
4✔
161
        }
162

163
        // Backward Compatibility layer
164
        if (isset($formats['jsonapi']) && !isset($patchFormats['jsonapi'])) {
148✔
165
            $patchFormats['jsonapi'] = ['application/vnd.api+json'];
8✔
166
        }
167

168
        if (isset($docsFormats['json']) && !isset($docsFormats['jsonopenapi'])) {
148✔
169
            trigger_deprecation('api-platform/core', '3.2', 'The "json" format is too broad, use ["jsonopenapi" => ["application/vnd.openapi+json"]] instead.');
×
170
            $docsFormats['jsonopenapi'] = ['application/vnd.openapi+json'];
×
171
        }
172

173
        $this->registerCommonConfiguration($container, $config, $loader, $formats, $patchFormats, $errorFormats, $docsFormats, $jsonSchemaFormats);
148✔
174
        $this->registerMetadataConfiguration($container, $config, $loader);
148✔
175
        $this->registerOAuthConfiguration($container, $config);
148✔
176
        $this->registerOpenApiConfiguration($container, $config, $loader);
148✔
177
        $this->registerSwaggerConfiguration($container, $config, $loader);
148✔
178
        $this->registerJsonApiConfiguration($formats, $loader, $config);
148✔
179
        $this->registerJsonLdHydraConfiguration($container, $formats, $loader, $config);
148✔
180
        $this->registerJsonHalConfiguration($formats, $loader);
148✔
181
        $this->registerJsonProblemConfiguration($errorFormats, $loader);
148✔
182
        $this->registerGraphQlConfiguration($container, $config, $loader);
148✔
183
        $this->registerCacheConfiguration($container);
144✔
184
        $this->registerDoctrineOrmConfiguration($container, $config, $loader);
144✔
185
        $this->registerDoctrineMongoDbOdmConfiguration($container, $config, $loader);
144✔
186
        $this->registerHttpCacheConfiguration($container, $config, $loader);
144✔
187
        $this->registerValidatorConfiguration($container, $config, $loader);
144✔
188
        $this->registerDataCollectorConfiguration($container, $config, $loader);
144✔
189
        $this->registerMercureConfiguration($container, $config, $loader);
144✔
190
        $this->registerMessengerConfiguration($container, $config, $loader);
144✔
191
        $this->registerElasticsearchConfiguration($container, $config, $loader);
144✔
192
        $this->registerSecurityConfiguration($container, $config, $loader);
144✔
193
        $this->registerMakerConfiguration($container, $config, $loader);
144✔
194
        $this->registerArgumentResolverConfiguration($loader);
144✔
195
        $this->registerLinkSecurityConfiguration($loader, $config);
144✔
196

197
        $container->registerForAutoconfiguration(FilterInterface::class)
144✔
198
            ->addTag('api_platform.filter');
144✔
199
        $container->registerForAutoconfiguration(LegacyFilterInterface::class)
144✔
200
            ->addTag('api_platform.filter');
144✔
201
        $container->registerForAutoconfiguration(ProviderInterface::class)
144✔
202
            ->addTag('api_platform.state_provider');
144✔
203
        $container->registerForAutoconfiguration(ProcessorInterface::class)
144✔
204
            ->addTag('api_platform.state_processor');
144✔
205
        $container->registerForAutoconfiguration(UriVariableTransformerInterface::class)
144✔
206
            ->addTag('api_platform.uri_variables.transformer');
144✔
207
        $container->registerForAutoconfiguration(ParameterProviderInterface::class)
144✔
208
            ->addTag('api_platform.parameter_provider');
144✔
209

210
        if (!$container->has('api_platform.state.item_provider')) {
144✔
211
            $container->setAlias('api_platform.state.item_provider', 'api_platform.state_provider.object');
×
212
        }
213

214
        $this->registerInflectorConfiguration($container, $config);
144✔
215
    }
216

217
    private function registerCommonConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader, array $formats, array $patchFormats, array $errorFormats, array $docsFormats, array $jsonSchemaFormats): void
218
    {
219
        $loader->load('state/state.xml');
148✔
220
        $loader->load('symfony/symfony.xml');
148✔
221
        $loader->load('api.xml');
148✔
222
        $loader->load('filter.xml');
148✔
223

224
        if (class_exists(Uuid::class)) {
148✔
225
            $loader->load('ramsey_uuid.xml');
148✔
226
        }
227

228
        if (class_exists(AbstractUid::class)) {
148✔
229
            $loader->load('symfony/uid.xml');
148✔
230
        }
231

232
        // TODO: remove in 4.x
233
        $container->setParameter('api_platform.event_listeners_backward_compatibility_layer', $config['event_listeners_backward_compatibility_layer']);
148✔
234

235
        $defaultContext = ['hydra_prefix' => $config['serializer']['hydra_prefix']] + ($container->hasParameter('serializer.default_context') ? $container->getParameter('serializer.default_context') : []);
148✔
236

237
        if (null === $defaultContext['hydra_prefix']) {
148✔
238
            trigger_deprecation('api-platform/core', '3.3', 'The hydra: prefix will be removed in 4.0 by default, set "api_platform.serializer" or "serializer.default_context" to "hydra_prefix: true" to keep the current behavior.');
144✔
239
            $defaultContext['hydra_prefix'] = true;
144✔
240
        }
241

242
        $container->setParameter('api_platform.serializer.default_context', $defaultContext);
148✔
243
        if (!$container->hasParameter('serializer.default_context')) {
148✔
244
            $container->setParameter('serializer.default_context', $container->getParameter('api_platform.serializer.default_context'));
148✔
245
        }
246

247
        if ($config['event_listeners_backward_compatibility_layer']) {
148✔
248
            trigger_deprecation('api-platform/core', '3.3', \sprintf('The "event_listeners_backward_compatibility_layer" will be removed in 4.0. Use the configuration "use_symfony_listeners" to use Symfony listeners. The following listeners are deprecated and will be removed in API Platform 4.0: "%s"', implode(', ', [
145✔
249
                AddHeadersListener::class,
145✔
250
                AddTagsListener::class,
145✔
251
                AddLinkHeaderListener::class,
145✔
252
                HydraAddLinkHeaderListener::class,
145✔
253
                DenyAccessListener::class,
145✔
254
            ])));
145✔
255
        }
256

257
        if ($config['event_listeners_backward_compatibility_layer']) {
148✔
258
            $loader->load('legacy/events.xml');
145✔
259
        }
260

261
        if ($config['use_symfony_listeners']) {
148✔
262
            $loader->load('symfony/events.xml');
144✔
263
        } else {
264
            $loader->load('symfony/controller.xml');
4✔
265
            $loader->load('state/provider.xml');
4✔
266
            $loader->load('state/processor.xml');
4✔
267
        }
268

269
        $container->setParameter('api_platform.enable_entrypoint', $config['enable_entrypoint']);
148✔
270
        $container->setParameter('api_platform.enable_docs', $config['enable_docs']);
148✔
271
        $container->setParameter('api_platform.keep_legacy_inflector', $config['keep_legacy_inflector']);
148✔
272
        $container->setParameter('api_platform.title', $config['title']);
148✔
273
        $container->setParameter('api_platform.description', $config['description']);
148✔
274
        $container->setParameter('api_platform.version', $config['version']);
148✔
275
        $container->setParameter('api_platform.show_webby', $config['show_webby']);
148✔
276
        $container->setParameter('api_platform.url_generation_strategy', $config['defaults']['url_generation_strategy'] ?? UrlGeneratorInterface::ABS_PATH);
148✔
277
        $container->setParameter('api_platform.exception_to_status', $config['exception_to_status']);
148✔
278
        $container->setParameter('api_platform.formats', $formats);
148✔
279
        $container->setParameter('api_platform.patch_formats', $patchFormats);
148✔
280
        $container->setParameter('api_platform.error_formats', $errorFormats);
148✔
281
        $container->setParameter('api_platform.docs_formats', $docsFormats);
148✔
282
        $container->setParameter('api_platform.jsonschema_formats', $jsonSchemaFormats);
148✔
283
        $container->setParameter('api_platform.eager_loading.enabled', $this->isConfigEnabled($container, $config['eager_loading']));
148✔
284
        $container->setParameter('api_platform.eager_loading.max_joins', $config['eager_loading']['max_joins']);
148✔
285
        $container->setParameter('api_platform.eager_loading.fetch_partial', $config['eager_loading']['fetch_partial']);
148✔
286
        $container->setParameter('api_platform.eager_loading.force_eager', $config['eager_loading']['force_eager']);
148✔
287
        $container->setParameter('api_platform.collection.exists_parameter_name', $config['collection']['exists_parameter_name']);
148✔
288
        $container->setParameter('api_platform.collection.order', $config['collection']['order']);
148✔
289
        $container->setParameter('api_platform.collection.order_parameter_name', $config['collection']['order_parameter_name']);
148✔
290
        $container->setParameter('api_platform.collection.order_nulls_comparison', $config['collection']['order_nulls_comparison']);
148✔
291
        $container->setParameter('api_platform.collection.pagination.enabled', $config['defaults']['pagination_enabled'] ?? true);
148✔
292
        $container->setParameter('api_platform.collection.pagination.partial', $config['defaults']['pagination_partial'] ?? false);
148✔
293
        $container->setParameter('api_platform.collection.pagination.client_enabled', $config['defaults']['pagination_client_enabled'] ?? false);
148✔
294
        $container->setParameter('api_platform.collection.pagination.client_items_per_page', $config['defaults']['pagination_client_items_per_page'] ?? false);
148✔
295
        $container->setParameter('api_platform.collection.pagination.client_partial', $config['defaults']['pagination_client_partial'] ?? false);
148✔
296
        $container->setParameter('api_platform.collection.pagination.items_per_page', $config['defaults']['pagination_items_per_page'] ?? 30);
148✔
297
        $container->setParameter('api_platform.collection.pagination.maximum_items_per_page', $config['defaults']['pagination_maximum_items_per_page'] ?? null);
148✔
298
        $container->setParameter('api_platform.collection.pagination.page_parameter_name', $config['defaults']['pagination_page_parameter_name'] ?? $config['collection']['pagination']['page_parameter_name']);
148✔
299
        $container->setParameter('api_platform.collection.pagination.enabled_parameter_name', $config['defaults']['pagination_enabled_parameter_name'] ?? $config['collection']['pagination']['enabled_parameter_name']);
148✔
300
        $container->setParameter('api_platform.collection.pagination.items_per_page_parameter_name', $config['defaults']['pagination_items_per_page_parameter_name'] ?? $config['collection']['pagination']['items_per_page_parameter_name']);
148✔
301
        $container->setParameter('api_platform.collection.pagination.partial_parameter_name', $config['defaults']['pagination_partial_parameter_name'] ?? $config['collection']['pagination']['partial_parameter_name']);
148✔
302
        $container->setParameter('api_platform.collection.pagination', $this->getPaginationDefaults($config['defaults'] ?? [], $config['collection']['pagination']));
148✔
303
        $container->setParameter('api_platform.handle_symfony_errors', $config['handle_symfony_errors'] ?? false);
148✔
304
        $container->setParameter('api_platform.http_cache.etag', $config['defaults']['cache_headers']['etag'] ?? true);
148✔
305
        $container->setParameter('api_platform.http_cache.max_age', $config['defaults']['cache_headers']['max_age'] ?? null);
148✔
306
        $container->setParameter('api_platform.http_cache.shared_max_age', $config['defaults']['cache_headers']['shared_max_age'] ?? null);
148✔
307
        $container->setParameter('api_platform.http_cache.vary', $config['defaults']['cache_headers']['vary'] ?? ['Accept']);
148✔
308
        $container->setParameter('api_platform.http_cache.public', $config['defaults']['cache_headers']['public'] ?? $config['http_cache']['public']);
148✔
309
        $container->setParameter('api_platform.http_cache.invalidation.max_header_length', $config['defaults']['cache_headers']['invalidation']['max_header_length'] ?? $config['http_cache']['invalidation']['max_header_length']);
148✔
310
        $container->setParameter('api_platform.http_cache.invalidation.xkey.glue', $config['defaults']['cache_headers']['invalidation']['xkey']['glue'] ?? $config['http_cache']['invalidation']['xkey']['glue']);
148✔
311

312
        $container->setAlias('api_platform.path_segment_name_generator', $config['path_segment_name_generator']);
148✔
313
        $container->setAlias('api_platform.inflector', $config['inflector']);
148✔
314

315
        if ($config['name_converter']) {
148✔
316
            $container->setAlias('api_platform.name_converter', $config['name_converter']);
8✔
317
        }
318
        $container->setParameter('api_platform.asset_package', $config['asset_package']);
148✔
319
        $container->setParameter('api_platform.defaults', $this->normalizeDefaults($config['defaults'] ?? []));
148✔
320
        $container->setParameter('api_platform.rfc_7807_compliant_errors', $config['defaults']['extra_properties']['rfc_7807_compliant_errors'] ?? false);
148✔
321

322
        if ($container->getParameter('kernel.debug')) {
148✔
323
            $container->removeDefinition('api_platform.serializer.mapping.cache_class_metadata_factory');
16✔
324
        }
325
    }
326

327
    /**
328
     * This method will be removed in 3.0 when "defaults" will be the regular configuration path for the pagination.
329
     */
330
    private function getPaginationDefaults(array $defaults, array $collectionPaginationConfiguration): array
331
    {
332
        $paginationOptions = [];
148✔
333

334
        foreach ($defaults as $key => $value) {
148✔
335
            if (!str_starts_with($key, 'pagination_')) {
148✔
336
                continue;
148✔
337
            }
338

339
            $paginationOptions[str_replace('pagination_', '', $key)] = $value;
4✔
340
        }
341

342
        return array_merge($collectionPaginationConfiguration, $paginationOptions);
148✔
343
    }
344

345
    private function normalizeDefaults(array $defaults): array
346
    {
347
        $normalizedDefaults = ['extra_properties' => $defaults['extra_properties'] ?? []];
148✔
348
        unset($defaults['extra_properties']);
148✔
349

350
        $rc = new \ReflectionClass(ApiResource::class);
148✔
351
        $publicProperties = [];
148✔
352
        foreach ($rc->getConstructor()->getParameters() as $param) {
148✔
353
            $publicProperties[$param->getName()] = true;
148✔
354
        }
355

356
        $nameConverter = new CamelCaseToSnakeCaseNameConverter();
148✔
357
        foreach ($defaults as $option => $value) {
148✔
358
            if (isset($publicProperties[$nameConverter->denormalize($option)])) {
148✔
359
                $normalizedDefaults[$option] = $value;
140✔
360

361
                continue;
140✔
362
            }
363

364
            $normalizedDefaults['extra_properties'][$option] = $value;
8✔
365
        }
366

367
        return $normalizedDefaults;
148✔
368
    }
369

370
    private function registerMetadataConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
371
    {
372
        [$xmlResources, $yamlResources] = $this->getResourcesToWatch($container, $config);
148✔
373

374
        $container->setParameter('api_platform.class_name_resources', $this->getClassNameResources());
148✔
375

376
        $loader->load('metadata/resource_name.xml');
148✔
377
        $loader->load('metadata/property_name.xml');
148✔
378

379
        if (!empty($config['resource_class_directories'])) {
148✔
380
            $container->setParameter('api_platform.resource_class_directories', array_merge(
×
381
                $config['resource_class_directories'],
×
382
                $container->getParameter('api_platform.resource_class_directories')
×
383
            ));
×
384
        }
385

386
        // V3 metadata
387
        $loader->load('metadata/xml.xml');
148✔
388
        $loader->load('metadata/links.xml');
148✔
389
        $loader->load('metadata/property.xml');
148✔
390
        $loader->load('metadata/resource.xml');
148✔
391
        $loader->load('metadata/operation.xml');
148✔
392

393
        $container->getDefinition('api_platform.metadata.resource_extractor.xml')->replaceArgument(0, $xmlResources);
148✔
394
        $container->getDefinition('api_platform.metadata.property_extractor.xml')->replaceArgument(0, $xmlResources);
148✔
395

396
        if (class_exists(PhpDocParser::class) || interface_exists(DocBlockFactoryInterface::class)) {
148✔
397
            $loader->load('metadata/php_doc.xml');
148✔
398
        }
399

400
        if (class_exists(Yaml::class)) {
148✔
401
            $loader->load('metadata/yaml.xml');
148✔
402
            $container->getDefinition('api_platform.metadata.resource_extractor.yaml')->replaceArgument(0, $yamlResources);
148✔
403
            $container->getDefinition('api_platform.metadata.property_extractor.yaml')->replaceArgument(0, $yamlResources);
148✔
404
        }
405
    }
406

407
    private function getClassNameResources(): array
408
    {
409
        return [
148✔
410
            Error::class,
148✔
411
            SymfonyValidationException::class,
148✔
412
            ValidationException::class,
148✔
413
        ];
148✔
414
    }
415

416
    private function getBundlesResourcesPaths(ContainerBuilder $container, array $config): array
417
    {
418
        $bundlesResourcesPaths = [];
148✔
419

420
        foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) {
148✔
421
            $dirname = $bundle['path'];
148✔
422
            $paths = [
148✔
423
                "$dirname/ApiResource",
148✔
424
                "$dirname/src/ApiResource",
148✔
425
            ];
148✔
426
            foreach (['.yaml', '.yml', '.xml', ''] as $extension) {
148✔
427
                $paths[] = "$dirname/Resources/config/api_resources$extension";
148✔
428
                $paths[] = "$dirname/config/api_resources$extension";
148✔
429
            }
430
            if ($this->isConfigEnabled($container, $config['doctrine'])) {
148✔
431
                $paths[] = "$dirname/Entity";
148✔
432
                $paths[] = "$dirname/src/Entity";
148✔
433
            }
434
            if ($this->isConfigEnabled($container, $config['doctrine_mongodb_odm'])) {
148✔
435
                $paths[] = "$dirname/Document";
144✔
436
                $paths[] = "$dirname/src/Document";
144✔
437
            }
438

439
            foreach ($paths as $path) {
148✔
440
                if ($container->fileExists($path, false)) {
148✔
441
                    $bundlesResourcesPaths[] = $path;
148✔
442
                }
443
            }
444
        }
445

446
        return $bundlesResourcesPaths;
148✔
447
    }
448

449
    private function getResourcesToWatch(ContainerBuilder $container, array $config): array
450
    {
451
        $paths = array_unique(array_merge($this->getBundlesResourcesPaths($container, $config), $config['mapping']['paths']));
148✔
452

453
        if (!$config['mapping']['paths']) {
148✔
454
            $projectDir = $container->getParameter('kernel.project_dir');
144✔
455
            foreach (["$projectDir/config/api_platform", "$projectDir/src/ApiResource"] as $dir) {
144✔
456
                if (is_dir($dir)) {
144✔
457
                    $paths[] = $dir;
144✔
458
                }
459
            }
460

461
            if ($this->isConfigEnabled($container, $config['doctrine']) && is_dir($doctrinePath = "$projectDir/src/Entity")) {
144✔
462
                $paths[] = $doctrinePath;
×
463
            }
464

465
            if ($this->isConfigEnabled($container, $config['doctrine_mongodb_odm']) && is_dir($documentPath = "$projectDir/src/Document")) {
144✔
466
                $paths[] = $documentPath;
×
467
            }
468
        }
469

470
        $resources = ['yml' => [], 'xml' => [], 'dir' => []];
148✔
471

472
        foreach ($paths as $path) {
148✔
473
            if (is_dir($path)) {
148✔
474
                foreach (Finder::create()->followLinks()->files()->in($path)->name('/\.(xml|ya?ml)$/')->sortByName() as $file) {
148✔
475
                    $resources['yaml' === ($extension = $file->getExtension()) ? 'yml' : $extension][] = $file->getRealPath();
148✔
476
                }
477

478
                $resources['dir'][] = $path;
148✔
479
                $container->addResource(new DirectoryResource($path, '/\.(xml|ya?ml|php)$/'));
148✔
480

481
                continue;
148✔
482
            }
483

484
            if ($container->fileExists($path, false)) {
×
485
                if (!preg_match('/\.(xml|ya?ml)$/', (string) $path, $matches)) {
×
486
                    throw new RuntimeException(\sprintf('Unsupported mapping type in "%s", supported types are XML & YAML.', $path));
×
487
                }
488

489
                $resources['yaml' === $matches[1] ? 'yml' : $matches[1]][] = $path;
×
490

491
                continue;
×
492
            }
493

494
            throw new RuntimeException(\sprintf('Could not open file or directory "%s".', $path));
×
495
        }
496

497
        $container->setParameter('api_platform.resource_class_directories', $resources['dir']);
148✔
498

499
        return [$resources['xml'], $resources['yml']];
148✔
500
    }
501

502
    private function registerOAuthConfiguration(ContainerBuilder $container, array $config): void
503
    {
504
        if (!$config['oauth']) {
148✔
505
            return;
×
506
        }
507

508
        $container->setParameter('api_platform.oauth.enabled', $this->isConfigEnabled($container, $config['oauth']));
148✔
509
        $container->setParameter('api_platform.oauth.clientId', $config['oauth']['clientId']);
148✔
510
        $container->setParameter('api_platform.oauth.clientSecret', $config['oauth']['clientSecret']);
148✔
511
        $container->setParameter('api_platform.oauth.type', $config['oauth']['type']);
148✔
512
        $container->setParameter('api_platform.oauth.flow', $config['oauth']['flow']);
148✔
513
        $container->setParameter('api_platform.oauth.tokenUrl', $config['oauth']['tokenUrl']);
148✔
514
        $container->setParameter('api_platform.oauth.authorizationUrl', $config['oauth']['authorizationUrl']);
148✔
515
        $container->setParameter('api_platform.oauth.refreshUrl', $config['oauth']['refreshUrl']);
148✔
516
        $container->setParameter('api_platform.oauth.scopes', $config['oauth']['scopes']);
148✔
517
        $container->setParameter('api_platform.oauth.pkce', $config['oauth']['pkce']);
148✔
518

519
        if ($container->hasDefinition('api_platform.swagger_ui.action')) {
148✔
520
            $container->getDefinition('api_platform.swagger_ui.action')->setArgument(10, $config['oauth']['pkce']);
×
521
        }
522
    }
523

524
    /**
525
     * Registers the Swagger, ReDoc and Swagger UI configuration.
526
     */
527
    private function registerSwaggerConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
528
    {
529
        foreach (array_keys($config['swagger']['api_keys']) as $keyName) {
148✔
530
            if (!preg_match('/^[a-zA-Z0-9._-]+$/', $keyName)) {
4✔
531
                trigger_deprecation('api-platform/core', '3.1', \sprintf('The swagger api_keys key "%s" is not valid with OpenAPI 3.1 it should match "^[a-zA-Z0-9._-]+$"', $keyName));
×
532
            }
533
        }
534

535
        $container->setParameter('api_platform.swagger.versions', $config['swagger']['versions']);
148✔
536

537
        if (!$config['enable_swagger'] && $config['enable_swagger_ui']) {
148✔
538
            throw new RuntimeException('You can not enable the Swagger UI without enabling Swagger, fix this by enabling swagger via the configuration "enable_swagger: true".');
×
539
        }
540

541
        if (!$config['enable_swagger']) {
148✔
542
            return;
×
543
        }
544

545
        $loader->load('openapi.xml');
148✔
546

547
        if ($config['use_deprecated_json_schema_type_factory'] ?? false) {
148✔
548
            $container->getDefinition('api_platform.openapi.factory')->setArgument('$jsonSchemaTypeFactory', new Reference('api_platform.json_schema.type_factory'));
4✔
549
        }
550

551
        if (class_exists(Yaml::class)) {
148✔
552
            $loader->load('openapi/yaml.xml');
148✔
553
        }
554

555
        $loader->load('swagger_ui.xml');
148✔
556

557
        if ($config['event_listeners_backward_compatibility_layer']) {
148✔
558
            $loader->load('legacy/swagger_ui.xml');
145✔
559
        }
560

561
        if ($config['use_symfony_listeners']) {
148✔
562
            $loader->load('symfony/swagger_ui.xml');
144✔
563
        }
564

565
        if ($config['enable_swagger_ui']) {
148✔
566
            $loader->load('state/swagger_ui.xml');
144✔
567
        }
568

569
        if (!$config['enable_swagger_ui'] && !$config['enable_re_doc']) {
148✔
570
            // Remove the listener but keep the controller to allow customizing the path of the UI
571
            $container->removeDefinition('api_platform.swagger.listener.ui');
×
572
        }
573

574
        $container->setParameter('api_platform.enable_swagger_ui', $config['enable_swagger_ui']);
148✔
575
        $container->setParameter('api_platform.enable_re_doc', $config['enable_re_doc']);
148✔
576
        $container->setParameter('api_platform.swagger.api_keys', $config['swagger']['api_keys']);
148✔
577
        if ($config['openapi']['swagger_ui_extra_configuration'] && $config['swagger']['swagger_ui_extra_configuration']) {
148✔
578
            throw new RuntimeException('You can not set "swagger_ui_extra_configuration" twice - in "openapi" and "swagger" section.');
×
579
        }
580
        $container->setParameter('api_platform.swagger_ui.extra_configuration', $config['openapi']['swagger_ui_extra_configuration'] ?: $config['swagger']['swagger_ui_extra_configuration']);
148✔
581
    }
582

583
    private function registerJsonApiConfiguration(array $formats, XmlFileLoader $loader, array $config): void
584
    {
585
        if (!isset($formats['jsonapi'])) {
148✔
586
            return;
140✔
587
        }
588

589
        if ($config['event_listeners_backward_compatibility_layer']) {
8✔
590
            $loader->load('legacy/jsonapi.xml');
5✔
591
        }
592

593
        $loader->load('jsonapi.xml');
8✔
594
        $loader->load('state/jsonapi.xml');
8✔
595
    }
596

597
    private function registerJsonLdHydraConfiguration(ContainerBuilder $container, array $formats, XmlFileLoader $loader, array $config): void
598
    {
599
        if (!isset($formats['jsonld'])) {
148✔
600
            return;
×
601
        }
602

603
        if ($config['use_symfony_listeners']) {
148✔
604
            $loader->load('symfony/jsonld.xml');
144✔
605
        } else {
606
            $loader->load('state/jsonld.xml');
4✔
607
        }
608

609
        if ($config['event_listeners_backward_compatibility_layer']) {
148✔
610
            $loader->load('legacy/hydra.xml');
145✔
611
        }
612

613
        $loader->load('state/hydra.xml');
148✔
614
        $loader->load('jsonld.xml');
148✔
615
        $loader->load('hydra.xml');
148✔
616

617
        if (!$container->has('api_platform.json_schema.schema_factory')) {
148✔
618
            $container->removeDefinition('api_platform.hydra.json_schema.schema_factory');
×
619
        }
620

621
        if (!$config['enable_docs']) {
148✔
622
            $container->removeDefinition('api_platform.hydra.listener.response.add_link_header');
×
623
            $container->removeDefinition('api_platform.hydra.processor.link');
×
624
        }
625
    }
626

627
    private function registerJsonHalConfiguration(array $formats, XmlFileLoader $loader): void
628
    {
629
        if (!isset($formats['jsonhal'])) {
148✔
630
            return;
×
631
        }
632

633
        $loader->load('hal.xml');
148✔
634
    }
635

636
    private function registerJsonProblemConfiguration(array $errorFormats, XmlFileLoader $loader): void
637
    {
638
        if (!isset($errorFormats['jsonproblem'])) {
148✔
639
            return;
×
640
        }
641

642
        if (class_exists(ConstraintViolationListNormalizer::class)) {
148✔
643
            $loader->load('legacy/problem.xml');
148✔
644
        }
645

646
        $loader->load('problem.xml');
148✔
647
    }
648

649
    private function registerGraphQlConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
650
    {
651
        $enabled = $this->isConfigEnabled($container, $config['graphql']);
148✔
652
        $graphqlIntrospectionEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['introspection']);
148✔
653

654
        $graphiqlEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['graphiql']);
148✔
655
        $graphqlPlayGroundEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['graphql_playground']);
148✔
656
        if ($graphqlPlayGroundEnabled) {
148✔
657
            trigger_deprecation('api-platform/core', '3.1', 'GraphQL Playground is deprecated and will be removed in API Platform 4.0. Only GraphiQL will be available in the future. Set api_platform.graphql.graphql_playground to false in the configuration to remove this deprecation.');
×
658
        }
659

660
        $container->setParameter('api_platform.graphql.enabled', $enabled);
148✔
661
        $container->setParameter('api_platform.graphql.introspection.enabled', $graphqlIntrospectionEnabled);
148✔
662
        $container->setParameter('api_platform.graphql.graphiql.enabled', $graphiqlEnabled);
148✔
663
        $container->setParameter('api_platform.graphql.graphql_playground.enabled', $graphqlPlayGroundEnabled);
148✔
664
        $container->setParameter('api_platform.graphql.collection.pagination', $config['graphql']['collection']['pagination']);
148✔
665

666
        if (!$enabled) {
148✔
667
            return;
×
668
        }
669

670
        if (!class_exists(Executor::class)) {
148✔
671
            throw new \RuntimeException('Graphql is enabled but not installed, run: composer require "api-platform/graphql".');
×
672
        }
673

674
        $container->setParameter('api_platform.graphql.default_ide', $config['graphql']['default_ide']);
148✔
675
        $container->setParameter('api_platform.graphql.nesting_separator', $config['graphql']['nesting_separator']);
148✔
676

677
        $loader->load('graphql.xml');
148✔
678

679
        // @phpstan-ignore-next-line because PHPStan uses the container of the test env cache and in test the parameter kernel.bundles always contains the key TwigBundle
680
        if (!class_exists(Environment::class) || !isset($container->getParameter('kernel.bundles')['TwigBundle'])) {
148✔
681
            if ($graphiqlEnabled || $graphqlPlayGroundEnabled) {
8✔
682
                throw new RuntimeException(\sprintf('GraphiQL and GraphQL Playground interfaces depend on Twig. Please activate TwigBundle for the %s environnement or disable GraphiQL and GraphQL Playground.', $container->getParameter('kernel.environment')));
4✔
683
            }
684
            $container->removeDefinition('api_platform.graphql.action.graphiql');
4✔
685
            $container->removeDefinition('api_platform.graphql.action.graphql_playground');
4✔
686
        }
687

688
        $container->registerForAutoconfiguration(QueryItemResolverInterface::class)
144✔
689
            ->addTag('api_platform.graphql.resolver');
144✔
690
        $container->registerForAutoconfiguration(QueryCollectionResolverInterface::class)
144✔
691
            ->addTag('api_platform.graphql.resolver');
144✔
692
        $container->registerForAutoconfiguration(MutationResolverInterface::class)
144✔
693
            ->addTag('api_platform.graphql.resolver');
144✔
694
        $container->registerForAutoconfiguration(GraphQlTypeInterface::class)
144✔
695
            ->addTag('api_platform.graphql.type');
144✔
696
        $container->registerForAutoconfiguration(ErrorHandlerInterface::class)
144✔
697
            ->addTag('api_platform.graphql.error_handler');
144✔
698

699
        /* TODO: remove these in 4.x only one resolver factory is used and we're using providers/processors */
700
        if ($config['event_listeners_backward_compatibility_layer']) {
144✔
701
            // @TODO: API Platform 3.3 trigger_deprecation('api-platform/core', '3.3', 'In API Platform 4 only one factory "api_platform.graphql.resolver.factory.item" will remain. Stages are deprecated in favor of using a provider/processor.');
702
            // + deprecate every service from legacy/graphql.xml
703
            $loader->load('legacy/graphql.xml');
141✔
704

705
            if (!$container->getParameter('kernel.debug')) {
141✔
706
                return;
128✔
707
            }
708

709
            $requestStack = new Reference('request_stack', ContainerInterface::NULL_ON_INVALID_REFERENCE);
13✔
710
            $collectionDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
13✔
711
                ->setDecoratedService('api_platform.graphql.resolver.factory.collection')
13✔
712
                ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.collection.inner'), $requestStack]);
13✔
713

714
            $itemDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
13✔
715
                ->setDecoratedService('api_platform.graphql.resolver.factory.item')
13✔
716
                ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.item.inner'), $requestStack]);
13✔
717

718
            $itemMutationDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
13✔
719
                ->setDecoratedService('api_platform.graphql.resolver.factory.item_mutation')
13✔
720
                ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.item_mutation.inner'), $requestStack]);
13✔
721

722
            $itemSubscriptionDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
13✔
723
                ->setDecoratedService('api_platform.graphql.resolver.factory.item_subscription')
13✔
724
                ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.item_subscription.inner'), $requestStack]);
13✔
725

726
            $container->addDefinitions([
13✔
727
                'api_platform.graphql.data_collector.resolver.factory.collection' => $collectionDataCollectorResolverFactory,
13✔
728
                'api_platform.graphql.data_collector.resolver.factory.item' => $itemDataCollectorResolverFactory,
13✔
729
                'api_platform.graphql.data_collector.resolver.factory.item_mutation' => $itemMutationDataCollectorResolverFactory,
13✔
730
                'api_platform.graphql.data_collector.resolver.factory.item_subscription' => $itemSubscriptionDataCollectorResolverFactory,
13✔
731
            ]);
13✔
732
        }
733
    }
734

735
    private function registerCacheConfiguration(ContainerBuilder $container): void
736
    {
737
        if (!$container->hasParameter('kernel.debug') || !$container->getParameter('kernel.debug')) {
144✔
738
            $container->removeDefinition('api_platform.cache_warmer.cache_pool_clearer');
128✔
739
        }
740
    }
741

742
    private function registerDoctrineOrmConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
743
    {
744
        if (!$this->isConfigEnabled($container, $config['doctrine'])) {
144✔
745
            return;
×
746
        }
747

748
        // For older versions of doctrine bridge this allows autoconfiguration for filters
749
        if (!$container->has(ManagerRegistry::class)) {
144✔
750
            $container->setAlias(ManagerRegistry::class, 'doctrine');
144✔
751
        }
752

753
        $container->registerForAutoconfiguration(QueryItemExtensionInterface::class)
144✔
754
            ->addTag('api_platform.doctrine.orm.query_extension.item');
144✔
755
        $container->registerForAutoconfiguration(DoctrineQueryCollectionExtensionInterface::class)
144✔
756
            ->addTag('api_platform.doctrine.orm.query_extension.collection');
144✔
757
        $container->registerForAutoconfiguration(DoctrineOrmAbstractFilter::class);
144✔
758

759
        $container->registerForAutoconfiguration(OrmLinksHandlerInterface::class)
144✔
760
            ->addTag('api_platform.doctrine.orm.links_handler');
144✔
761

762
        $loader->load('doctrine_orm.xml');
144✔
763

764
        if ($this->isConfigEnabled($container, $config['eager_loading'])) {
144✔
765
            return;
144✔
766
        }
767

768
        $container->removeAlias(EagerLoadingExtension::class);
×
769
        $container->removeDefinition('api_platform.doctrine.orm.query_extension.eager_loading');
×
770
        $container->removeAlias(FilterEagerLoadingExtension::class);
×
771
        $container->removeDefinition('api_platform.doctrine.orm.query_extension.filter_eager_loading');
×
772
    }
773

774
    private function registerDoctrineMongoDbOdmConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
775
    {
776
        if (!$this->isConfigEnabled($container, $config['doctrine_mongodb_odm'])) {
144✔
777
            return;
4✔
778
        }
779

780
        $container->registerForAutoconfiguration(AggregationItemExtensionInterface::class)
140✔
781
            ->addTag('api_platform.doctrine_mongodb.odm.aggregation_extension.item');
140✔
782
        $container->registerForAutoconfiguration(AggregationCollectionExtensionInterface::class)
140✔
783
            ->addTag('api_platform.doctrine_mongodb.odm.aggregation_extension.collection');
140✔
784
        $container->registerForAutoconfiguration(DoctrineMongoDbOdmAbstractFilter::class)
140✔
785
            ->setBindings(['$managerRegistry' => new Reference('doctrine_mongodb')]);
140✔
786
        $container->registerForAutoconfiguration(OdmLinksHandlerInterface::class)
140✔
787
            ->addTag('api_platform.doctrine.odm.links_handler');
140✔
788

789
        $loader->load('doctrine_mongodb_odm.xml');
140✔
790
    }
791

792
    private function registerHttpCacheConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
793
    {
794
        $loader->load('http_cache.xml');
144✔
795

796
        if ($config['event_listeners_backward_compatibility_layer']) {
144✔
797
            $loader->load('legacy/http_cache.xml');
141✔
798
        }
799

800
        if (!$this->isConfigEnabled($container, $config['http_cache']['invalidation'])) {
144✔
801
            return;
×
802
        }
803

804
        if ($this->isConfigEnabled($container, $config['doctrine'])) {
144✔
805
            $loader->load('doctrine_orm_http_cache_purger.xml');
144✔
806
        }
807

808
        if ($config['event_listeners_backward_compatibility_layer']) {
144✔
809
            $loader->load('legacy/http_cache_purger.xml');
141✔
810
        }
811

812
        $loader->load('state/http_cache_purger.xml');
144✔
813
        $loader->load('http_cache_purger.xml');
144✔
814

815
        foreach ($config['http_cache']['invalidation']['scoped_clients'] as $client) {
144✔
816
            $definition = $container->getDefinition($client);
4✔
817
            $definition->addTag('api_platform.http_cache.http_client');
4✔
818
        }
819

820
        if (!($urls = $config['http_cache']['invalidation']['urls'])) {
144✔
821
            $urls = $config['http_cache']['invalidation']['varnish_urls'];
140✔
822
        }
823

824
        foreach ($urls as $key => $url) {
144✔
825
            $definition = new Definition(ScopingHttpClient::class, [new Reference('http_client'), $url, ['base_uri' => $url] + $config['http_cache']['invalidation']['request_options']]);
8✔
826
            $definition->setFactory([ScopingHttpClient::class, 'forBaseUri']);
8✔
827
            $definition->addTag('api_platform.http_cache.http_client');
8✔
828
            $container->setDefinition('api_platform.invalidation_http_client.'.$key, $definition);
8✔
829
        }
830

831
        $serviceName = $config['http_cache']['invalidation']['purger'];
144✔
832

833
        if (!$container->hasDefinition('api_platform.http_cache.purger')) {
144✔
834
            $container->setAlias('api_platform.http_cache.purger', $serviceName);
144✔
835
        }
836
    }
837

838
    /**
839
     * Normalizes the format from config to the one accepted by Symfony HttpFoundation.
840
     */
841
    private function getFormats(array $configFormats): array
842
    {
843
        $formats = [];
148✔
844
        foreach ($configFormats as $format => $value) {
148✔
845
            foreach ($value['mime_types'] as $mimeType) {
148✔
846
                $formats[$format][] = $mimeType;
148✔
847
            }
848
        }
849

850
        return $formats;
148✔
851
    }
852

853
    private function registerValidatorConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
854
    {
855
        if (interface_exists(ValidatorInterface::class)) {
144✔
856
            $container->setParameter('api_platform.validator.legacy_validation_exception', $config['validator']['legacy_validation_exception'] ?? true);
144✔
857
            $loader->load('metadata/validator.xml');
144✔
858
            $loader->load('validator/validator.xml');
144✔
859

860
            if ($this->isConfigEnabled($container, $config['graphql'])) {
144✔
861
                $loader->load('graphql/validator.xml');
144✔
862
            }
863

864
            if ($config['event_listeners_backward_compatibility_layer']) {
144✔
865
                $loader->load('legacy/validator.xml');
141✔
866
            }
867

868
            $loader->load($config['use_symfony_listeners'] ? 'validator/events.xml' : 'validator/state.xml');
144✔
869

870
            $container->registerForAutoconfiguration(ValidationGroupsGeneratorInterface::class)
144✔
871
                ->addTag('api_platform.validation_groups_generator');
144✔
872
            $container->registerForAutoconfiguration(PropertySchemaRestrictionMetadataInterface::class)
144✔
873
                ->addTag('api_platform.metadata.property_schema_restriction');
144✔
874
        }
875

876
        if (!$config['validator']) {
144✔
877
            return;
×
878
        }
879

880
        $container->setParameter('api_platform.validator.serialize_payload_fields', $config['validator']['serialize_payload_fields']);
144✔
881
        $container->setParameter('api_platform.validator.query_parameter_validation', $config['validator']['query_parameter_validation']);
144✔
882

883
        if (class_exists(QueryParameterValidator::class)) {
144✔
884
            $loader->load('legacy/parameter_validator/parameter_validator.xml');
144✔
885
            $loader->load($config['use_symfony_listeners'] ? 'legacy/parameter_validator/events.xml' : 'legacy/parameter_validator/state.xml');
144✔
886
        }
887

888
        if (!$config['validator']['query_parameter_validation']) {
144✔
889
            $container->removeDefinition('api_platform.listener.view.validate_query_parameters');
×
890
            $container->removeDefinition('api_platform.validator.query_parameter_validator');
×
891
            $container->removeDefinition('api_platform.symfony.parameter_validator');
×
892
        }
893
    }
894

895
    private function registerDataCollectorConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
896
    {
897
        if (!$config['enable_profiler']) {
144✔
898
            return;
×
899
        }
900

901
        $loader->load('data_collector.xml');
144✔
902

903
        if ($container->hasParameter('kernel.debug') && $container->getParameter('kernel.debug')) {
144✔
904
            $loader->load('debug.xml');
16✔
905
        }
906
    }
907

908
    private function registerMercureConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
909
    {
910
        if (!$this->isConfigEnabled($container, $config['mercure'])) {
144✔
911
            return;
×
912
        }
913

914
        $container->setParameter('api_platform.mercure.include_type', $config['mercure']['include_type']);
144✔
915

916
        if ($config['event_listeners_backward_compatibility_layer']) {
144✔
917
            $loader->load('legacy/mercure.xml');
141✔
918
        }
919

920
        $loader->load('state/mercure.xml');
144✔
921

922
        if ($this->isConfigEnabled($container, $config['doctrine'])) {
144✔
923
            $loader->load('doctrine_orm_mercure_publisher.xml');
144✔
924
        }
925
        if ($this->isConfigEnabled($container, $config['doctrine_mongodb_odm'])) {
144✔
926
            $loader->load('doctrine_odm_mercure_publisher.xml');
140✔
927
        }
928

929
        if ($this->isConfigEnabled($container, $config['graphql'])) {
144✔
930
            $loader->load('graphql_mercure.xml');
144✔
931
        }
932
    }
933

934
    private function registerMessengerConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
935
    {
936
        if (!$this->isConfigEnabled($container, $config['messenger'])) {
144✔
937
            return;
×
938
        }
939

940
        $loader->load('messenger.xml');
144✔
941
    }
942

943
    private function registerElasticsearchConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
944
    {
945
        $enabled = $this->isConfigEnabled($container, $config['elasticsearch']);
144✔
946

947
        $container->setParameter('api_platform.elasticsearch.enabled', $enabled);
144✔
948

949
        if (!$enabled) {
144✔
950
            return;
140✔
951
        }
952

953
        $clientClass = class_exists(\Elasticsearch\Client::class) ? \Elasticsearch\Client::class : \Elastic\Elasticsearch\Client::class;
4✔
954

955
        $clientDefinition = new Definition($clientClass);
4✔
956
        $container->setDefinition('api_platform.elasticsearch.client', $clientDefinition);
4✔
957
        $container->registerForAutoconfiguration(RequestBodySearchCollectionExtensionInterface::class)
4✔
958
            ->addTag('api_platform.elasticsearch.request_body_search_extension.collection');
4✔
959
        $container->setParameter('api_platform.elasticsearch.hosts', $config['elasticsearch']['hosts']);
4✔
960
        $loader->load('elasticsearch.xml');
4✔
961

962
        // @phpstan-ignore-next-line
963
        if (\Elasticsearch\Client::class === $clientClass) {
4✔
964
            $loader->load('legacy/elasticsearch.xml');
×
965
            $container->setParameter('api_platform.elasticsearch.mapping', $config['elasticsearch']['mapping']);
×
966
            $container->setDefinition('api_platform.elasticsearch.client_for_metadata', $clientDefinition);
×
967
        }
968
    }
969

970
    private function registerSecurityConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
971
    {
972
        /** @var string[] $bundles */
973
        $bundles = $container->getParameter('kernel.bundles');
144✔
974

975
        if (!isset($bundles['SecurityBundle'])) {
144✔
976
            return;
×
977
        }
978

979
        $loader->load('security.xml');
144✔
980

981
        if ($config['event_listeners_backward_compatibility_layer']) {
144✔
982
            $loader->load('legacy/security.xml');
141✔
983
        }
984

985
        $loader->load('state/security.xml');
144✔
986

987
        if (interface_exists(ValidatorInterface::class)) {
144✔
988
            $loader->load('state/security_validator.xml');
144✔
989
        }
990

991
        if ($this->isConfigEnabled($container, $config['graphql'])) {
144✔
992
            $loader->load('graphql/security.xml');
144✔
993
        }
994
    }
995

996
    private function registerOpenApiConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
997
    {
998
        $container->setParameter('api_platform.openapi.termsOfService', $config['openapi']['termsOfService']);
148✔
999
        $container->setParameter('api_platform.openapi.contact.name', $config['openapi']['contact']['name']);
148✔
1000
        $container->setParameter('api_platform.openapi.contact.url', $config['openapi']['contact']['url']);
148✔
1001
        $container->setParameter('api_platform.openapi.contact.email', $config['openapi']['contact']['email']);
148✔
1002
        $container->setParameter('api_platform.openapi.license.name', $config['openapi']['license']['name']);
148✔
1003
        $container->setParameter('api_platform.openapi.license.url', $config['openapi']['license']['url']);
148✔
1004
        $container->setParameter('api_platform.openapi.overrideResponses', $config['openapi']['overrideResponses']);
148✔
1005

1006
        $loader->load('json_schema.xml');
148✔
1007

1008
        if ($config['use_deprecated_json_schema_type_factory'] ?? false) {
148✔
1009
            $container->getDefinition('api_platform.json_schema.schema_factory')->setArgument('$typeFactory', new Reference('api_platform.json_schema.type_factory'));
4✔
1010
        }
1011
    }
1012

1013
    private function registerMakerConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
1014
    {
1015
        if (!$this->isConfigEnabled($container, $config['maker'])) {
144✔
1016
            return;
×
1017
        }
1018

1019
        $loader->load('maker.xml');
144✔
1020
    }
1021

1022
    private function registerArgumentResolverConfiguration(XmlFileLoader $loader): void
1023
    {
1024
        $loader->load('argument_resolver.xml');
144✔
1025
    }
1026

1027
    private function registerInflectorConfiguration(ContainerBuilder $container, array $config): void
1028
    {
1029
        $container->setParameter('api_platform.keep_legacy_inflector', $config['keep_legacy_inflector'] ?? false);
144✔
1030

1031
        if ($config['keep_legacy_inflector']) {
144✔
1032
            trigger_deprecation('api-platform/core', '3.2', 'Using doctrine/inflector is deprecated since API Platform 3.2 and will be removed in API Platform 4. Use symfony/string instead. Run "composer require symfony/string" and set "keep_legacy_inflector" to false in config.');
×
1033
        }
1034
    }
1035

1036
    private function registerLinkSecurityConfiguration(XmlFileLoader $loader, array $config): void
1037
    {
1038
        if ($config['enable_link_security']) {
144✔
1039
            $loader->load('link_security.xml');
4✔
1040
        }
1041
    }
1042
}
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