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

api-platform / core / 7582727293

19 Jan 2024 10:45AM UTC coverage: 61.96% (+2.8%) from 59.207%
7582727293

push

github

web-flow
feat(symfony): request and view kernel listeners (#6102)

133 of 266 new or added lines in 19 files covered. (50.0%)

447 existing lines in 32 files now uncovered.

17435 of 28139 relevant lines covered (61.96%)

32.39 hits per line

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

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

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

103
    /**
104
     * {@inheritdoc}
105
     */
106
    public function load(array $configs, ContainerBuilder $container): void
107
    {
108
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
144✔
109

110
        $configuration = new Configuration();
144✔
111
        $config = $this->processConfiguration($configuration, $configs);
144✔
112

113
        if (!$config['formats']) {
140✔
114
            trigger_deprecation('api-platform/core', '3.2', 'Setting the "formats" section will be mandatory in API Platform 4.');
×
115
            $config['formats'] = [
×
116
                'jsonld' => ['mime_types' => ['application/ld+json']],
×
117
                // 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
118
                'json' => ['mime_types' => ['application/json']], // Swagger support
×
119
            ];
×
120
        }
121

122
        $formats = $this->getFormats($config['formats']);
140✔
123
        $patchFormats = $this->getFormats($config['patch_formats']);
140✔
124
        $errorFormats = $this->getFormats($config['error_formats']);
140✔
125
        $docsFormats = $this->getFormats($config['docs_formats']);
140✔
126

127
        if (!isset($errorFormats['json'])) {
140✔
128
            $errorFormats['json'] = ['application/problem+json', 'application/json'];
140✔
129
        }
130

131
        if (!isset($errorFormats['jsonproblem'])) {
140✔
132
            $errorFormats['jsonproblem'] = ['application/problem+json'];
×
133
        }
134

135
        if ($this->isConfigEnabled($container, $config['graphql']) && !isset($formats['json'])) {
140✔
136
            trigger_deprecation('api-platform/core', '3.2', 'Add the "json" format to the configuration to use GraphQL.');
4✔
137
            $formats['json'] = ['application/json'];
4✔
138
        }
139

140
        // Backward Compatibility layer
141
        if (isset($formats['jsonapi']) && !isset($patchFormats['jsonapi'])) {
140✔
142
            $patchFormats['jsonapi'] = ['application/vnd.api+json'];
8✔
143
        }
144

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

150
        $this->registerCommonConfiguration($container, $config, $loader, $formats, $patchFormats, $errorFormats, $docsFormats);
140✔
151
        $this->registerMetadataConfiguration($container, $config, $loader);
140✔
152
        $this->registerOAuthConfiguration($container, $config);
140✔
153
        $this->registerOpenApiConfiguration($container, $config, $loader);
140✔
154
        $this->registerSwaggerConfiguration($container, $config, $loader);
140✔
155
        $this->registerJsonApiConfiguration($formats, $loader, $config);
140✔
156
        $this->registerJsonLdHydraConfiguration($container, $formats, $loader, $config);
140✔
157
        $this->registerJsonHalConfiguration($formats, $loader);
140✔
158
        $this->registerJsonProblemConfiguration($errorFormats, $loader);
140✔
159
        $this->registerGraphQlConfiguration($container, $config, $loader);
140✔
160
        $this->registerCacheConfiguration($container);
136✔
161
        $this->registerDoctrineOrmConfiguration($container, $config, $loader);
136✔
162
        $this->registerDoctrineMongoDbOdmConfiguration($container, $config, $loader);
136✔
163
        $this->registerHttpCacheConfiguration($container, $config, $loader);
136✔
164
        $this->registerValidatorConfiguration($container, $config, $loader);
136✔
165
        $this->registerDataCollectorConfiguration($container, $config, $loader);
136✔
166
        $this->registerMercureConfiguration($container, $config, $loader);
136✔
167
        $this->registerMessengerConfiguration($container, $config, $loader);
136✔
168
        $this->registerElasticsearchConfiguration($container, $config, $loader);
136✔
169
        $this->registerSecurityConfiguration($container, $config, $loader);
136✔
170
        $this->registerMakerConfiguration($container, $config, $loader);
136✔
171
        $this->registerArgumentResolverConfiguration($loader);
136✔
172
        $this->registerLinkSecurityConfiguration($loader, $config);
136✔
173

174
        $container->registerForAutoconfiguration(FilterInterface::class)
136✔
175
            ->addTag('api_platform.filter');
136✔
176
        $container->registerForAutoconfiguration(ProviderInterface::class)
136✔
177
            ->addTag('api_platform.state_provider');
136✔
178
        $container->registerForAutoconfiguration(ProcessorInterface::class)
136✔
179
            ->addTag('api_platform.state_processor');
136✔
180

181
        if (!$container->has('api_platform.state.item_provider')) {
136✔
182
            $container->setAlias('api_platform.state.item_provider', 'api_platform.state_provider.object');
×
183
        }
184

185
        $this->registerInflectorConfiguration($config);
136✔
186
    }
187

188
    private function registerCommonConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader, array $formats, array $patchFormats, array $errorFormats, array $docsFormats): void
189
    {
190
        $loader->load('state/state.xml');
140✔
191
        $loader->load('symfony/symfony.xml');
140✔
192
        $loader->load('api.xml');
140✔
193
        $loader->load('filter.xml');
140✔
194

195
        if (class_exists(Uuid::class)) {
140✔
196
            $loader->load('ramsey_uuid.xml');
140✔
197
        }
198

199
        if (class_exists(AbstractUid::class)) {
140✔
200
            $loader->load('symfony/uid.xml');
140✔
201
        }
202

203
        // TODO: remove in 4.x
204
        $container->setParameter('api_platform.event_listeners_backward_compatibility_layer', $config['event_listeners_backward_compatibility_layer']);
140✔
205
        $container->setParameter('api_platform.use_symfony_listeners', $config['use_symfony_listeners']);
140✔
206

207
        if ($config['event_listeners_backward_compatibility_layer']) {
140✔
208
            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(', ', [
137✔
209
                AddHeadersListener::class,
137✔
210
                AddTagsListener::class,
137✔
211
                AddLinkHeaderListener::class,
137✔
212
                HydraAddLinkHeaderListener::class,
137✔
213
                DenyAccessListener::class,
137✔
214
            ])));
137✔
215
        }
216

217
        if ($config['event_listeners_backward_compatibility_layer']) {
140✔
218
            $loader->load('legacy/events.xml');
137✔
219
        }
220

221
        if ($config['use_symfony_listeners']) {
140✔
NEW
222
            $loader->load('symfony/events.xml');
×
223
        } else {
224
            $loader->load('symfony/controller.xml');
140✔
225
            $loader->load('state/provider.xml');
140✔
226
            $loader->load('state/processor.xml');
140✔
227
        }
228

229
        $container->setParameter('api_platform.enable_entrypoint', $config['enable_entrypoint']);
140✔
230
        $container->setParameter('api_platform.enable_docs', $config['enable_docs']);
140✔
231
        $container->setParameter('api_platform.keep_legacy_inflector', $config['keep_legacy_inflector']);
140✔
232
        $container->setParameter('api_platform.title', $config['title']);
140✔
233
        $container->setParameter('api_platform.description', $config['description']);
140✔
234
        $container->setParameter('api_platform.version', $config['version']);
140✔
235
        $container->setParameter('api_platform.show_webby', $config['show_webby']);
140✔
236
        $container->setParameter('api_platform.url_generation_strategy', $config['defaults']['url_generation_strategy'] ?? UrlGeneratorInterface::ABS_PATH);
140✔
237
        $container->setParameter('api_platform.exception_to_status', $config['exception_to_status']);
140✔
238
        $container->setParameter('api_platform.formats', $formats);
140✔
239
        $container->setParameter('api_platform.patch_formats', $patchFormats);
140✔
240
        $container->setParameter('api_platform.error_formats', $errorFormats);
140✔
241
        $container->setParameter('api_platform.docs_formats', $docsFormats);
140✔
242
        $container->setParameter('api_platform.eager_loading.enabled', $this->isConfigEnabled($container, $config['eager_loading']));
140✔
243
        $container->setParameter('api_platform.eager_loading.max_joins', $config['eager_loading']['max_joins']);
140✔
244
        $container->setParameter('api_platform.eager_loading.fetch_partial', $config['eager_loading']['fetch_partial']);
140✔
245
        $container->setParameter('api_platform.eager_loading.force_eager', $config['eager_loading']['force_eager']);
140✔
246
        $container->setParameter('api_platform.collection.exists_parameter_name', $config['collection']['exists_parameter_name']);
140✔
247
        $container->setParameter('api_platform.collection.order', $config['collection']['order']);
140✔
248
        $container->setParameter('api_platform.collection.order_parameter_name', $config['collection']['order_parameter_name']);
140✔
249
        $container->setParameter('api_platform.collection.order_nulls_comparison', $config['collection']['order_nulls_comparison']);
140✔
250
        $container->setParameter('api_platform.collection.pagination.enabled', $config['defaults']['pagination_enabled'] ?? true);
140✔
251
        $container->setParameter('api_platform.collection.pagination.partial', $config['defaults']['pagination_partial'] ?? false);
140✔
252
        $container->setParameter('api_platform.collection.pagination.client_enabled', $config['defaults']['pagination_client_enabled'] ?? false);
140✔
253
        $container->setParameter('api_platform.collection.pagination.client_items_per_page', $config['defaults']['pagination_client_items_per_page'] ?? false);
140✔
254
        $container->setParameter('api_platform.collection.pagination.client_partial', $config['defaults']['pagination_client_partial'] ?? false);
140✔
255
        $container->setParameter('api_platform.collection.pagination.items_per_page', $config['defaults']['pagination_items_per_page'] ?? 30);
140✔
256
        $container->setParameter('api_platform.collection.pagination.maximum_items_per_page', $config['defaults']['pagination_maximum_items_per_page'] ?? null);
140✔
257
        $container->setParameter('api_platform.collection.pagination.page_parameter_name', $config['defaults']['pagination_page_parameter_name'] ?? $config['collection']['pagination']['page_parameter_name']);
140✔
258
        $container->setParameter('api_platform.collection.pagination.enabled_parameter_name', $config['defaults']['pagination_enabled_parameter_name'] ?? $config['collection']['pagination']['enabled_parameter_name']);
140✔
259
        $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']);
140✔
260
        $container->setParameter('api_platform.collection.pagination.partial_parameter_name', $config['defaults']['pagination_partial_parameter_name'] ?? $config['collection']['pagination']['partial_parameter_name']);
140✔
261
        $container->setParameter('api_platform.collection.pagination', $this->getPaginationDefaults($config['defaults'] ?? [], $config['collection']['pagination']));
140✔
262
        $container->setParameter('api_platform.handle_symfony_errors', $config['handle_symfony_errors'] ?? false);
140✔
263
        $container->setParameter('api_platform.http_cache.etag', $config['defaults']['cache_headers']['etag'] ?? true);
140✔
264
        $container->setParameter('api_platform.http_cache.max_age', $config['defaults']['cache_headers']['max_age'] ?? null);
140✔
265
        $container->setParameter('api_platform.http_cache.shared_max_age', $config['defaults']['cache_headers']['shared_max_age'] ?? null);
140✔
266
        $container->setParameter('api_platform.http_cache.vary', $config['defaults']['cache_headers']['vary'] ?? ['Accept']);
140✔
267
        $container->setParameter('api_platform.http_cache.public', $config['defaults']['cache_headers']['public'] ?? $config['http_cache']['public']);
140✔
268
        $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']);
140✔
269
        $container->setParameter('api_platform.http_cache.invalidation.xkey.glue', $config['defaults']['cache_headers']['invalidation']['xkey']['glue'] ?? $config['http_cache']['invalidation']['xkey']['glue']);
140✔
270

271
        $container->setAlias('api_platform.path_segment_name_generator', $config['path_segment_name_generator']);
140✔
272

273
        if ($config['name_converter']) {
140✔
274
            $container->setAlias('api_platform.name_converter', $config['name_converter']);
8✔
275
        }
276
        $container->setParameter('api_platform.asset_package', $config['asset_package']);
140✔
277
        $container->setParameter('api_platform.defaults', $this->normalizeDefaults($config['defaults'] ?? []));
140✔
278
        $container->setParameter('api_platform.rfc_7807_compliant_errors', $config['defaults']['extra_properties']['rfc_7807_compliant_errors'] ?? false);
140✔
279

280
        if ($container->getParameter('kernel.debug')) {
140✔
281
            $container->removeDefinition('api_platform.serializer.mapping.cache_class_metadata_factory');
16✔
282
        }
283
    }
284

285
    /**
286
     * This method will be removed in 3.0 when "defaults" will be the regular configuration path for the pagination.
287
     */
288
    private function getPaginationDefaults(array $defaults, array $collectionPaginationConfiguration): array
289
    {
290
        $paginationOptions = [];
140✔
291

292
        foreach ($defaults as $key => $value) {
140✔
293
            if (!str_starts_with($key, 'pagination_')) {
140✔
294
                continue;
140✔
295
            }
296

297
            $paginationOptions[str_replace('pagination_', '', $key)] = $value;
4✔
298
        }
299

300
        return array_merge($collectionPaginationConfiguration, $paginationOptions);
140✔
301
    }
302

303
    private function normalizeDefaults(array $defaults): array
304
    {
305
        $normalizedDefaults = ['extra_properties' => $defaults['extra_properties'] ?? []];
140✔
306
        unset($defaults['extra_properties']);
140✔
307

308
        $rc = new \ReflectionClass(ApiResource::class);
140✔
309
        $publicProperties = [];
140✔
310
        foreach ($rc->getConstructor()->getParameters() as $param) {
140✔
311
            $publicProperties[$param->getName()] = true;
140✔
312
        }
313

314
        $nameConverter = new CamelCaseToSnakeCaseNameConverter();
140✔
315
        foreach ($defaults as $option => $value) {
140✔
316
            if (isset($publicProperties[$nameConverter->denormalize($option)])) {
140✔
317
                $normalizedDefaults[$option] = $value;
132✔
318

319
                continue;
132✔
320
            }
321

322
            $normalizedDefaults['extra_properties'][$option] = $value;
8✔
323
        }
324

325
        return $normalizedDefaults;
140✔
326
    }
327

328
    private function registerMetadataConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
329
    {
330
        [$xmlResources, $yamlResources] = $this->getResourcesToWatch($container, $config);
140✔
331

332
        $container->setParameter('api_platform.class_name_resources', $this->getClassNameResources());
140✔
333

334
        $loader->load('metadata/resource_name.xml');
140✔
335
        $loader->load('metadata/property_name.xml');
140✔
336

337
        if (!empty($config['resource_class_directories'])) {
140✔
338
            $container->setParameter('api_platform.resource_class_directories', array_merge(
×
339
                $config['resource_class_directories'],
×
340
                $container->getParameter('api_platform.resource_class_directories')
×
341
            ));
×
342
        }
343

344
        // V3 metadata
345
        $loader->load('metadata/xml.xml');
140✔
346
        $loader->load('metadata/links.xml');
140✔
347
        $loader->load('metadata/property.xml');
140✔
348
        $loader->load('metadata/resource.xml');
140✔
349
        $loader->load('metadata/operation.xml');
140✔
350

351
        $container->getDefinition('api_platform.metadata.resource_extractor.xml')->replaceArgument(0, $xmlResources);
140✔
352
        $container->getDefinition('api_platform.metadata.property_extractor.xml')->replaceArgument(0, $xmlResources);
140✔
353

354
        if (class_exists(PhpDocParser::class) || interface_exists(DocBlockFactoryInterface::class)) {
140✔
355
            $loader->load('metadata/php_doc.xml');
140✔
356
        }
357

358
        if (class_exists(Yaml::class)) {
140✔
359
            $loader->load('metadata/yaml.xml');
140✔
360
            $container->getDefinition('api_platform.metadata.resource_extractor.yaml')->replaceArgument(0, $yamlResources);
140✔
361
            $container->getDefinition('api_platform.metadata.property_extractor.yaml')->replaceArgument(0, $yamlResources);
140✔
362
        }
363
    }
364

365
    private function getClassNameResources(): array
366
    {
367
        return [
140✔
368
            Error::class,
140✔
369
            ValidationException::class,
140✔
370
        ];
140✔
371
    }
372

373
    private function getBundlesResourcesPaths(ContainerBuilder $container, array $config): array
374
    {
375
        $bundlesResourcesPaths = [];
140✔
376

377
        foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) {
140✔
378
            $dirname = $bundle['path'];
140✔
379
            $paths = [
140✔
380
                "$dirname/ApiResource",
140✔
381
                "$dirname/src/ApiResource",
140✔
382
            ];
140✔
383
            foreach (['.yaml', '.yml', '.xml', ''] as $extension) {
140✔
384
                $paths[] = "$dirname/Resources/config/api_resources$extension";
140✔
385
                $paths[] = "$dirname/config/api_resources$extension";
140✔
386
            }
387
            if ($this->isConfigEnabled($container, $config['doctrine'])) {
140✔
388
                $paths[] = "$dirname/Entity";
140✔
389
                $paths[] = "$dirname/src/Entity";
140✔
390
            }
391
            if ($this->isConfigEnabled($container, $config['doctrine_mongodb_odm'])) {
140✔
392
                $paths[] = "$dirname/Document";
136✔
393
                $paths[] = "$dirname/src/Document";
136✔
394
            }
395

396
            foreach ($paths as $path) {
140✔
397
                if ($container->fileExists($path, false)) {
140✔
398
                    $bundlesResourcesPaths[] = $path;
140✔
399
                }
400
            }
401
        }
402

403
        return $bundlesResourcesPaths;
140✔
404
    }
405

406
    private function getResourcesToWatch(ContainerBuilder $container, array $config): array
407
    {
408
        $paths = array_unique(array_merge($this->getBundlesResourcesPaths($container, $config), $config['mapping']['paths']));
140✔
409

410
        if (!$config['mapping']['paths']) {
140✔
411
            $projectDir = $container->getParameter('kernel.project_dir');
136✔
412
            foreach (["$projectDir/config/api_platform", "$projectDir/src/ApiResource"] as $dir) {
136✔
413
                if (is_dir($dir)) {
136✔
414
                    $paths[] = $dir;
136✔
415
                }
416
            }
417

418
            if ($this->isConfigEnabled($container, $config['doctrine']) && is_dir($doctrinePath = "$projectDir/src/Entity")) {
136✔
419
                $paths[] = $doctrinePath;
×
420
            }
421

422
            if ($this->isConfigEnabled($container, $config['doctrine_mongodb_odm']) && is_dir($documentPath = "$projectDir/src/Document")) {
136✔
423
                $paths[] = $documentPath;
×
424
            }
425
        }
426

427
        $resources = ['yml' => [], 'xml' => [], 'dir' => []];
140✔
428

429
        foreach ($paths as $path) {
140✔
430
            if (is_dir($path)) {
140✔
431
                foreach (Finder::create()->followLinks()->files()->in($path)->name('/\.(xml|ya?ml)$/')->sortByName() as $file) {
140✔
432
                    $resources['yaml' === ($extension = $file->getExtension()) ? 'yml' : $extension][] = $file->getRealPath();
140✔
433
                }
434

435
                $resources['dir'][] = $path;
140✔
436
                $container->addResource(new DirectoryResource($path, '/\.(xml|ya?ml|php)$/'));
140✔
437

438
                continue;
140✔
439
            }
440

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

446
                $resources['yaml' === $matches[1] ? 'yml' : $matches[1]][] = $path;
×
447

448
                continue;
×
449
            }
450

451
            throw new RuntimeException(sprintf('Could not open file or directory "%s".', $path));
×
452
        }
453

454
        $container->setParameter('api_platform.resource_class_directories', $resources['dir']);
140✔
455

456
        return [$resources['xml'], $resources['yml']];
140✔
457
    }
458

459
    private function registerOAuthConfiguration(ContainerBuilder $container, array $config): void
460
    {
461
        if (!$config['oauth']) {
140✔
462
            return;
×
463
        }
464

465
        $container->setParameter('api_platform.oauth.enabled', $this->isConfigEnabled($container, $config['oauth']));
140✔
466
        $container->setParameter('api_platform.oauth.clientId', $config['oauth']['clientId']);
140✔
467
        $container->setParameter('api_platform.oauth.clientSecret', $config['oauth']['clientSecret']);
140✔
468
        $container->setParameter('api_platform.oauth.type', $config['oauth']['type']);
140✔
469
        $container->setParameter('api_platform.oauth.flow', $config['oauth']['flow']);
140✔
470
        $container->setParameter('api_platform.oauth.tokenUrl', $config['oauth']['tokenUrl']);
140✔
471
        $container->setParameter('api_platform.oauth.authorizationUrl', $config['oauth']['authorizationUrl']);
140✔
472
        $container->setParameter('api_platform.oauth.refreshUrl', $config['oauth']['refreshUrl']);
140✔
473
        $container->setParameter('api_platform.oauth.scopes', $config['oauth']['scopes']);
140✔
474
        $container->setParameter('api_platform.oauth.pkce', $config['oauth']['pkce']);
140✔
475

476
        if ($container->hasDefinition('api_platform.swagger_ui.action')) {
140✔
477
            $container->getDefinition('api_platform.swagger_ui.action')->setArgument(10, $config['oauth']['pkce']);
×
478
        }
479
    }
480

481
    /**
482
     * Registers the Swagger, ReDoc and Swagger UI configuration.
483
     */
484
    private function registerSwaggerConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
485
    {
486
        foreach (array_keys($config['swagger']['api_keys']) as $keyName) {
140✔
487
            if (!preg_match('/^[a-zA-Z0-9._-]+$/', $keyName)) {
4✔
488
                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));
×
489
            }
490
        }
491

492
        $container->setParameter('api_platform.swagger.versions', $config['swagger']['versions']);
140✔
493

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

498
        if (!$config['enable_swagger']) {
140✔
499
            return;
×
500
        }
501

502
        $loader->load('openapi.xml');
140✔
503
        $loader->load('swagger_ui.xml');
140✔
504

505
        if ($config['event_listeners_backward_compatibility_layer']) {
140✔
506
            $loader->load('legacy/swagger_ui.xml');
137✔
507
        }
508

509
        if ($config['use_symfony_listeners']) {
140✔
NEW
510
            $loader->load('symfony/swagger_ui.xml');
×
511
        }
512

513
        $loader->load('state/swagger_ui.xml');
140✔
514

515
        if (!$config['enable_swagger_ui'] && !$config['enable_re_doc']) {
140✔
516
            // Remove the listener but keep the controller to allow customizing the path of the UI
517
            $container->removeDefinition('api_platform.swagger.listener.ui');
×
518
        }
519

520
        $container->setParameter('api_platform.enable_swagger_ui', $config['enable_swagger_ui']);
140✔
521
        $container->setParameter('api_platform.enable_re_doc', $config['enable_re_doc']);
140✔
522
        $container->setParameter('api_platform.swagger.api_keys', $config['swagger']['api_keys']);
140✔
523
        if ($config['openapi']['swagger_ui_extra_configuration'] && $config['swagger']['swagger_ui_extra_configuration']) {
140✔
524
            throw new RuntimeException('You can not set "swagger_ui_extra_configuration" twice - in "openapi" and "swagger" section.');
×
525
        }
526
        $container->setParameter('api_platform.swagger_ui.extra_configuration', $config['openapi']['swagger_ui_extra_configuration'] ?: $config['swagger']['swagger_ui_extra_configuration']);
140✔
527
    }
528

529
    private function registerJsonApiConfiguration(array $formats, XmlFileLoader $loader, array $config): void
530
    {
531
        if (!isset($formats['jsonapi'])) {
140✔
532
            return;
132✔
533
        }
534

535
        if ($config['event_listeners_backward_compatibility_layer']) {
8✔
536
            $loader->load('legacy/jsonapi.xml');
5✔
537
        }
538

539
        $loader->load('jsonapi.xml');
8✔
540
        $loader->load('state/jsonapi.xml');
8✔
541
    }
542

543
    private function registerJsonLdHydraConfiguration(ContainerBuilder $container, array $formats, XmlFileLoader $loader, array $config): void
544
    {
545
        if (!isset($formats['jsonld'])) {
140✔
546
            return;
×
547
        }
548

549
        if ($config['use_symfony_listeners']) {
140✔
NEW
550
            $loader->load('symfony/jsonld.xml');
×
551
        } else {
552
            $loader->load('state/jsonld.xml');
140✔
553
        }
554

555
        if ($config['event_listeners_backward_compatibility_layer']) {
140✔
556
            $loader->load('legacy/hydra.xml');
137✔
557
        }
558

559
        $loader->load('state/hydra.xml');
140✔
560
        $loader->load('jsonld.xml');
140✔
561
        $loader->load('hydra.xml');
140✔
562

563
        if (!$container->has('api_platform.json_schema.schema_factory')) {
140✔
564
            $container->removeDefinition('api_platform.hydra.json_schema.schema_factory');
×
565
        }
566

567
        if (!$config['enable_docs']) {
140✔
568
            $container->removeDefinition('api_platform.hydra.listener.response.add_link_header');
×
569
            $container->removeDefinition('api_platform.hydra.processor.link');
×
570
        }
571
    }
572

573
    private function registerJsonHalConfiguration(array $formats, XmlFileLoader $loader): void
574
    {
575
        if (!isset($formats['jsonhal'])) {
140✔
576
            return;
×
577
        }
578

579
        $loader->load('hal.xml');
140✔
580
    }
581

582
    private function registerJsonProblemConfiguration(array $errorFormats, XmlFileLoader $loader): void
583
    {
584
        if (!isset($errorFormats['jsonproblem'])) {
140✔
585
            return;
×
586
        }
587

588
        $loader->load('problem.xml');
140✔
589
    }
590

591
    private function registerGraphQlConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
592
    {
593
        $enabled = $this->isConfigEnabled($container, $config['graphql']);
140✔
594

595
        $graphqlIntrospectionEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['introspection']);
140✔
596

597
        $graphiqlEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['graphiql']);
140✔
598
        $graphqlPlayGroundEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['graphql_playground']);
140✔
599
        if ($graphqlPlayGroundEnabled) {
140✔
600
            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.');
×
601
        }
602

603
        $container->setParameter('api_platform.graphql.enabled', $enabled);
140✔
604
        $container->setParameter('api_platform.graphql.introspection.enabled', $graphqlIntrospectionEnabled);
140✔
605
        $container->setParameter('api_platform.graphql.graphiql.enabled', $graphiqlEnabled);
140✔
606
        $container->setParameter('api_platform.graphql.graphql_playground.enabled', $graphqlPlayGroundEnabled);
140✔
607
        $container->setParameter('api_platform.graphql.collection.pagination', $config['graphql']['collection']['pagination']);
140✔
608

609
        if (!$enabled) {
140✔
610
            return;
×
611
        }
612

613
        $container->setParameter('api_platform.graphql.default_ide', $config['graphql']['default_ide']);
140✔
614
        $container->setParameter('api_platform.graphql.nesting_separator', $config['graphql']['nesting_separator']);
140✔
615

616
        $loader->load('graphql.xml');
140✔
617

618
        // @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
619
        if (!class_exists(Environment::class) || !isset($container->getParameter('kernel.bundles')['TwigBundle'])) {
140✔
620
            if ($graphiqlEnabled || $graphqlPlayGroundEnabled) {
8✔
621
                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✔
622
            }
623
            $container->removeDefinition('api_platform.graphql.action.graphiql');
4✔
624
            $container->removeDefinition('api_platform.graphql.action.graphql_playground');
4✔
625
        }
626

627
        $container->registerForAutoconfiguration(QueryItemResolverInterface::class)
136✔
628
            ->addTag('api_platform.graphql.resolver');
136✔
629
        $container->registerForAutoconfiguration(QueryCollectionResolverInterface::class)
136✔
630
            ->addTag('api_platform.graphql.resolver');
136✔
631
        $container->registerForAutoconfiguration(MutationResolverInterface::class)
136✔
632
            ->addTag('api_platform.graphql.resolver');
136✔
633
        $container->registerForAutoconfiguration(GraphQlTypeInterface::class)
136✔
634
            ->addTag('api_platform.graphql.type');
136✔
635
        $container->registerForAutoconfiguration(ErrorHandlerInterface::class)
136✔
636
            ->addTag('api_platform.graphql.error_handler');
136✔
637

638
        /* TODO: remove these in 4.x only one resolver factory is used and we're using providers/processors */
639
        if ($config['event_listeners_backward_compatibility_layer']) {
136✔
640
            // @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.');
641
            // + deprecate every service from legacy/graphql.xml
642
            $loader->load('legacy/graphql.xml');
133✔
643

644
            if (!$container->getParameter('kernel.debug')) {
133✔
645
                return;
120✔
646
            }
647

648
            $requestStack = new Reference('request_stack', ContainerInterface::NULL_ON_INVALID_REFERENCE);
13✔
649
            $collectionDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
13✔
650
                ->setDecoratedService('api_platform.graphql.resolver.factory.collection')
13✔
651
                ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.collection.inner'), $requestStack]);
13✔
652

653
            $itemDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
13✔
654
                ->setDecoratedService('api_platform.graphql.resolver.factory.item')
13✔
655
                ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.item.inner'), $requestStack]);
13✔
656

657
            $itemMutationDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
13✔
658
                ->setDecoratedService('api_platform.graphql.resolver.factory.item_mutation')
13✔
659
                ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.item_mutation.inner'), $requestStack]);
13✔
660

661
            $itemSubscriptionDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
13✔
662
                ->setDecoratedService('api_platform.graphql.resolver.factory.item_subscription')
13✔
663
                ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.item_subscription.inner'), $requestStack]);
13✔
664

665
            $container->addDefinitions([
13✔
666
                'api_platform.graphql.data_collector.resolver.factory.collection' => $collectionDataCollectorResolverFactory,
13✔
667
                'api_platform.graphql.data_collector.resolver.factory.item' => $itemDataCollectorResolverFactory,
13✔
668
                'api_platform.graphql.data_collector.resolver.factory.item_mutation' => $itemMutationDataCollectorResolverFactory,
13✔
669
                'api_platform.graphql.data_collector.resolver.factory.item_subscription' => $itemSubscriptionDataCollectorResolverFactory,
13✔
670
            ]);
13✔
671
        }
672
    }
673

674
    private function registerCacheConfiguration(ContainerBuilder $container): void
675
    {
676
        if (!$container->hasParameter('kernel.debug') || !$container->getParameter('kernel.debug')) {
136✔
677
            $container->removeDefinition('api_platform.cache_warmer.cache_pool_clearer');
120✔
678
        }
679
    }
680

681
    private function registerDoctrineOrmConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
682
    {
683
        if (!$this->isConfigEnabled($container, $config['doctrine'])) {
136✔
684
            return;
×
685
        }
686

687
        // For older versions of doctrine bridge this allows autoconfiguration for filters
688
        if (!$container->has(ManagerRegistry::class)) {
136✔
689
            $container->setAlias(ManagerRegistry::class, 'doctrine');
136✔
690
        }
691

692
        $container->registerForAutoconfiguration(QueryItemExtensionInterface::class)
136✔
693
            ->addTag('api_platform.doctrine.orm.query_extension.item');
136✔
694
        $container->registerForAutoconfiguration(DoctrineQueryCollectionExtensionInterface::class)
136✔
695
            ->addTag('api_platform.doctrine.orm.query_extension.collection');
136✔
696
        $container->registerForAutoconfiguration(DoctrineOrmAbstractFilter::class);
136✔
697

698
        $container->registerForAutoconfiguration(OrmLinksHandlerInterface::class)
136✔
699
            ->addTag('api_platform.doctrine.orm.links_handler');
136✔
700

701
        $loader->load('doctrine_orm.xml');
136✔
702

703
        if ($this->isConfigEnabled($container, $config['eager_loading'])) {
136✔
704
            return;
136✔
705
        }
706

707
        $container->removeAlias(EagerLoadingExtension::class);
×
708
        $container->removeDefinition('api_platform.doctrine.orm.query_extension.eager_loading');
×
709
        $container->removeAlias(FilterEagerLoadingExtension::class);
×
710
        $container->removeDefinition('api_platform.doctrine.orm.query_extension.filter_eager_loading');
×
711
    }
712

713
    private function registerDoctrineMongoDbOdmConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
714
    {
715
        if (!$this->isConfigEnabled($container, $config['doctrine_mongodb_odm'])) {
136✔
716
            return;
4✔
717
        }
718

719
        $container->registerForAutoconfiguration(AggregationItemExtensionInterface::class)
132✔
720
            ->addTag('api_platform.doctrine_mongodb.odm.aggregation_extension.item');
132✔
721
        $container->registerForAutoconfiguration(AggregationCollectionExtensionInterface::class)
132✔
722
            ->addTag('api_platform.doctrine_mongodb.odm.aggregation_extension.collection');
132✔
723
        $container->registerForAutoconfiguration(DoctrineMongoDbOdmAbstractFilter::class)
132✔
724
            ->setBindings(['$managerRegistry' => new Reference('doctrine_mongodb')]);
132✔
725
        $container->registerForAutoconfiguration(OdmLinksHandlerInterface::class)
132✔
726
            ->addTag('api_platform.doctrine.odm.links_handler');
132✔
727

728
        $loader->load('doctrine_mongodb_odm.xml');
132✔
729
    }
730

731
    private function registerHttpCacheConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
732
    {
733
        $loader->load('http_cache.xml');
136✔
734

735
        if ($config['event_listeners_backward_compatibility_layer']) {
136✔
736
            $loader->load('legacy/http_cache.xml');
133✔
737
        }
738

739
        if (!$this->isConfigEnabled($container, $config['http_cache']['invalidation'])) {
136✔
740
            return;
×
741
        }
742

743
        if ($this->isConfigEnabled($container, $config['doctrine'])) {
136✔
744
            $loader->load('doctrine_orm_http_cache_purger.xml');
136✔
745
        }
746

747
        if ($config['event_listeners_backward_compatibility_layer']) {
136✔
748
            $loader->load('legacy/http_cache_purger.xml');
133✔
749
        }
750

751
        $loader->load('state/http_cache_purger.xml');
136✔
752
        $loader->load('http_cache_purger.xml');
136✔
753

754
        foreach ($config['http_cache']['invalidation']['scoped_clients'] as $client) {
136✔
755
            $definition = $container->getDefinition($client);
4✔
756
            $definition->addTag('api_platform.http_cache.http_client');
4✔
757
        }
758

759
        if (!($urls = $config['http_cache']['invalidation']['urls'])) {
136✔
760
            $urls = $config['http_cache']['invalidation']['varnish_urls'];
132✔
761
        }
762

763
        foreach ($urls as $key => $url) {
136✔
764
            $definition = new Definition(ScopingHttpClient::class, [new Reference('http_client'), $url, ['base_uri' => $url] + $config['http_cache']['invalidation']['request_options']]);
8✔
765
            $definition->setFactory([ScopingHttpClient::class, 'forBaseUri']);
8✔
766
            $definition->addTag('api_platform.http_cache.http_client');
8✔
767
            $container->setDefinition('api_platform.invalidation_http_client.'.$key, $definition);
8✔
768
        }
769

770
        $serviceName = $config['http_cache']['invalidation']['purger'];
136✔
771

772
        if (!$container->hasDefinition('api_platform.http_cache.purger')) {
136✔
773
            $container->setAlias('api_platform.http_cache.purger', $serviceName);
136✔
774
        }
775
    }
776

777
    /**
778
     * Normalizes the format from config to the one accepted by Symfony HttpFoundation.
779
     */
780
    private function getFormats(array $configFormats): array
781
    {
782
        $formats = [];
140✔
783
        foreach ($configFormats as $format => $value) {
140✔
784
            foreach ($value['mime_types'] as $mimeType) {
140✔
785
                $formats[$format][] = $mimeType;
140✔
786
            }
787
        }
788

789
        return $formats;
140✔
790
    }
791

792
    private function registerValidatorConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
793
    {
794
        if (interface_exists(ValidatorInterface::class)) {
136✔
795
            $loader->load('metadata/validator.xml');
136✔
796
            $loader->load('validator/validator.xml');
136✔
797

798
            if ($this->isConfigEnabled($container, $config['graphql'])) {
136✔
799
                $loader->load('graphql/validator.xml');
136✔
800
            }
801

802
            if ($config['event_listeners_backward_compatibility_layer']) {
136✔
803
                $loader->load('legacy/validator.xml');
133✔
804
            }
805

806
            $loader->load($config['use_symfony_listeners'] ? 'validator/events.xml' : 'validator/state.xml');
136✔
807

808
            $container->registerForAutoconfiguration(ValidationGroupsGeneratorInterface::class)
136✔
809
                ->addTag('api_platform.validation_groups_generator');
136✔
810
            $container->registerForAutoconfiguration(PropertySchemaRestrictionMetadataInterface::class)
136✔
811
                ->addTag('api_platform.metadata.property_schema_restriction');
136✔
812
        }
813

814
        if (!$config['validator']) {
136✔
815
            return;
×
816
        }
817

818
        $container->setParameter('api_platform.validator.serialize_payload_fields', $config['validator']['serialize_payload_fields']);
136✔
819
        $container->setParameter('api_platform.validator.query_parameter_validation', $config['validator']['query_parameter_validation']);
136✔
820

821
        if (!$config['validator']['query_parameter_validation']) {
136✔
822
            $container->removeDefinition('api_platform.listener.view.validate_query_parameters');
×
823
            $container->removeDefinition('api_platform.validator.query_parameter_validator');
×
824
        }
825
    }
826

827
    private function registerDataCollectorConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
828
    {
829
        if (!$config['enable_profiler']) {
136✔
830
            return;
×
831
        }
832

833
        $loader->load('data_collector.xml');
136✔
834

835
        if ($container->hasParameter('kernel.debug') && $container->getParameter('kernel.debug')) {
136✔
836
            $loader->load('debug.xml');
16✔
837
        }
838
    }
839

840
    private function registerMercureConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
841
    {
842
        if (!$this->isConfigEnabled($container, $config['mercure'])) {
136✔
843
            return;
×
844
        }
845

846
        $container->setParameter('api_platform.mercure.include_type', $config['mercure']['include_type']);
136✔
847

848
        if ($config['event_listeners_backward_compatibility_layer']) {
136✔
849
            $loader->load('legacy/mercure.xml');
133✔
850
        }
851

852
        $loader->load('state/mercure.xml');
136✔
853

854
        if ($this->isConfigEnabled($container, $config['doctrine'])) {
136✔
855
            $loader->load('doctrine_orm_mercure_publisher.xml');
136✔
856
        }
857
        if ($this->isConfigEnabled($container, $config['doctrine_mongodb_odm'])) {
136✔
858
            $loader->load('doctrine_odm_mercure_publisher.xml');
132✔
859
        }
860

861
        if ($this->isConfigEnabled($container, $config['graphql'])) {
136✔
862
            $loader->load('graphql_mercure.xml');
136✔
863
        }
864
    }
865

866
    private function registerMessengerConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
867
    {
868
        if (!$this->isConfigEnabled($container, $config['messenger'])) {
136✔
869
            return;
×
870
        }
871

872
        $loader->load('messenger.xml');
136✔
873
    }
874

875
    private function registerElasticsearchConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
876
    {
877
        $enabled = $this->isConfigEnabled($container, $config['elasticsearch']);
136✔
878

879
        $container->setParameter('api_platform.elasticsearch.enabled', $enabled);
136✔
880

881
        if (!$enabled) {
136✔
882
            return;
132✔
883
        }
884

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

887
        $clientDefinition = new Definition($clientClass);
4✔
888
        $container->setDefinition('api_platform.elasticsearch.client', $clientDefinition);
4✔
889
        $container->registerForAutoconfiguration(RequestBodySearchCollectionExtensionInterface::class)
4✔
890
            ->addTag('api_platform.elasticsearch.request_body_search_extension.collection');
4✔
891
        $container->setParameter('api_platform.elasticsearch.hosts', $config['elasticsearch']['hosts']);
4✔
892
        $loader->load('elasticsearch.xml');
4✔
893

894
        // @phpstan-ignore-next-line
895
        if (\Elasticsearch\Client::class === $clientClass) {
4✔
896
            $loader->load('legacy/elasticsearch.xml');
×
897
            $container->setParameter('api_platform.elasticsearch.mapping', $config['elasticsearch']['mapping']);
×
898
            $container->setDefinition('api_platform.elasticsearch.client_for_metadata', $clientDefinition);
×
899
        }
900
    }
901

902
    private function registerSecurityConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
903
    {
904
        /** @var string[] $bundles */
905
        $bundles = $container->getParameter('kernel.bundles');
136✔
906

907
        if (!isset($bundles['SecurityBundle'])) {
136✔
908
            return;
×
909
        }
910

911
        $loader->load('security.xml');
136✔
912

913
        if ($config['event_listeners_backward_compatibility_layer']) {
136✔
914
            $loader->load('legacy/security.xml');
133✔
915
        }
916

917
        $loader->load('state/security.xml');
136✔
918

919
        if (interface_exists(ValidatorInterface::class) && !$config['use_symfony_listeners']) {
136✔
920
            $loader->load('state/security_validator.xml');
136✔
921
        }
922

923
        if ($this->isConfigEnabled($container, $config['graphql'])) {
136✔
924
            $loader->load('graphql/security.xml');
136✔
925
        }
926
    }
927

928
    private function registerOpenApiConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
929
    {
930
        $container->setParameter('api_platform.openapi.termsOfService', $config['openapi']['termsOfService']);
140✔
931
        $container->setParameter('api_platform.openapi.contact.name', $config['openapi']['contact']['name']);
140✔
932
        $container->setParameter('api_platform.openapi.contact.url', $config['openapi']['contact']['url']);
140✔
933
        $container->setParameter('api_platform.openapi.contact.email', $config['openapi']['contact']['email']);
140✔
934
        $container->setParameter('api_platform.openapi.license.name', $config['openapi']['license']['name']);
140✔
935
        $container->setParameter('api_platform.openapi.license.url', $config['openapi']['license']['url']);
140✔
936

937
        $loader->load('json_schema.xml');
140✔
938
    }
939

940
    private function registerMakerConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
941
    {
942
        if (!$this->isConfigEnabled($container, $config['maker'])) {
136✔
943
            return;
×
944
        }
945

946
        $loader->load('maker.xml');
136✔
947
    }
948

949
    private function registerArgumentResolverConfiguration(XmlFileLoader $loader): void
950
    {
951
        $loader->load('argument_resolver.xml');
136✔
952
    }
953

954
    private function registerInflectorConfiguration(array $config): void
955
    {
956
        if ($config['keep_legacy_inflector']) {
136✔
957
            Inflector::keepLegacyInflector(true);
×
958
            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.');
×
959
        } else {
960
            Inflector::keepLegacyInflector(false);
136✔
961
        }
962
    }
963

964
    private function registerLinkSecurityConfiguration(XmlFileLoader $loader, array $config): void
965
    {
966
        if ($config['enable_link_security']) {
136✔
967
            $loader->load('link_security.xml');
4✔
968
        }
969
    }
970
}
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