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

api-platform / core / 9710836697

28 Jun 2024 09:35AM UTC coverage: 63.285% (+1.2%) from 62.122%
9710836697

push

github

soyuka
docs: changelog v3.3.7

11104 of 17546 relevant lines covered (63.29%)

52.26 hits per line

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

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

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

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

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

118
        if (null === $config['use_symfony_listeners']) {
148✔
119
            $config['use_symfony_listeners'] = true;
144✔
120
            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✔
121
        }
122

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

125
        if (!$config['formats']) {
148✔
126
            trigger_deprecation('api-platform/core', '3.2', 'Setting the "formats" section will be mandatory in API Platform 4.');
×
127
            $config['formats'] = [
×
128
                'jsonld' => ['mime_types' => ['application/ld+json']],
×
129
                // 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
130
                'json' => ['mime_types' => ['application/json']], // Swagger support
×
131
            ];
×
132
        }
133

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

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

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

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

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

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

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

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

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

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

213
        $this->registerInflectorConfiguration($config);
144✔
214
    }
215

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

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

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

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

234
        if ($config['event_listeners_backward_compatibility_layer']) {
148✔
235
            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✔
236
                AddHeadersListener::class,
145✔
237
                AddTagsListener::class,
145✔
238
                AddLinkHeaderListener::class,
145✔
239
                HydraAddLinkHeaderListener::class,
145✔
240
                DenyAccessListener::class,
145✔
241
            ])));
145✔
242
        }
243

244
        if ($config['event_listeners_backward_compatibility_layer']) {
148✔
245
            $loader->load('legacy/events.xml');
145✔
246
        }
247

248
        if ($config['use_symfony_listeners']) {
148✔
249
            $loader->load('symfony/events.xml');
144✔
250
        } else {
251
            $loader->load('symfony/controller.xml');
4✔
252
            $loader->load('state/provider.xml');
4✔
253
            $loader->load('state/processor.xml');
4✔
254
        }
255

256
        $container->setParameter('api_platform.enable_entrypoint', $config['enable_entrypoint']);
148✔
257
        $container->setParameter('api_platform.enable_docs', $config['enable_docs']);
148✔
258
        $container->setParameter('api_platform.keep_legacy_inflector', $config['keep_legacy_inflector']);
148✔
259
        $container->setParameter('api_platform.title', $config['title']);
148✔
260
        $container->setParameter('api_platform.description', $config['description']);
148✔
261
        $container->setParameter('api_platform.version', $config['version']);
148✔
262
        $container->setParameter('api_platform.show_webby', $config['show_webby']);
148✔
263
        $container->setParameter('api_platform.url_generation_strategy', $config['defaults']['url_generation_strategy'] ?? UrlGeneratorInterface::ABS_PATH);
148✔
264
        $container->setParameter('api_platform.exception_to_status', $config['exception_to_status']);
148✔
265
        $container->setParameter('api_platform.formats', $formats);
148✔
266
        $container->setParameter('api_platform.patch_formats', $patchFormats);
148✔
267
        $container->setParameter('api_platform.error_formats', $errorFormats);
148✔
268
        $container->setParameter('api_platform.docs_formats', $docsFormats);
148✔
269
        $container->setParameter('api_platform.jsonschema_formats', $jsonSchemaFormats);
148✔
270
        $container->setParameter('api_platform.eager_loading.enabled', $this->isConfigEnabled($container, $config['eager_loading']));
148✔
271
        $container->setParameter('api_platform.eager_loading.max_joins', $config['eager_loading']['max_joins']);
148✔
272
        $container->setParameter('api_platform.eager_loading.fetch_partial', $config['eager_loading']['fetch_partial']);
148✔
273
        $container->setParameter('api_platform.eager_loading.force_eager', $config['eager_loading']['force_eager']);
148✔
274
        $container->setParameter('api_platform.collection.exists_parameter_name', $config['collection']['exists_parameter_name']);
148✔
275
        $container->setParameter('api_platform.collection.order', $config['collection']['order']);
148✔
276
        $container->setParameter('api_platform.collection.order_parameter_name', $config['collection']['order_parameter_name']);
148✔
277
        $container->setParameter('api_platform.collection.order_nulls_comparison', $config['collection']['order_nulls_comparison']);
148✔
278
        $container->setParameter('api_platform.collection.pagination.enabled', $config['defaults']['pagination_enabled'] ?? true);
148✔
279
        $container->setParameter('api_platform.collection.pagination.partial', $config['defaults']['pagination_partial'] ?? false);
148✔
280
        $container->setParameter('api_platform.collection.pagination.client_enabled', $config['defaults']['pagination_client_enabled'] ?? false);
148✔
281
        $container->setParameter('api_platform.collection.pagination.client_items_per_page', $config['defaults']['pagination_client_items_per_page'] ?? false);
148✔
282
        $container->setParameter('api_platform.collection.pagination.client_partial', $config['defaults']['pagination_client_partial'] ?? false);
148✔
283
        $container->setParameter('api_platform.collection.pagination.items_per_page', $config['defaults']['pagination_items_per_page'] ?? 30);
148✔
284
        $container->setParameter('api_platform.collection.pagination.maximum_items_per_page', $config['defaults']['pagination_maximum_items_per_page'] ?? null);
148✔
285
        $container->setParameter('api_platform.collection.pagination.page_parameter_name', $config['defaults']['pagination_page_parameter_name'] ?? $config['collection']['pagination']['page_parameter_name']);
148✔
286
        $container->setParameter('api_platform.collection.pagination.enabled_parameter_name', $config['defaults']['pagination_enabled_parameter_name'] ?? $config['collection']['pagination']['enabled_parameter_name']);
148✔
287
        $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✔
288
        $container->setParameter('api_platform.collection.pagination.partial_parameter_name', $config['defaults']['pagination_partial_parameter_name'] ?? $config['collection']['pagination']['partial_parameter_name']);
148✔
289
        $container->setParameter('api_platform.collection.pagination', $this->getPaginationDefaults($config['defaults'] ?? [], $config['collection']['pagination']));
148✔
290
        $container->setParameter('api_platform.handle_symfony_errors', $config['handle_symfony_errors'] ?? false);
148✔
291
        $container->setParameter('api_platform.http_cache.etag', $config['defaults']['cache_headers']['etag'] ?? true);
148✔
292
        $container->setParameter('api_platform.http_cache.max_age', $config['defaults']['cache_headers']['max_age'] ?? null);
148✔
293
        $container->setParameter('api_platform.http_cache.shared_max_age', $config['defaults']['cache_headers']['shared_max_age'] ?? null);
148✔
294
        $container->setParameter('api_platform.http_cache.vary', $config['defaults']['cache_headers']['vary'] ?? ['Accept']);
148✔
295
        $container->setParameter('api_platform.http_cache.public', $config['defaults']['cache_headers']['public'] ?? $config['http_cache']['public']);
148✔
296
        $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✔
297
        $container->setParameter('api_platform.http_cache.invalidation.xkey.glue', $config['defaults']['cache_headers']['invalidation']['xkey']['glue'] ?? $config['http_cache']['invalidation']['xkey']['glue']);
148✔
298

299
        $container->setAlias('api_platform.path_segment_name_generator', $config['path_segment_name_generator']);
148✔
300

301
        if ($config['name_converter']) {
148✔
302
            $container->setAlias('api_platform.name_converter', $config['name_converter']);
8✔
303
        }
304
        $container->setParameter('api_platform.asset_package', $config['asset_package']);
148✔
305
        $container->setParameter('api_platform.defaults', $this->normalizeDefaults($config['defaults'] ?? []));
148✔
306
        $container->setParameter('api_platform.rfc_7807_compliant_errors', $config['defaults']['extra_properties']['rfc_7807_compliant_errors'] ?? false);
148✔
307

308
        if ($container->getParameter('kernel.debug')) {
148✔
309
            $container->removeDefinition('api_platform.serializer.mapping.cache_class_metadata_factory');
16✔
310
        }
311
    }
312

313
    /**
314
     * This method will be removed in 3.0 when "defaults" will be the regular configuration path for the pagination.
315
     */
316
    private function getPaginationDefaults(array $defaults, array $collectionPaginationConfiguration): array
317
    {
318
        $paginationOptions = [];
148✔
319

320
        foreach ($defaults as $key => $value) {
148✔
321
            if (!str_starts_with($key, 'pagination_')) {
148✔
322
                continue;
148✔
323
            }
324

325
            $paginationOptions[str_replace('pagination_', '', $key)] = $value;
4✔
326
        }
327

328
        return array_merge($collectionPaginationConfiguration, $paginationOptions);
148✔
329
    }
330

331
    private function normalizeDefaults(array $defaults): array
332
    {
333
        $normalizedDefaults = ['extra_properties' => $defaults['extra_properties'] ?? []];
148✔
334
        unset($defaults['extra_properties']);
148✔
335

336
        $rc = new \ReflectionClass(ApiResource::class);
148✔
337
        $publicProperties = [];
148✔
338
        foreach ($rc->getConstructor()->getParameters() as $param) {
148✔
339
            $publicProperties[$param->getName()] = true;
148✔
340
        }
341

342
        $nameConverter = new CamelCaseToSnakeCaseNameConverter();
148✔
343
        foreach ($defaults as $option => $value) {
148✔
344
            if (isset($publicProperties[$nameConverter->denormalize($option)])) {
148✔
345
                $normalizedDefaults[$option] = $value;
140✔
346

347
                continue;
140✔
348
            }
349

350
            $normalizedDefaults['extra_properties'][$option] = $value;
8✔
351
        }
352

353
        return $normalizedDefaults;
148✔
354
    }
355

356
    private function registerMetadataConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
357
    {
358
        [$xmlResources, $yamlResources] = $this->getResourcesToWatch($container, $config);
148✔
359

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

362
        $loader->load('metadata/resource_name.xml');
148✔
363
        $loader->load('metadata/property_name.xml');
148✔
364

365
        if (!empty($config['resource_class_directories'])) {
148✔
366
            $container->setParameter('api_platform.resource_class_directories', array_merge(
×
367
                $config['resource_class_directories'],
×
368
                $container->getParameter('api_platform.resource_class_directories')
×
369
            ));
×
370
        }
371

372
        // V3 metadata
373
        $loader->load('metadata/xml.xml');
148✔
374
        $loader->load('metadata/links.xml');
148✔
375
        $loader->load('metadata/property.xml');
148✔
376
        $loader->load('metadata/resource.xml');
148✔
377
        $loader->load('metadata/operation.xml');
148✔
378

379
        $container->getDefinition('api_platform.metadata.resource_extractor.xml')->replaceArgument(0, $xmlResources);
148✔
380
        $container->getDefinition('api_platform.metadata.property_extractor.xml')->replaceArgument(0, $xmlResources);
148✔
381

382
        if (class_exists(PhpDocParser::class) || interface_exists(DocBlockFactoryInterface::class)) {
148✔
383
            $loader->load('metadata/php_doc.xml');
148✔
384
        }
385

386
        if (class_exists(Yaml::class)) {
148✔
387
            $loader->load('metadata/yaml.xml');
148✔
388
            $container->getDefinition('api_platform.metadata.resource_extractor.yaml')->replaceArgument(0, $yamlResources);
148✔
389
            $container->getDefinition('api_platform.metadata.property_extractor.yaml')->replaceArgument(0, $yamlResources);
148✔
390
        }
391
    }
392

393
    private function getClassNameResources(): array
394
    {
395
        return [
148✔
396
            Error::class,
148✔
397
            SymfonyValidationException::class,
148✔
398
            ValidationException::class,
148✔
399
        ];
148✔
400
    }
401

402
    private function getBundlesResourcesPaths(ContainerBuilder $container, array $config): array
403
    {
404
        $bundlesResourcesPaths = [];
148✔
405

406
        foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) {
148✔
407
            $dirname = $bundle['path'];
148✔
408
            $paths = [
148✔
409
                "$dirname/ApiResource",
148✔
410
                "$dirname/src/ApiResource",
148✔
411
            ];
148✔
412
            foreach (['.yaml', '.yml', '.xml', ''] as $extension) {
148✔
413
                $paths[] = "$dirname/Resources/config/api_resources$extension";
148✔
414
                $paths[] = "$dirname/config/api_resources$extension";
148✔
415
            }
416
            if ($this->isConfigEnabled($container, $config['doctrine'])) {
148✔
417
                $paths[] = "$dirname/Entity";
148✔
418
                $paths[] = "$dirname/src/Entity";
148✔
419
            }
420
            if ($this->isConfigEnabled($container, $config['doctrine_mongodb_odm'])) {
148✔
421
                $paths[] = "$dirname/Document";
144✔
422
                $paths[] = "$dirname/src/Document";
144✔
423
            }
424

425
            foreach ($paths as $path) {
148✔
426
                if ($container->fileExists($path, false)) {
148✔
427
                    $bundlesResourcesPaths[] = $path;
148✔
428
                }
429
            }
430
        }
431

432
        return $bundlesResourcesPaths;
148✔
433
    }
434

435
    private function getResourcesToWatch(ContainerBuilder $container, array $config): array
436
    {
437
        $paths = array_unique(array_merge($this->getBundlesResourcesPaths($container, $config), $config['mapping']['paths']));
148✔
438

439
        if (!$config['mapping']['paths']) {
148✔
440
            $projectDir = $container->getParameter('kernel.project_dir');
144✔
441
            foreach (["$projectDir/config/api_platform", "$projectDir/src/ApiResource"] as $dir) {
144✔
442
                if (is_dir($dir)) {
144✔
443
                    $paths[] = $dir;
144✔
444
                }
445
            }
446

447
            if ($this->isConfigEnabled($container, $config['doctrine']) && is_dir($doctrinePath = "$projectDir/src/Entity")) {
144✔
448
                $paths[] = $doctrinePath;
×
449
            }
450

451
            if ($this->isConfigEnabled($container, $config['doctrine_mongodb_odm']) && is_dir($documentPath = "$projectDir/src/Document")) {
144✔
452
                $paths[] = $documentPath;
×
453
            }
454
        }
455

456
        $resources = ['yml' => [], 'xml' => [], 'dir' => []];
148✔
457

458
        foreach ($paths as $path) {
148✔
459
            if (is_dir($path)) {
148✔
460
                foreach (Finder::create()->followLinks()->files()->in($path)->name('/\.(xml|ya?ml)$/')->sortByName() as $file) {
148✔
461
                    $resources['yaml' === ($extension = $file->getExtension()) ? 'yml' : $extension][] = $file->getRealPath();
148✔
462
                }
463

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

467
                continue;
148✔
468
            }
469

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

475
                $resources['yaml' === $matches[1] ? 'yml' : $matches[1]][] = $path;
×
476

477
                continue;
×
478
            }
479

480
            throw new RuntimeException(sprintf('Could not open file or directory "%s".', $path));
×
481
        }
482

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

485
        return [$resources['xml'], $resources['yml']];
148✔
486
    }
487

488
    private function registerOAuthConfiguration(ContainerBuilder $container, array $config): void
489
    {
490
        if (!$config['oauth']) {
148✔
491
            return;
×
492
        }
493

494
        $container->setParameter('api_platform.oauth.enabled', $this->isConfigEnabled($container, $config['oauth']));
148✔
495
        $container->setParameter('api_platform.oauth.clientId', $config['oauth']['clientId']);
148✔
496
        $container->setParameter('api_platform.oauth.clientSecret', $config['oauth']['clientSecret']);
148✔
497
        $container->setParameter('api_platform.oauth.type', $config['oauth']['type']);
148✔
498
        $container->setParameter('api_platform.oauth.flow', $config['oauth']['flow']);
148✔
499
        $container->setParameter('api_platform.oauth.tokenUrl', $config['oauth']['tokenUrl']);
148✔
500
        $container->setParameter('api_platform.oauth.authorizationUrl', $config['oauth']['authorizationUrl']);
148✔
501
        $container->setParameter('api_platform.oauth.refreshUrl', $config['oauth']['refreshUrl']);
148✔
502
        $container->setParameter('api_platform.oauth.scopes', $config['oauth']['scopes']);
148✔
503
        $container->setParameter('api_platform.oauth.pkce', $config['oauth']['pkce']);
148✔
504

505
        if ($container->hasDefinition('api_platform.swagger_ui.action')) {
148✔
506
            $container->getDefinition('api_platform.swagger_ui.action')->setArgument(10, $config['oauth']['pkce']);
×
507
        }
508
    }
509

510
    /**
511
     * Registers the Swagger, ReDoc and Swagger UI configuration.
512
     */
513
    private function registerSwaggerConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
514
    {
515
        foreach (array_keys($config['swagger']['api_keys']) as $keyName) {
148✔
516
            if (!preg_match('/^[a-zA-Z0-9._-]+$/', $keyName)) {
4✔
517
                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));
×
518
            }
519
        }
520

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

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

527
        if (!$config['enable_swagger']) {
148✔
528
            return;
×
529
        }
530

531
        $loader->load('openapi.xml');
148✔
532
        $loader->load('swagger_ui.xml');
148✔
533

534
        if ($config['event_listeners_backward_compatibility_layer']) {
148✔
535
            $loader->load('legacy/swagger_ui.xml');
145✔
536
        }
537

538
        if ($config['use_symfony_listeners']) {
148✔
539
            $loader->load('symfony/swagger_ui.xml');
144✔
540
        }
541

542
        if ($config['enable_swagger_ui']) {
148✔
543
            $loader->load('state/swagger_ui.xml');
144✔
544
        }
545

546
        if (!$config['enable_swagger_ui'] && !$config['enable_re_doc']) {
148✔
547
            // Remove the listener but keep the controller to allow customizing the path of the UI
548
            $container->removeDefinition('api_platform.swagger.listener.ui');
×
549
        }
550

551
        $container->setParameter('api_platform.enable_swagger_ui', $config['enable_swagger_ui']);
148✔
552
        $container->setParameter('api_platform.enable_re_doc', $config['enable_re_doc']);
148✔
553
        $container->setParameter('api_platform.swagger.api_keys', $config['swagger']['api_keys']);
148✔
554
        if ($config['openapi']['swagger_ui_extra_configuration'] && $config['swagger']['swagger_ui_extra_configuration']) {
148✔
555
            throw new RuntimeException('You can not set "swagger_ui_extra_configuration" twice - in "openapi" and "swagger" section.');
×
556
        }
557
        $container->setParameter('api_platform.swagger_ui.extra_configuration', $config['openapi']['swagger_ui_extra_configuration'] ?: $config['swagger']['swagger_ui_extra_configuration']);
148✔
558
    }
559

560
    private function registerJsonApiConfiguration(array $formats, XmlFileLoader $loader, array $config): void
561
    {
562
        if (!isset($formats['jsonapi'])) {
148✔
563
            return;
140✔
564
        }
565

566
        if ($config['event_listeners_backward_compatibility_layer']) {
8✔
567
            $loader->load('legacy/jsonapi.xml');
5✔
568
        }
569

570
        $loader->load('jsonapi.xml');
8✔
571
        $loader->load('state/jsonapi.xml');
8✔
572
    }
573

574
    private function registerJsonLdHydraConfiguration(ContainerBuilder $container, array $formats, XmlFileLoader $loader, array $config): void
575
    {
576
        if (!isset($formats['jsonld'])) {
148✔
577
            return;
×
578
        }
579

580
        if ($config['use_symfony_listeners']) {
148✔
581
            $loader->load('symfony/jsonld.xml');
144✔
582
        } else {
583
            $loader->load('state/jsonld.xml');
4✔
584
        }
585

586
        if ($config['event_listeners_backward_compatibility_layer']) {
148✔
587
            $loader->load('legacy/hydra.xml');
145✔
588
        }
589

590
        $loader->load('state/hydra.xml');
148✔
591
        $loader->load('jsonld.xml');
148✔
592
        $loader->load('hydra.xml');
148✔
593

594
        if (!$container->has('api_platform.json_schema.schema_factory')) {
148✔
595
            $container->removeDefinition('api_platform.hydra.json_schema.schema_factory');
×
596
        }
597

598
        if (!$config['enable_docs']) {
148✔
599
            $container->removeDefinition('api_platform.hydra.listener.response.add_link_header');
×
600
            $container->removeDefinition('api_platform.hydra.processor.link');
×
601
        }
602
    }
603

604
    private function registerJsonHalConfiguration(array $formats, XmlFileLoader $loader): void
605
    {
606
        if (!isset($formats['jsonhal'])) {
148✔
607
            return;
×
608
        }
609

610
        $loader->load('hal.xml');
148✔
611
    }
612

613
    private function registerJsonProblemConfiguration(array $errorFormats, XmlFileLoader $loader): void
614
    {
615
        if (!isset($errorFormats['jsonproblem'])) {
148✔
616
            return;
×
617
        }
618

619
        if (class_exists(ConstraintViolationListNormalizer::class)) {
148✔
620
            $loader->load('legacy/problem.xml');
148✔
621
        }
622

623
        $loader->load('problem.xml');
148✔
624
    }
625

626
    private function registerGraphQlConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
627
    {
628
        $enabled = $this->isConfigEnabled($container, $config['graphql']);
148✔
629

630
        $graphqlIntrospectionEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['introspection']);
148✔
631

632
        $graphiqlEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['graphiql']);
148✔
633
        $graphqlPlayGroundEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['graphql_playground']);
148✔
634
        if ($graphqlPlayGroundEnabled) {
148✔
635
            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.');
×
636
        }
637

638
        $container->setParameter('api_platform.graphql.enabled', $enabled);
148✔
639
        $container->setParameter('api_platform.graphql.introspection.enabled', $graphqlIntrospectionEnabled);
148✔
640
        $container->setParameter('api_platform.graphql.graphiql.enabled', $graphiqlEnabled);
148✔
641
        $container->setParameter('api_platform.graphql.graphql_playground.enabled', $graphqlPlayGroundEnabled);
148✔
642
        $container->setParameter('api_platform.graphql.collection.pagination', $config['graphql']['collection']['pagination']);
148✔
643

644
        if (!$enabled) {
148✔
645
            return;
×
646
        }
647

648
        $container->setParameter('api_platform.graphql.default_ide', $config['graphql']['default_ide']);
148✔
649
        $container->setParameter('api_platform.graphql.nesting_separator', $config['graphql']['nesting_separator']);
148✔
650

651
        $loader->load('graphql.xml');
148✔
652

653
        // @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
654
        if (!class_exists(Environment::class) || !isset($container->getParameter('kernel.bundles')['TwigBundle'])) {
148✔
655
            if ($graphiqlEnabled || $graphqlPlayGroundEnabled) {
8✔
656
                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✔
657
            }
658
            $container->removeDefinition('api_platform.graphql.action.graphiql');
4✔
659
            $container->removeDefinition('api_platform.graphql.action.graphql_playground');
4✔
660
        }
661

662
        $container->registerForAutoconfiguration(QueryItemResolverInterface::class)
144✔
663
            ->addTag('api_platform.graphql.resolver');
144✔
664
        $container->registerForAutoconfiguration(QueryCollectionResolverInterface::class)
144✔
665
            ->addTag('api_platform.graphql.resolver');
144✔
666
        $container->registerForAutoconfiguration(MutationResolverInterface::class)
144✔
667
            ->addTag('api_platform.graphql.resolver');
144✔
668
        $container->registerForAutoconfiguration(GraphQlTypeInterface::class)
144✔
669
            ->addTag('api_platform.graphql.type');
144✔
670
        $container->registerForAutoconfiguration(ErrorHandlerInterface::class)
144✔
671
            ->addTag('api_platform.graphql.error_handler');
144✔
672

673
        /* TODO: remove these in 4.x only one resolver factory is used and we're using providers/processors */
674
        if ($config['event_listeners_backward_compatibility_layer']) {
144✔
675
            // @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.');
676
            // + deprecate every service from legacy/graphql.xml
677
            $loader->load('legacy/graphql.xml');
141✔
678

679
            if (!$container->getParameter('kernel.debug')) {
141✔
680
                return;
128✔
681
            }
682

683
            $requestStack = new Reference('request_stack', ContainerInterface::NULL_ON_INVALID_REFERENCE);
13✔
684
            $collectionDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
13✔
685
                ->setDecoratedService('api_platform.graphql.resolver.factory.collection')
13✔
686
                ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.collection.inner'), $requestStack]);
13✔
687

688
            $itemDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
13✔
689
                ->setDecoratedService('api_platform.graphql.resolver.factory.item')
13✔
690
                ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.item.inner'), $requestStack]);
13✔
691

692
            $itemMutationDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
13✔
693
                ->setDecoratedService('api_platform.graphql.resolver.factory.item_mutation')
13✔
694
                ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.item_mutation.inner'), $requestStack]);
13✔
695

696
            $itemSubscriptionDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
13✔
697
                ->setDecoratedService('api_platform.graphql.resolver.factory.item_subscription')
13✔
698
                ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.item_subscription.inner'), $requestStack]);
13✔
699

700
            $container->addDefinitions([
13✔
701
                'api_platform.graphql.data_collector.resolver.factory.collection' => $collectionDataCollectorResolverFactory,
13✔
702
                'api_platform.graphql.data_collector.resolver.factory.item' => $itemDataCollectorResolverFactory,
13✔
703
                'api_platform.graphql.data_collector.resolver.factory.item_mutation' => $itemMutationDataCollectorResolverFactory,
13✔
704
                'api_platform.graphql.data_collector.resolver.factory.item_subscription' => $itemSubscriptionDataCollectorResolverFactory,
13✔
705
            ]);
13✔
706
        }
707
    }
708

709
    private function registerCacheConfiguration(ContainerBuilder $container): void
710
    {
711
        if (!$container->hasParameter('kernel.debug') || !$container->getParameter('kernel.debug')) {
144✔
712
            $container->removeDefinition('api_platform.cache_warmer.cache_pool_clearer');
128✔
713
        }
714
    }
715

716
    private function registerDoctrineOrmConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
717
    {
718
        if (!$this->isConfigEnabled($container, $config['doctrine'])) {
144✔
719
            return;
×
720
        }
721

722
        // For older versions of doctrine bridge this allows autoconfiguration for filters
723
        if (!$container->has(ManagerRegistry::class)) {
144✔
724
            $container->setAlias(ManagerRegistry::class, 'doctrine');
144✔
725
        }
726

727
        $container->registerForAutoconfiguration(QueryItemExtensionInterface::class)
144✔
728
            ->addTag('api_platform.doctrine.orm.query_extension.item');
144✔
729
        $container->registerForAutoconfiguration(DoctrineQueryCollectionExtensionInterface::class)
144✔
730
            ->addTag('api_platform.doctrine.orm.query_extension.collection');
144✔
731
        $container->registerForAutoconfiguration(DoctrineOrmAbstractFilter::class);
144✔
732

733
        $container->registerForAutoconfiguration(OrmLinksHandlerInterface::class)
144✔
734
            ->addTag('api_platform.doctrine.orm.links_handler');
144✔
735

736
        $loader->load('doctrine_orm.xml');
144✔
737

738
        if ($this->isConfigEnabled($container, $config['eager_loading'])) {
144✔
739
            return;
144✔
740
        }
741

742
        $container->removeAlias(EagerLoadingExtension::class);
×
743
        $container->removeDefinition('api_platform.doctrine.orm.query_extension.eager_loading');
×
744
        $container->removeAlias(FilterEagerLoadingExtension::class);
×
745
        $container->removeDefinition('api_platform.doctrine.orm.query_extension.filter_eager_loading');
×
746
    }
747

748
    private function registerDoctrineMongoDbOdmConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
749
    {
750
        if (!$this->isConfigEnabled($container, $config['doctrine_mongodb_odm'])) {
144✔
751
            return;
4✔
752
        }
753

754
        $container->registerForAutoconfiguration(AggregationItemExtensionInterface::class)
140✔
755
            ->addTag('api_platform.doctrine_mongodb.odm.aggregation_extension.item');
140✔
756
        $container->registerForAutoconfiguration(AggregationCollectionExtensionInterface::class)
140✔
757
            ->addTag('api_platform.doctrine_mongodb.odm.aggregation_extension.collection');
140✔
758
        $container->registerForAutoconfiguration(DoctrineMongoDbOdmAbstractFilter::class)
140✔
759
            ->setBindings(['$managerRegistry' => new Reference('doctrine_mongodb')]);
140✔
760
        $container->registerForAutoconfiguration(OdmLinksHandlerInterface::class)
140✔
761
            ->addTag('api_platform.doctrine.odm.links_handler');
140✔
762

763
        $loader->load('doctrine_mongodb_odm.xml');
140✔
764
    }
765

766
    private function registerHttpCacheConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
767
    {
768
        $loader->load('http_cache.xml');
144✔
769

770
        if ($config['event_listeners_backward_compatibility_layer']) {
144✔
771
            $loader->load('legacy/http_cache.xml');
141✔
772
        }
773

774
        if (!$this->isConfigEnabled($container, $config['http_cache']['invalidation'])) {
144✔
775
            return;
×
776
        }
777

778
        if ($this->isConfigEnabled($container, $config['doctrine'])) {
144✔
779
            $loader->load('doctrine_orm_http_cache_purger.xml');
144✔
780
        }
781

782
        if ($config['event_listeners_backward_compatibility_layer']) {
144✔
783
            $loader->load('legacy/http_cache_purger.xml');
141✔
784
        }
785

786
        $loader->load('state/http_cache_purger.xml');
144✔
787
        $loader->load('http_cache_purger.xml');
144✔
788

789
        foreach ($config['http_cache']['invalidation']['scoped_clients'] as $client) {
144✔
790
            $definition = $container->getDefinition($client);
4✔
791
            $definition->addTag('api_platform.http_cache.http_client');
4✔
792
        }
793

794
        if (!($urls = $config['http_cache']['invalidation']['urls'])) {
144✔
795
            $urls = $config['http_cache']['invalidation']['varnish_urls'];
140✔
796
        }
797

798
        foreach ($urls as $key => $url) {
144✔
799
            $definition = new Definition(ScopingHttpClient::class, [new Reference('http_client'), $url, ['base_uri' => $url] + $config['http_cache']['invalidation']['request_options']]);
8✔
800
            $definition->setFactory([ScopingHttpClient::class, 'forBaseUri']);
8✔
801
            $definition->addTag('api_platform.http_cache.http_client');
8✔
802
            $container->setDefinition('api_platform.invalidation_http_client.'.$key, $definition);
8✔
803
        }
804

805
        $serviceName = $config['http_cache']['invalidation']['purger'];
144✔
806

807
        if (!$container->hasDefinition('api_platform.http_cache.purger')) {
144✔
808
            $container->setAlias('api_platform.http_cache.purger', $serviceName);
144✔
809
        }
810
    }
811

812
    /**
813
     * Normalizes the format from config to the one accepted by Symfony HttpFoundation.
814
     */
815
    private function getFormats(array $configFormats): array
816
    {
817
        $formats = [];
148✔
818
        foreach ($configFormats as $format => $value) {
148✔
819
            foreach ($value['mime_types'] as $mimeType) {
148✔
820
                $formats[$format][] = $mimeType;
148✔
821
            }
822
        }
823

824
        return $formats;
148✔
825
    }
826

827
    private function registerValidatorConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
828
    {
829
        if (interface_exists(ValidatorInterface::class)) {
144✔
830
            $container->setParameter('api_platform.validator.legacy_validation_exception', $config['validator']['legacy_validation_exception'] ?? true);
144✔
831
            $loader->load('metadata/validator.xml');
144✔
832
            $loader->load('validator/validator.xml');
144✔
833

834
            if ($this->isConfigEnabled($container, $config['graphql'])) {
144✔
835
                $loader->load('graphql/validator.xml');
144✔
836
            }
837

838
            if ($config['event_listeners_backward_compatibility_layer']) {
144✔
839
                $loader->load('legacy/validator.xml');
141✔
840
            }
841

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

844
            $container->registerForAutoconfiguration(ValidationGroupsGeneratorInterface::class)
144✔
845
                ->addTag('api_platform.validation_groups_generator');
144✔
846
            $container->registerForAutoconfiguration(PropertySchemaRestrictionMetadataInterface::class)
144✔
847
                ->addTag('api_platform.metadata.property_schema_restriction');
144✔
848
        }
849

850
        if (!$config['validator']) {
144✔
851
            return;
×
852
        }
853

854
        $container->setParameter('api_platform.validator.serialize_payload_fields', $config['validator']['serialize_payload_fields']);
144✔
855
        $container->setParameter('api_platform.validator.query_parameter_validation', $config['validator']['query_parameter_validation']);
144✔
856

857
        if (!$config['validator']['query_parameter_validation']) {
144✔
858
            $container->removeDefinition('api_platform.listener.view.validate_query_parameters');
×
859
            $container->removeDefinition('api_platform.validator.query_parameter_validator');
×
860
            $container->removeDefinition('api_platform.symfony.parameter_validator');
×
861
        }
862
    }
863

864
    private function registerDataCollectorConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
865
    {
866
        if (!$config['enable_profiler']) {
144✔
867
            return;
×
868
        }
869

870
        $loader->load('data_collector.xml');
144✔
871

872
        if ($container->hasParameter('kernel.debug') && $container->getParameter('kernel.debug')) {
144✔
873
            $loader->load('debug.xml');
16✔
874
        }
875
    }
876

877
    private function registerMercureConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
878
    {
879
        if (!$this->isConfigEnabled($container, $config['mercure'])) {
144✔
880
            return;
×
881
        }
882

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

885
        if ($config['event_listeners_backward_compatibility_layer']) {
144✔
886
            $loader->load('legacy/mercure.xml');
141✔
887
        }
888

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

891
        if ($this->isConfigEnabled($container, $config['doctrine'])) {
144✔
892
            $loader->load('doctrine_orm_mercure_publisher.xml');
144✔
893
        }
894
        if ($this->isConfigEnabled($container, $config['doctrine_mongodb_odm'])) {
144✔
895
            $loader->load('doctrine_odm_mercure_publisher.xml');
140✔
896
        }
897

898
        if ($this->isConfigEnabled($container, $config['graphql'])) {
144✔
899
            $loader->load('graphql_mercure.xml');
144✔
900
        }
901
    }
902

903
    private function registerMessengerConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
904
    {
905
        if (!$this->isConfigEnabled($container, $config['messenger'])) {
144✔
906
            return;
×
907
        }
908

909
        $loader->load('messenger.xml');
144✔
910
    }
911

912
    private function registerElasticsearchConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
913
    {
914
        $enabled = $this->isConfigEnabled($container, $config['elasticsearch']);
144✔
915

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

918
        if (!$enabled) {
144✔
919
            return;
140✔
920
        }
921

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

924
        $clientDefinition = new Definition($clientClass);
4✔
925
        $container->setDefinition('api_platform.elasticsearch.client', $clientDefinition);
4✔
926
        $container->registerForAutoconfiguration(RequestBodySearchCollectionExtensionInterface::class)
4✔
927
            ->addTag('api_platform.elasticsearch.request_body_search_extension.collection');
4✔
928
        $container->setParameter('api_platform.elasticsearch.hosts', $config['elasticsearch']['hosts']);
4✔
929
        $loader->load('elasticsearch.xml');
4✔
930

931
        // @phpstan-ignore-next-line
932
        if (\Elasticsearch\Client::class === $clientClass) {
4✔
933
            $loader->load('legacy/elasticsearch.xml');
×
934
            $container->setParameter('api_platform.elasticsearch.mapping', $config['elasticsearch']['mapping']);
×
935
            $container->setDefinition('api_platform.elasticsearch.client_for_metadata', $clientDefinition);
×
936
        }
937
    }
938

939
    private function registerSecurityConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
940
    {
941
        /** @var string[] $bundles */
942
        $bundles = $container->getParameter('kernel.bundles');
144✔
943

944
        if (!isset($bundles['SecurityBundle'])) {
144✔
945
            return;
×
946
        }
947

948
        $loader->load('security.xml');
144✔
949

950
        if ($config['event_listeners_backward_compatibility_layer']) {
144✔
951
            $loader->load('legacy/security.xml');
141✔
952
        }
953

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

956
        if (interface_exists(ValidatorInterface::class) && !$config['use_symfony_listeners']) {
144✔
957
            $loader->load('state/security_validator.xml');
4✔
958
        }
959

960
        if ($this->isConfigEnabled($container, $config['graphql'])) {
144✔
961
            $loader->load('graphql/security.xml');
144✔
962
        }
963
    }
964

965
    private function registerOpenApiConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
966
    {
967
        $container->setParameter('api_platform.openapi.termsOfService', $config['openapi']['termsOfService']);
148✔
968
        $container->setParameter('api_platform.openapi.contact.name', $config['openapi']['contact']['name']);
148✔
969
        $container->setParameter('api_platform.openapi.contact.url', $config['openapi']['contact']['url']);
148✔
970
        $container->setParameter('api_platform.openapi.contact.email', $config['openapi']['contact']['email']);
148✔
971
        $container->setParameter('api_platform.openapi.license.name', $config['openapi']['license']['name']);
148✔
972
        $container->setParameter('api_platform.openapi.license.url', $config['openapi']['license']['url']);
148✔
973
        $container->setParameter('api_platform.openapi.overrideResponses', $config['openapi']['overrideResponses']);
148✔
974

975
        $loader->load('json_schema.xml');
148✔
976
    }
977

978
    private function registerMakerConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
979
    {
980
        if (!$this->isConfigEnabled($container, $config['maker'])) {
144✔
981
            return;
×
982
        }
983

984
        $loader->load('maker.xml');
144✔
985
    }
986

987
    private function registerArgumentResolverConfiguration(XmlFileLoader $loader): void
988
    {
989
        $loader->load('argument_resolver.xml');
144✔
990
    }
991

992
    private function registerInflectorConfiguration(array $config): void
993
    {
994
        if ($config['keep_legacy_inflector']) {
144✔
995
            Inflector::keepLegacyInflector(true);
×
996
            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.');
×
997
        } else {
998
            Inflector::keepLegacyInflector(false);
144✔
999
        }
1000
    }
1001

1002
    private function registerLinkSecurityConfiguration(XmlFileLoader $loader, array $config): void
1003
    {
1004
        if ($config['enable_link_security']) {
144✔
1005
            $loader->load('link_security.xml');
4✔
1006
        }
1007
    }
1008
}
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