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

api-platform / core / 3713134090

pending completion
3713134090

Pull #5254

github

GitHub
Merge b2ec54b3c into ac711530f
Pull Request #5254: [OpenApi] Add ApiResource::openapi and deprecate openapiContext

197 of 197 new or added lines in 5 files covered. (100.0%)

10372 of 12438 relevant lines covered (83.39%)

11.97 hits per line

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

90.1
/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;
17
use ApiPlatform\Api\UrlGeneratorInterface;
18
use ApiPlatform\Doctrine\Odm\Extension\AggregationCollectionExtensionInterface;
19
use ApiPlatform\Doctrine\Odm\Extension\AggregationItemExtensionInterface;
20
use ApiPlatform\Doctrine\Odm\Filter\AbstractFilter as DoctrineMongoDbOdmAbstractFilter;
21
use ApiPlatform\Doctrine\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\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\Metadata\ApiResource;
33
use ApiPlatform\State\ProcessorInterface;
34
use ApiPlatform\State\ProviderInterface;
35
use ApiPlatform\Symfony\GraphQl\Resolver\Factory\DataCollectorResolverFactory;
36
use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaRestrictionMetadataInterface;
37
use ApiPlatform\Symfony\Validator\ValidationGroupsGeneratorInterface;
38
use Doctrine\Persistence\ManagerRegistry;
39
use phpDocumentor\Reflection\DocBlockFactoryInterface;
40
use PHPStan\PhpDocParser\Parser\PhpDocParser;
41
use Ramsey\Uuid\Uuid;
42
use Symfony\Component\Config\FileLocator;
43
use Symfony\Component\Config\Resource\DirectoryResource;
44
use Symfony\Component\DependencyInjection\ContainerBuilder;
45
use Symfony\Component\DependencyInjection\ContainerInterface;
46
use Symfony\Component\DependencyInjection\Definition;
47
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
48
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
49
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
50
use Symfony\Component\DependencyInjection\Reference;
51
use Symfony\Component\Finder\Finder;
52
use Symfony\Component\HttpClient\ScopingHttpClient;
53
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
54
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
55
use Symfony\Component\Uid\AbstractUid;
56
use Symfony\Component\Validator\Validator\ValidatorInterface;
57
use Symfony\Component\Yaml\Yaml;
58
use Twig\Environment;
59

60
/**
61
 * The extension of this bundle.
62
 *
63
 * @author Kévin Dunglas <dunglas@gmail.com>
64
 */
65
final class ApiPlatformExtension extends Extension implements PrependExtensionInterface
66
{
67
    /**
68
     * {@inheritdoc}
69
     */
70
    public function prepend(ContainerBuilder $container): void
71
    {
72
        if (isset($container->getExtensions()['framework'])) {
1✔
73
            $container->prependExtensionConfig('framework', [
1✔
74
                'serializer' => [
1✔
75
                    'enabled' => true,
1✔
76
                ],
1✔
77
            ]);
1✔
78
            $container->prependExtensionConfig('framework', [
1✔
79
                'property_info' => [
1✔
80
                    'enabled' => true,
1✔
81
                ],
1✔
82
            ]);
1✔
83
        }
84
    }
85

86
    /**
87
     * {@inheritdoc}
88
     */
89
    public function load(array $configs, ContainerBuilder $container): void
90
    {
91
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
29✔
92

93
        $configuration = new Configuration();
29✔
94
        $config = $this->processConfiguration($configuration, $configs);
29✔
95

96
        $formats = $this->getFormats($config['formats']);
29✔
97
        $patchFormats = $this->getFormats($config['patch_formats']);
29✔
98
        $errorFormats = $this->getFormats($config['error_formats']);
29✔
99

100
        // Backward Compatibility layer
101
        if (isset($formats['jsonapi']) && !isset($patchFormats['jsonapi'])) {
29✔
102
            $patchFormats['jsonapi'] = ['application/vnd.api+json'];
2✔
103
        }
104

105
        $this->registerCommonConfiguration($container, $config, $loader, $formats, $patchFormats, $errorFormats);
29✔
106
        $this->registerMetadataConfiguration($container, $config, $loader);
29✔
107
        $this->registerOAuthConfiguration($container, $config);
29✔
108
        $this->registerOpenApiConfiguration($container, $config, $loader);
29✔
109
        $this->registerSwaggerConfiguration($container, $config, $loader);
29✔
110
        $this->registerJsonApiConfiguration($formats, $loader);
29✔
111
        $this->registerJsonLdHydraConfiguration($container, $formats, $loader, $config);
29✔
112
        $this->registerJsonHalConfiguration($formats, $loader);
29✔
113
        $this->registerJsonProblemConfiguration($errorFormats, $loader);
29✔
114
        $this->registerGraphQlConfiguration($container, $config, $loader);
29✔
115
        $this->registerCacheConfiguration($container);
28✔
116
        $this->registerDoctrineOrmConfiguration($container, $config, $loader);
28✔
117
        $this->registerDoctrineMongoDbOdmConfiguration($container, $config, $loader);
28✔
118
        $this->registerHttpCacheConfiguration($container, $config, $loader);
28✔
119
        $this->registerValidatorConfiguration($container, $config, $loader);
28✔
120
        $this->registerDataCollectorConfiguration($container, $config, $loader);
28✔
121
        $this->registerMercureConfiguration($container, $config, $loader);
28✔
122
        $this->registerMessengerConfiguration($container, $config, $loader);
28✔
123
        $this->registerElasticsearchConfiguration($container, $config, $loader);
28✔
124
        $this->registerSecurityConfiguration($container, $loader);
28✔
125
        $this->registerMakerConfiguration($container, $config, $loader);
28✔
126
        $this->registerArgumentResolverConfiguration($loader);
28✔
127

128
        $container->registerForAutoconfiguration(FilterInterface::class)
28✔
129
            ->addTag('api_platform.filter');
28✔
130
        $container->registerForAutoconfiguration(ProviderInterface::class)
28✔
131
            ->addTag('api_platform.state_provider');
28✔
132
        $container->registerForAutoconfiguration(ProcessorInterface::class)
28✔
133
            ->addTag('api_platform.state_processor');
28✔
134

135
        if (!$container->has('api_platform.state.item_provider')) {
28✔
136
            $container->setAlias('api_platform.state.item_provider', 'api_platform.state_provider.object');
×
137
        }
138
    }
139

140
    private function registerCommonConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader, array $formats, array $patchFormats, array $errorFormats): void
141
    {
142
        $loader->load('symfony/events.xml');
29✔
143
        $loader->load('api.xml');
29✔
144
        $loader->load('state.xml');
29✔
145
        $loader->load('filter.xml');
29✔
146

147
        if (class_exists(Uuid::class)) {
29✔
148
            $loader->load('ramsey_uuid.xml');
29✔
149
        }
150

151
        if (class_exists(AbstractUid::class)) {
29✔
152
            $loader->load('symfony/uid.xml');
29✔
153
        }
154

155
        $container->setParameter('api_platform.enable_entrypoint', $config['enable_entrypoint']);
29✔
156
        $container->setParameter('api_platform.enable_docs', $config['enable_docs']);
29✔
157
        $container->setParameter('api_platform.title', $config['title']);
29✔
158
        $container->setParameter('api_platform.description', $config['description']);
29✔
159
        $container->setParameter('api_platform.version', $config['version']);
29✔
160
        $container->setParameter('api_platform.show_webby', $config['show_webby']);
29✔
161
        $container->setParameter('api_platform.url_generation_strategy', $config['defaults']['url_generation_strategy'] ?? UrlGeneratorInterface::ABS_PATH);
29✔
162
        $container->setParameter('api_platform.exception_to_status', $config['exception_to_status']);
29✔
163
        $container->setParameter('api_platform.formats', $formats);
29✔
164
        $container->setParameter('api_platform.patch_formats', $patchFormats);
29✔
165
        $container->setParameter('api_platform.error_formats', $errorFormats);
29✔
166
        $container->setParameter('api_platform.eager_loading.enabled', $this->isConfigEnabled($container, $config['eager_loading']));
29✔
167
        $container->setParameter('api_platform.eager_loading.max_joins', $config['eager_loading']['max_joins']);
29✔
168
        $container->setParameter('api_platform.eager_loading.fetch_partial', $config['eager_loading']['fetch_partial']);
29✔
169
        $container->setParameter('api_platform.eager_loading.force_eager', $config['eager_loading']['force_eager']);
29✔
170
        $container->setParameter('api_platform.collection.exists_parameter_name', $config['collection']['exists_parameter_name']);
29✔
171
        $container->setParameter('api_platform.collection.order', $config['collection']['order']);
29✔
172
        $container->setParameter('api_platform.collection.order_parameter_name', $config['collection']['order_parameter_name']);
29✔
173
        $container->setParameter('api_platform.collection.order_nulls_comparison', $config['collection']['order_nulls_comparison']);
29✔
174
        $container->setParameter('api_platform.collection.pagination.enabled', $config['defaults']['pagination_enabled'] ?? true);
29✔
175
        $container->setParameter('api_platform.collection.pagination.partial', $config['defaults']['pagination_partial'] ?? false);
29✔
176
        $container->setParameter('api_platform.collection.pagination.client_enabled', $config['defaults']['pagination_client_enabled'] ?? false);
29✔
177
        $container->setParameter('api_platform.collection.pagination.client_items_per_page', $config['defaults']['pagination_client_items_per_page'] ?? false);
29✔
178
        $container->setParameter('api_platform.collection.pagination.client_partial', $config['defaults']['pagination_client_partial'] ?? false);
29✔
179
        $container->setParameter('api_platform.collection.pagination.items_per_page', $config['defaults']['pagination_items_per_page'] ?? 30);
29✔
180
        $container->setParameter('api_platform.collection.pagination.maximum_items_per_page', $config['defaults']['pagination_maximum_items_per_page'] ?? null);
29✔
181
        $container->setParameter('api_platform.collection.pagination.page_parameter_name', $config['defaults']['pagination_page_parameter_name'] ?? $config['collection']['pagination']['page_parameter_name']);
29✔
182
        $container->setParameter('api_platform.collection.pagination.enabled_parameter_name', $config['defaults']['pagination_enabled_parameter_name'] ?? $config['collection']['pagination']['enabled_parameter_name']);
29✔
183
        $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']);
29✔
184
        $container->setParameter('api_platform.collection.pagination.partial_parameter_name', $config['defaults']['pagination_partial_parameter_name'] ?? $config['collection']['pagination']['partial_parameter_name']);
29✔
185
        $container->setParameter('api_platform.collection.pagination', $this->getPaginationDefaults($config['defaults'] ?? [], $config['collection']['pagination']));
29✔
186
        $container->setParameter('api_platform.http_cache.etag', $config['defaults']['cache_headers']['etag'] ?? true);
29✔
187
        $container->setParameter('api_platform.http_cache.max_age', $config['defaults']['cache_headers']['max_age'] ?? null);
29✔
188
        $container->setParameter('api_platform.http_cache.shared_max_age', $config['defaults']['cache_headers']['shared_max_age'] ?? null);
29✔
189
        $container->setParameter('api_platform.http_cache.vary', $config['defaults']['cache_headers']['vary'] ?? ['Accept']);
29✔
190
        $container->setParameter('api_platform.http_cache.public', $config['defaults']['cache_headers']['public'] ?? $config['http_cache']['public']);
29✔
191
        $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']);
29✔
192
        $container->setParameter('api_platform.http_cache.invalidation.xkey.glue', $config['defaults']['cache_headers']['invalidation']['xkey']['glue'] ?? $config['http_cache']['invalidation']['xkey']['glue']);
29✔
193

194
        $container->setAlias('api_platform.path_segment_name_generator', $config['path_segment_name_generator']);
29✔
195

196
        if ($config['name_converter']) {
29✔
197
            $container->setAlias('api_platform.name_converter', $config['name_converter']);
2✔
198
        }
199
        $container->setParameter('api_platform.asset_package', $config['asset_package']);
29✔
200
        $container->setParameter('api_platform.defaults', $this->normalizeDefaults($config['defaults'] ?? []));
29✔
201
    }
202

203
    /**
204
     * This method will be removed in 3.0 when "defaults" will be the regular configuration path for the pagination.
205
     */
206
    private function getPaginationDefaults(array $defaults, array $collectionPaginationConfiguration): array
207
    {
208
        $paginationOptions = [];
29✔
209

210
        foreach ($defaults as $key => $value) {
29✔
211
            if (!str_starts_with($key, 'pagination_')) {
29✔
212
                continue;
29✔
213
            }
214

215
            $paginationOptions[str_replace('pagination_', '', $key)] = $value;
1✔
216
        }
217

218
        return array_merge($collectionPaginationConfiguration, $paginationOptions);
29✔
219
    }
220

221
    private function normalizeDefaults(array $defaults): array
222
    {
223
        $normalizedDefaults = ['extra_properties' => $defaults['extra_properties'] ?? []];
29✔
224
        unset($defaults['extra_properties']);
29✔
225

226
        $rc = new \ReflectionClass(ApiResource::class);
29✔
227
        $publicProperties = [];
29✔
228
        foreach ($rc->getConstructor()->getParameters() as $param) {
29✔
229
            $publicProperties[$param->getName()] = true;
29✔
230
        }
231

232
        $nameConverter = new CamelCaseToSnakeCaseNameConverter();
29✔
233
        foreach ($defaults as $option => $value) {
29✔
234
            if (isset($publicProperties[$nameConverter->denormalize($option)])) {
29✔
235
                $normalizedDefaults[$option] = $value;
27✔
236

237
                continue;
27✔
238
            }
239

240
            $normalizedDefaults['extra_properties'][$option] = $value;
2✔
241
        }
242

243
        return $normalizedDefaults;
29✔
244
    }
245

246
    private function registerMetadataConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
247
    {
248
        [$xmlResources, $yamlResources] = $this->getResourcesToWatch($container, $config);
29✔
249

250
        $loader->load('metadata/resource_name.xml');
29✔
251
        $loader->load('metadata/property_name.xml');
29✔
252

253
        if (!empty($config['resource_class_directories'])) {
29✔
254
            $container->setParameter('api_platform.resource_class_directories', array_merge(
×
255
                $config['resource_class_directories'], $container->getParameter('api_platform.resource_class_directories')
×
256
            ));
×
257
        }
258

259
        // V3 metadata
260
        $loader->load('metadata/xml.xml');
29✔
261
        $loader->load('metadata/links.xml');
29✔
262
        $loader->load('metadata/property.xml');
29✔
263
        $loader->load('metadata/resource.xml');
29✔
264
        $loader->load('metadata/operation.xml');
29✔
265

266
        $container->getDefinition('api_platform.metadata.resource_extractor.xml')->replaceArgument(0, $xmlResources);
29✔
267
        $container->getDefinition('api_platform.metadata.property_extractor.xml')->replaceArgument(0, $xmlResources);
29✔
268

269
        if (class_exists(PhpDocParser::class) || interface_exists(DocBlockFactoryInterface::class)) {
29✔
270
            $loader->load('metadata/php_doc.xml');
29✔
271
        }
272

273
        if (class_exists(Yaml::class)) {
29✔
274
            $loader->load('metadata/yaml.xml');
29✔
275
            $container->getDefinition('api_platform.metadata.resource_extractor.yaml')->replaceArgument(0, $yamlResources);
29✔
276
            $container->getDefinition('api_platform.metadata.property_extractor.yaml')->replaceArgument(0, $yamlResources);
29✔
277
        }
278
    }
279

280
    private function getBundlesResourcesPaths(ContainerBuilder $container, array $config): array
281
    {
282
        $bundlesResourcesPaths = [];
29✔
283

284
        foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) {
29✔
285
            $dirname = $bundle['path'];
29✔
286
            $paths = [
29✔
287
                "$dirname/ApiResource",
29✔
288
                "$dirname/src/ApiResource",
29✔
289
            ];
29✔
290
            foreach (['.yaml', '.yml', '.xml', ''] as $extension) {
29✔
291
                $paths[] = "$dirname/Resources/config/api_resources$extension";
29✔
292
                $paths[] = "$dirname/config/api_resources$extension";
29✔
293
            }
294
            if ($this->isConfigEnabled($container, $config['doctrine'])) {
29✔
295
                $paths[] = "$dirname/Entity";
29✔
296
                $paths[] = "$dirname/src/Entity";
29✔
297
            }
298
            if ($this->isConfigEnabled($container, $config['doctrine_mongodb_odm'])) {
29✔
299
                $paths[] = "$dirname/Document";
28✔
300
                $paths[] = "$dirname/src/Document";
28✔
301
            }
302

303
            foreach ($paths as $path) {
29✔
304
                if ($container->fileExists($path, false)) {
29✔
305
                    $bundlesResourcesPaths[] = $path;
29✔
306
                }
307
            }
308
        }
309

310
        return $bundlesResourcesPaths;
29✔
311
    }
312

313
    private function getResourcesToWatch(ContainerBuilder $container, array $config): array
314
    {
315
        $paths = array_unique(array_merge($this->getBundlesResourcesPaths($container, $config), $config['mapping']['paths']));
29✔
316

317
        if (!$config['mapping']['paths']) {
29✔
318
            $projectDir = $container->getParameter('kernel.project_dir');
28✔
319
            foreach (["$projectDir/config/api_platform", "$projectDir/src/ApiResource"] as $dir) {
28✔
320
                if (is_dir($dir)) {
28✔
321
                    $paths[] = $dir;
28✔
322
                }
323
            }
324

325
            if ($this->isConfigEnabled($container, $config['doctrine']) && is_dir($doctrinePath = "$projectDir/src/Entity")) {
28✔
326
                $paths[] = $doctrinePath;
×
327
            }
328

329
            if ($this->isConfigEnabled($container, $config['doctrine_mongodb_odm']) && is_dir($documentPath = "$projectDir/src/Document")) {
28✔
330
                $paths[] = $documentPath;
×
331
            }
332
        }
333

334
        $resources = ['yml' => [], 'xml' => [], 'dir' => []];
29✔
335

336
        foreach ($paths as $path) {
29✔
337
            if (is_dir($path)) {
29✔
338
                foreach (Finder::create()->followLinks()->files()->in($path)->name('/\.(xml|ya?ml)$/')->sortByName() as $file) {
29✔
339
                    $resources['yaml' === ($extension = $file->getExtension()) ? 'yml' : $extension][] = $file->getRealPath();
29✔
340
                }
341

342
                $resources['dir'][] = $path;
29✔
343
                $container->addResource(new DirectoryResource($path, '/\.(xml|ya?ml|php)$/'));
29✔
344

345
                continue;
29✔
346
            }
347

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

353
                $resources['yaml' === $matches[1] ? 'yml' : $matches[1]][] = $path;
×
354

355
                continue;
×
356
            }
357

358
            throw new RuntimeException(sprintf('Could not open file or directory "%s".', $path));
×
359
        }
360

361
        $container->setParameter('api_platform.resource_class_directories', $resources['dir']);
29✔
362

363
        return [$resources['xml'], $resources['yml']];
29✔
364
    }
365

366
    private function registerOAuthConfiguration(ContainerBuilder $container, array $config): void
367
    {
368
        if (!$config['oauth']) {
29✔
369
            return;
×
370
        }
371

372
        $container->setParameter('api_platform.oauth.enabled', $this->isConfigEnabled($container, $config['oauth']));
29✔
373
        $container->setParameter('api_platform.oauth.clientId', $config['oauth']['clientId']);
29✔
374
        $container->setParameter('api_platform.oauth.clientSecret', $config['oauth']['clientSecret']);
29✔
375
        $container->setParameter('api_platform.oauth.type', $config['oauth']['type']);
29✔
376
        $container->setParameter('api_platform.oauth.flow', $config['oauth']['flow']);
29✔
377
        $container->setParameter('api_platform.oauth.tokenUrl', $config['oauth']['tokenUrl']);
29✔
378
        $container->setParameter('api_platform.oauth.authorizationUrl', $config['oauth']['authorizationUrl']);
29✔
379
        $container->setParameter('api_platform.oauth.refreshUrl', $config['oauth']['refreshUrl']);
29✔
380
        $container->setParameter('api_platform.oauth.scopes', $config['oauth']['scopes']);
29✔
381
        $container->setParameter('api_platform.oauth.pkce', $config['oauth']['pkce']);
29✔
382

383
        if ($container->hasDefinition('api_platform.swagger_ui.action')) {
29✔
384
            $container->getDefinition('api_platform.swagger_ui.action')->setArgument(10, $config['oauth']['pkce']);
×
385
        }
386
    }
387

388
    /**
389
     * Registers the Swagger, ReDoc and Swagger UI configuration.
390
     */
391
    private function registerSwaggerConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
392
    {
393
        $container->setParameter('api_platform.swagger.versions', $config['swagger']['versions']);
29✔
394

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

399
        if (!$config['enable_swagger']) {
29✔
400
            return;
×
401
        }
402

403
        $loader->load('openapi.xml');
29✔
404
        $loader->load('swagger_ui.xml');
29✔
405

406
        if (!$config['enable_swagger_ui'] && !$config['enable_re_doc']) {
29✔
407
            // Remove the listener but keep the controller to allow customizing the path of the UI
408
            $container->removeDefinition('api_platform.swagger.listener.ui');
×
409
        }
410

411
        $container->setParameter('api_platform.enable_swagger_ui', $config['enable_swagger_ui']);
29✔
412
        $container->setParameter('api_platform.enable_re_doc', $config['enable_re_doc']);
29✔
413
        $container->setParameter('api_platform.swagger.api_keys', $config['swagger']['api_keys']);
29✔
414
        if ($config['openapi']['swagger_ui_extra_configuration'] && $config['swagger']['swagger_ui_extra_configuration']) {
29✔
415
            throw new RuntimeException('You can not set "swagger_ui_extra_configuration" twice - in "openapi" and "swagger" section.');
×
416
        }
417
        $container->setParameter('api_platform.swagger_ui.extra_configuration', $config['openapi']['swagger_ui_extra_configuration'] ?: $config['swagger']['swagger_ui_extra_configuration']);
29✔
418
    }
419

420
    private function registerJsonApiConfiguration(array $formats, XmlFileLoader $loader): void
421
    {
422
        if (!isset($formats['jsonapi'])) {
29✔
423
            return;
27✔
424
        }
425

426
        $loader->load('jsonapi.xml');
2✔
427
    }
428

429
    private function registerJsonLdHydraConfiguration(ContainerBuilder $container, array $formats, XmlFileLoader $loader, array $config): void
430
    {
431
        if (!isset($formats['jsonld'])) {
29✔
432
            return;
×
433
        }
434

435
        $loader->load('jsonld.xml');
29✔
436
        $loader->load('hydra.xml');
29✔
437

438
        if (!$container->has('api_platform.json_schema.schema_factory')) {
29✔
439
            $container->removeDefinition('api_platform.hydra.json_schema.schema_factory');
×
440
        }
441

442
        if (!$config['enable_docs']) {
29✔
443
            $container->removeDefinition('api_platform.hydra.listener.response.add_link_header');
×
444
        }
445
    }
446

447
    private function registerJsonHalConfiguration(array $formats, XmlFileLoader $loader): void
448
    {
449
        if (!isset($formats['jsonhal'])) {
29✔
450
            return;
×
451
        }
452

453
        $loader->load('hal.xml');
29✔
454
    }
455

456
    private function registerJsonProblemConfiguration(array $errorFormats, XmlFileLoader $loader): void
457
    {
458
        if (!isset($errorFormats['jsonproblem'])) {
29✔
459
            return;
×
460
        }
461

462
        $loader->load('problem.xml');
29✔
463
    }
464

465
    private function registerGraphQlConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
466
    {
467
        $enabled = $this->isConfigEnabled($container, $config['graphql']);
29✔
468

469
        $graphiqlEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['graphiql']);
29✔
470
        $graphqlPlayGroundEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['graphql_playground']);
29✔
471

472
        $container->setParameter('api_platform.graphql.enabled', $enabled);
29✔
473
        $container->setParameter('api_platform.graphql.graphiql.enabled', $graphiqlEnabled);
29✔
474
        $container->setParameter('api_platform.graphql.graphql_playground.enabled', $graphqlPlayGroundEnabled);
29✔
475
        $container->setParameter('api_platform.graphql.collection.pagination', $config['graphql']['collection']['pagination']);
29✔
476

477
        if (!$enabled) {
29✔
478
            return;
×
479
        }
480

481
        $container->setParameter('api_platform.graphql.default_ide', $config['graphql']['default_ide']);
29✔
482
        $container->setParameter('api_platform.graphql.nesting_separator', $config['graphql']['nesting_separator']);
29✔
483

484
        $loader->load('graphql.xml');
29✔
485

486
        // @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
487
        if (!class_exists(Environment::class) || !isset($container->getParameter('kernel.bundles')['TwigBundle'])) {
29✔
488
            if ($graphiqlEnabled || $graphqlPlayGroundEnabled) {
2✔
489
                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')));
1✔
490
            }
491
            $container->removeDefinition('api_platform.graphql.action.graphiql');
1✔
492
            $container->removeDefinition('api_platform.graphql.action.graphql_playground');
1✔
493
        }
494

495
        $container->registerForAutoconfiguration(QueryItemResolverInterface::class)
28✔
496
            ->addTag('api_platform.graphql.query_resolver');
28✔
497
        $container->registerForAutoconfiguration(QueryCollectionResolverInterface::class)
28✔
498
            ->addTag('api_platform.graphql.query_resolver');
28✔
499
        $container->registerForAutoconfiguration(MutationResolverInterface::class)
28✔
500
            ->addTag('api_platform.graphql.mutation_resolver');
28✔
501
        $container->registerForAutoconfiguration(GraphQlTypeInterface::class)
28✔
502
            ->addTag('api_platform.graphql.type');
28✔
503
        $container->registerForAutoconfiguration(ErrorHandlerInterface::class)
28✔
504
            ->addTag('api_platform.graphql.error_handler');
28✔
505

506
        if (!$container->getParameter('kernel.debug')) {
28✔
507
            return;
25✔
508
        }
509

510
        $requestStack = new Reference('request_stack', ContainerInterface::NULL_ON_INVALID_REFERENCE);
3✔
511
        $collectionDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
3✔
512
            ->setDecoratedService('api_platform.graphql.resolver.factory.collection')
3✔
513
            ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.collection.inner'), $requestStack]);
3✔
514

515
        $itemDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
3✔
516
            ->setDecoratedService('api_platform.graphql.resolver.factory.item')
3✔
517
            ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.item.inner'), $requestStack]);
3✔
518

519
        $itemMutationDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
3✔
520
            ->setDecoratedService('api_platform.graphql.resolver.factory.item_mutation')
3✔
521
            ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.item_mutation.inner'), $requestStack]);
3✔
522

523
        $itemSubscriptionDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
3✔
524
            ->setDecoratedService('api_platform.graphql.resolver.factory.item_subscription')
3✔
525
            ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.item_subscription.inner'), $requestStack]);
3✔
526

527
        $container->addDefinitions([
3✔
528
            'api_platform.graphql.data_collector.resolver.factory.collection' => $collectionDataCollectorResolverFactory,
3✔
529
            'api_platform.graphql.data_collector.resolver.factory.item' => $itemDataCollectorResolverFactory,
3✔
530
            'api_platform.graphql.data_collector.resolver.factory.item_mutation' => $itemMutationDataCollectorResolverFactory,
3✔
531
            'api_platform.graphql.data_collector.resolver.factory.item_subscription' => $itemSubscriptionDataCollectorResolverFactory,
3✔
532
        ]);
3✔
533
    }
534

535
    private function registerCacheConfiguration(ContainerBuilder $container): void
536
    {
537
        if (!$container->hasParameter('kernel.debug') || !$container->getParameter('kernel.debug')) {
28✔
538
            $container->removeDefinition('api_platform.cache_warmer.cache_pool_clearer');
25✔
539
        }
540
    }
541

542
    private function registerDoctrineOrmConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
543
    {
544
        if (!$this->isConfigEnabled($container, $config['doctrine'])) {
28✔
545
            return;
×
546
        }
547

548
        // For older versions of doctrine bridge this allows autoconfiguration for filters
549
        if (!$container->has(ManagerRegistry::class)) {
28✔
550
            $container->setAlias(ManagerRegistry::class, 'doctrine');
28✔
551
        }
552

553
        $container->registerForAutoconfiguration(QueryItemExtensionInterface::class)
28✔
554
            ->addTag('api_platform.doctrine.orm.query_extension.item');
28✔
555
        $container->registerForAutoconfiguration(DoctrineQueryCollectionExtensionInterface::class)
28✔
556
            ->addTag('api_platform.doctrine.orm.query_extension.collection');
28✔
557
        $container->registerForAutoconfiguration(DoctrineOrmAbstractFilter::class);
28✔
558

559
        $loader->load('doctrine_orm.xml');
28✔
560

561
        if ($this->isConfigEnabled($container, $config['eager_loading'])) {
28✔
562
            return;
28✔
563
        }
564

565
        $container->removeAlias(EagerLoadingExtension::class);
×
566
        $container->removeDefinition('api_platform.doctrine.orm.query_extension.eager_loading');
×
567
        $container->removeAlias(FilterEagerLoadingExtension::class);
×
568
        $container->removeDefinition('api_platform.doctrine.orm.query_extension.filter_eager_loading');
×
569
    }
570

571
    private function registerDoctrineMongoDbOdmConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
572
    {
573
        if (!$this->isConfigEnabled($container, $config['doctrine_mongodb_odm'])) {
28✔
574
            return;
1✔
575
        }
576

577
        $container->registerForAutoconfiguration(AggregationItemExtensionInterface::class)
27✔
578
            ->addTag('api_platform.doctrine_mongodb.odm.aggregation_extension.item');
27✔
579
        $container->registerForAutoconfiguration(AggregationCollectionExtensionInterface::class)
27✔
580
            ->addTag('api_platform.doctrine_mongodb.odm.aggregation_extension.collection');
27✔
581
        $container->registerForAutoconfiguration(DoctrineMongoDbOdmAbstractFilter::class)
27✔
582
            ->setBindings(['$managerRegistry' => new Reference('doctrine_mongodb')]);
27✔
583

584
        $loader->load('doctrine_mongodb_odm.xml');
27✔
585
    }
586

587
    private function registerHttpCacheConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
588
    {
589
        $loader->load('http_cache.xml');
28✔
590

591
        if (!$this->isConfigEnabled($container, $config['http_cache']['invalidation'])) {
28✔
592
            return;
×
593
        }
594

595
        if ($this->isConfigEnabled($container, $config['doctrine'])) {
28✔
596
            $loader->load('doctrine_orm_http_cache_purger.xml');
28✔
597
        }
598

599
        $loader->load('http_cache_purger.xml');
28✔
600

601
        $definitions = [];
28✔
602
        foreach ($config['http_cache']['invalidation']['varnish_urls'] as $key => $url) {
28✔
603
            $definition = new Definition(ScopingHttpClient::class, [new Reference('http_client'), $url, ['base_uri' => $url] + $config['http_cache']['invalidation']['request_options']]);
27✔
604
            $definition->setFactory([ScopingHttpClient::class, 'forBaseUri']);
27✔
605

606
            $definitions[] = $definition;
27✔
607
        }
608

609
        foreach (['api_platform.http_cache.purger.varnish.ban', 'api_platform.http_cache.purger.varnish.xkey'] as $serviceName) {
28✔
610
            $container->findDefinition($serviceName)->setArguments([
28✔
611
                $definitions,
28✔
612
                $config['http_cache']['invalidation']['max_header_length'],
28✔
613
            ]);
28✔
614
        }
615

616
        $serviceName = $config['http_cache']['invalidation']['purger'];
28✔
617
        $container->setAlias('api_platform.http_cache.purger', $serviceName);
28✔
618
    }
619

620
    /**
621
     * Normalizes the format from config to the one accepted by Symfony HttpFoundation.
622
     */
623
    private function getFormats(array $configFormats): array
624
    {
625
        $formats = [];
29✔
626
        foreach ($configFormats as $format => $value) {
29✔
627
            foreach ($value['mime_types'] as $mimeType) {
29✔
628
                $formats[$format][] = $mimeType;
29✔
629
            }
630
        }
631

632
        return $formats;
29✔
633
    }
634

635
    private function registerValidatorConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
636
    {
637
        if (interface_exists(ValidatorInterface::class)) {
28✔
638
            $loader->load('metadata/validator.xml');
28✔
639
            $loader->load('symfony/validator.xml');
28✔
640

641
            $container->registerForAutoconfiguration(ValidationGroupsGeneratorInterface::class)
28✔
642
                ->addTag('api_platform.validation_groups_generator');
28✔
643
            $container->registerForAutoconfiguration(PropertySchemaRestrictionMetadataInterface::class)
28✔
644
                ->addTag('api_platform.metadata.property_schema_restriction');
28✔
645
        }
646

647
        if (!$config['validator']) {
28✔
648
            return;
×
649
        }
650

651
        $container->setParameter('api_platform.validator.serialize_payload_fields', $config['validator']['serialize_payload_fields']);
28✔
652
        $container->setParameter('api_platform.validator.query_parameter_validation', $config['validator']['query_parameter_validation']);
28✔
653

654
        if (!$config['validator']['query_parameter_validation']) {
28✔
655
            $container->removeDefinition('api_platform.listener.view.validate_query_parameters');
×
656
            $container->removeDefinition('api_platform.validator.query_parameter_validator');
×
657
        }
658
    }
659

660
    private function registerDataCollectorConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
661
    {
662
        if (!$config['enable_profiler']) {
28✔
663
            return;
×
664
        }
665

666
        $loader->load('data_collector.xml');
28✔
667

668
        if ($container->hasParameter('kernel.debug') && $container->getParameter('kernel.debug')) {
28✔
669
            $loader->load('debug.xml');
3✔
670
        }
671
    }
672

673
    private function registerMercureConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
674
    {
675
        if (!$this->isConfigEnabled($container, $config['mercure'])) {
28✔
676
            return;
×
677
        }
678

679
        $loader->load('mercure.xml');
28✔
680

681
        if ($this->isConfigEnabled($container, $config['doctrine'])) {
28✔
682
            $loader->load('doctrine_orm_mercure_publisher.xml');
28✔
683
        }
684
        if ($this->isConfigEnabled($container, $config['doctrine_mongodb_odm'])) {
28✔
685
            $loader->load('doctrine_odm_mercure_publisher.xml');
27✔
686
        }
687

688
        if ($this->isConfigEnabled($container, $config['graphql'])) {
28✔
689
            $loader->load('graphql_mercure.xml');
28✔
690
        }
691
    }
692

693
    private function registerMessengerConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
694
    {
695
        if (!$this->isConfigEnabled($container, $config['messenger'])) {
28✔
696
            return;
×
697
        }
698

699
        $loader->load('messenger.xml');
28✔
700
    }
701

702
    private function registerElasticsearchConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
703
    {
704
        $enabled = $this->isConfigEnabled($container, $config['elasticsearch']);
28✔
705

706
        $container->setParameter('api_platform.elasticsearch.enabled', $enabled);
28✔
707

708
        if (!$enabled) {
28✔
709
            return;
27✔
710
        }
711

712
        $loader->load('elasticsearch.xml');
1✔
713

714
        $container->registerForAutoconfiguration(RequestBodySearchCollectionExtensionInterface::class)
1✔
715
            ->addTag('api_platform.elasticsearch.request_body_search_extension.collection');
1✔
716

717
        $container->setParameter('api_platform.elasticsearch.hosts', $config['elasticsearch']['hosts']);
1✔
718
        $container->setParameter('api_platform.elasticsearch.mapping', $config['elasticsearch']['mapping']);
1✔
719
    }
720

721
    private function registerSecurityConfiguration(ContainerBuilder $container, XmlFileLoader $loader): void
722
    {
723
        /** @var string[] $bundles */
724
        $bundles = $container->getParameter('kernel.bundles');
28✔
725

726
        if (!isset($bundles['SecurityBundle'])) {
28✔
727
            return;
×
728
        }
729

730
        $loader->load('security.xml');
28✔
731
    }
732

733
    private function registerOpenApiConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
734
    {
735
        $container->setParameter('api_platform.openapi.termsOfService', $config['openapi']['termsOfService']);
29✔
736
        $container->setParameter('api_platform.openapi.contact.name', $config['openapi']['contact']['name']);
29✔
737
        $container->setParameter('api_platform.openapi.contact.url', $config['openapi']['contact']['url']);
29✔
738
        $container->setParameter('api_platform.openapi.contact.email', $config['openapi']['contact']['email']);
29✔
739
        $container->setParameter('api_platform.openapi.license.name', $config['openapi']['license']['name']);
29✔
740
        $container->setParameter('api_platform.openapi.license.url', $config['openapi']['license']['url']);
29✔
741

742
        $loader->load('json_schema.xml');
29✔
743
    }
744

745
    private function registerMakerConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader): void
746
    {
747
        if (!$this->isConfigEnabled($container, $config['maker'])) {
28✔
748
            return;
×
749
        }
750

751
        $loader->load('maker.xml');
28✔
752
    }
753

754
    private function registerArgumentResolverConfiguration(XmlFileLoader $loader): void
755
    {
756
        $loader->load('argument_resolver.xml');
28✔
757
    }
758
}
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