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

api-platform / core / 20790770148

07 Jan 2026 05:41PM UTC coverage: 28.879% (-0.005%) from 28.884%
20790770148

Pull #7653

github

web-flow
Merge f1f04f778 into 582508eee
Pull Request #7653: Fix PartialSearchFilter for values with `%` or `_`

6 of 6 new or added lines in 1 file covered. (100.0%)

209 existing lines in 17 files now uncovered.

16828 of 58270 relevant lines covered (28.88%)

78.89 hits per line

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

77.12
/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\Executor;
29
use ApiPlatform\GraphQl\Resolver\MutationResolverInterface;
30
use ApiPlatform\GraphQl\Resolver\QueryCollectionResolverInterface;
31
use ApiPlatform\GraphQl\Resolver\QueryItemResolverInterface;
32
use ApiPlatform\GraphQl\Type\Definition\TypeInterface as GraphQlTypeInterface;
33
use ApiPlatform\Metadata\ApiResource;
34
use ApiPlatform\Metadata\AsOperationMutator;
35
use ApiPlatform\Metadata\AsResourceMutator;
36
use ApiPlatform\Metadata\FilterInterface;
37
use ApiPlatform\Metadata\OperationMutatorInterface;
38
use ApiPlatform\Metadata\ResourceMutatorInterface;
39
use ApiPlatform\Metadata\UriVariableTransformerInterface;
40
use ApiPlatform\Metadata\UrlGeneratorInterface;
41
use ApiPlatform\OpenApi\Model\Tag;
42
use ApiPlatform\RamseyUuid\Serializer\UuidDenormalizer;
43
use ApiPlatform\State\ApiResource\Error;
44
use ApiPlatform\State\ParameterProviderInterface;
45
use ApiPlatform\State\ProcessorInterface;
46
use ApiPlatform\State\ProviderInterface;
47
use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaRestrictionMetadataInterface;
48
use ApiPlatform\Symfony\Validator\ValidationGroupsGeneratorInterface;
49
use ApiPlatform\Validator\Exception\ValidationException;
50
use Composer\InstalledVersions;
51
use Doctrine\Persistence\ManagerRegistry;
52
use PHPStan\PhpDocParser\Parser\PhpDocParser;
53
use Ramsey\Uuid\Uuid;
54
use Symfony\Bundle\FrameworkBundle\Command\TranslationExtractCommand;
55
use Symfony\Bundle\FrameworkBundle\Controller\ControllerHelper;
56
use Symfony\Component\Config\FileLocator;
57
use Symfony\Component\Config\Resource\DirectoryResource;
58
use Symfony\Component\DependencyInjection\ChildDefinition;
59
use Symfony\Component\DependencyInjection\ContainerBuilder;
60
use Symfony\Component\DependencyInjection\Definition;
61
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
62
use Symfony\Component\DependencyInjection\Extension\Extension;
63
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
64
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
65
use Symfony\Component\DependencyInjection\Reference;
66
use Symfony\Component\Finder\Finder;
67
use Symfony\Component\HttpClient\ScopingHttpClient;
68
use Symfony\Component\JsonStreamer\JsonStreamWriter;
69
use Symfony\Component\ObjectMapper\ObjectMapper;
70
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
71
use Symfony\Component\Serializer\Normalizer\NumberNormalizer;
72
use Symfony\Component\Uid\AbstractUid;
73
use Symfony\Component\Validator\Validator\ValidatorInterface;
74
use Symfony\Component\Yaml\Yaml;
75
use Twig\Environment;
76

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

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

117
        $configuration = new Configuration();
12✔
118
        $config = $this->processConfiguration($configuration, $configs);
12✔
119
        $container->setParameter('api_platform.use_symfony_listeners', $config['use_symfony_listeners']);
12✔
120

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

126
        if (!$config['enable_docs']) {
12✔
127
            // JSON-LD documentation format is mandatory, even if documentation is disabled.
128
            $docsFormats = isset($formats['jsonld']) ? ['jsonld' => ['application/ld+json']] : [];
×
129
            // If documentation is disabled, the Hydra documentation for all the resources is hidden by default.
130
            if (!isset($config['defaults']['hideHydraOperation']) && !isset($config['defaults']['hide_hydra_operation'])) {
×
131
                $config['defaults']['hideHydraOperation'] = true;
×
132
            }
133
        }
134
        $jsonSchemaFormats = $config['jsonschema_formats'];
12✔
135

136
        if (!$jsonSchemaFormats) {
12✔
137
            foreach (array_merge(array_keys($formats), array_keys($errorFormats)) as $f) {
12✔
138
                // Distinct JSON-based formats must have names that start with 'json'
139
                if (str_starts_with($f, 'json')) {
12✔
140
                    $jsonSchemaFormats[$f] = true;
12✔
141
                }
142
            }
143
        }
144

145
        if (!isset($errorFormats['json'])) {
12✔
146
            $errorFormats['json'] = ['application/problem+json', 'application/json'];
12✔
147
        }
148

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

153
        if (isset($formats['jsonapi']) && !isset($patchFormats['jsonapi'])) {
12✔
154
            $patchFormats['jsonapi'] = ['application/vnd.api+json'];
12✔
155
        }
156

157
        $this->registerCommonConfiguration($container, $config, $loader, $formats, $patchFormats, $errorFormats, $docsFormats);
12✔
158
        $this->registerMetadataConfiguration($container, $config, $loader);
12✔
159
        $this->registerOAuthConfiguration($container, $config);
12✔
160
        $this->registerOpenApiConfiguration($container, $config, $loader);
12✔
161
        $this->registerSwaggerConfiguration($container, $config, $loader);
12✔
162
        $this->registerJsonApiConfiguration($formats, $loader, $config);
12✔
163
        $this->registerJsonLdHydraConfiguration($container, $formats, $loader, $config);
12✔
164
        $this->registerJsonHalConfiguration($formats, $loader);
12✔
165
        $this->registerJsonProblemConfiguration($errorFormats, $loader);
12✔
166
        $this->registerGraphQlConfiguration($container, $config, $loader);
12✔
167
        $this->registerCacheConfiguration($container);
12✔
168
        $this->registerDoctrineOrmConfiguration($container, $config, $loader);
12✔
169
        $this->registerDoctrineMongoDbOdmConfiguration($container, $config, $loader);
12✔
170
        $this->registerHttpCacheConfiguration($container, $config, $loader);
12✔
171
        $this->registerValidatorConfiguration($container, $config, $loader);
12✔
172
        $this->registerDataCollectorConfiguration($container, $config, $loader);
12✔
173
        $this->registerMercureConfiguration($container, $config, $loader);
12✔
174
        $this->registerMessengerConfiguration($container, $config, $loader);
12✔
175
        $this->registerElasticsearchConfiguration($container, $config, $loader);
12✔
176
        $this->registerSecurityConfiguration($container, $config, $loader);
12✔
177
        $this->registerMakerConfiguration($container, $config, $loader);
12✔
178
        $this->registerArgumentResolverConfiguration($loader);
12✔
179
        $this->registerLinkSecurityConfiguration($loader, $config);
12✔
180
        $this->registerJsonStreamerConfiguration($container, $loader, $formats, $config);
12✔
181

182
        // TranslationExtractCommand was introduced in framework-bundle/7.3 with the object mapper service
183
        if (class_exists(ObjectMapper::class) && class_exists(TranslationExtractCommand::class)) {
12✔
184
            $loader->load('state/object_mapper.php');
12✔
185
        }
186
        $container->registerForAutoconfiguration(FilterInterface::class)
12✔
187
            ->addTag('api_platform.filter');
12✔
188
        $container->registerForAutoconfiguration(ProviderInterface::class)
12✔
189
            ->addTag('api_platform.state_provider');
12✔
190
        $container->registerForAutoconfiguration(ProcessorInterface::class)
12✔
191
            ->addTag('api_platform.state_processor');
12✔
192
        $container->registerForAutoconfiguration(UriVariableTransformerInterface::class)
12✔
193
            ->addTag('api_platform.uri_variables.transformer');
12✔
194
        $container->registerForAutoconfiguration(ParameterProviderInterface::class)
12✔
195
            ->addTag('api_platform.parameter_provider');
12✔
196
        $container->registerAttributeForAutoconfiguration(ApiResource::class, static function (ChildDefinition $definition): void {
12✔
197
            $definition->setAbstract(true)
×
198
                ->addTag('api_platform.resource')
×
199
                ->addTag('container.excluded', ['source' => 'by #[ApiResource] attribute']);
×
200
        });
12✔
201
        $container->registerAttributeForAutoconfiguration(
12✔
202
            AsResourceMutator::class,
12✔
203
            static function (ChildDefinition $definition, AsResourceMutator $attribute, \ReflectionClass $reflector): void { // @phpstan-ignore-line
12✔
204
                if (!is_a($reflector->name, ResourceMutatorInterface::class, true)) {
×
205
                    throw new RuntimeException(\sprintf('Resource mutator "%s" should implement %s', $reflector->name, ResourceMutatorInterface::class));
×
206
                }
207

208
                $definition->addTag('api_platform.resource_mutator', [
×
209
                    'resourceClass' => $attribute->resourceClass,
×
210
                ]);
×
211
            },
12✔
212
        );
12✔
213

214
        $container->registerAttributeForAutoconfiguration(
12✔
215
            AsOperationMutator::class,
12✔
216
            static function (ChildDefinition $definition, AsOperationMutator $attribute, \ReflectionClass $reflector): void { // @phpstan-ignore-line
12✔
217
                if (!is_a($reflector->name, OperationMutatorInterface::class, true)) {
×
218
                    throw new RuntimeException(\sprintf('Operation mutator "%s" should implement %s', $reflector->name, OperationMutatorInterface::class));
×
219
                }
220

221
                $definition->addTag('api_platform.operation_mutator', [
×
222
                    'operationName' => $attribute->operationName,
×
223
                ]);
×
224
            },
12✔
225
        );
12✔
226

227
        if (!$container->has('api_platform.state.item_provider')) {
12✔
228
            $container->setAlias('api_platform.state.item_provider', 'api_platform.state_provider.object');
×
229
        }
230

231
        if ($container->getParameter('kernel.debug')) {
12✔
232
            $this->injectStopwatch($container);
12✔
233
        }
234
    }
235

236
    private function injectStopwatch(ContainerBuilder $container): void
237
    {
238
        $services = [
12✔
239
            'api_platform.state_processor.add_link_header',
12✔
240
            'api_platform.state_processor.respond',
12✔
241
            'api_platform.state_processor.serialize',
12✔
242
            'api_platform.state_processor.write',
12✔
243
            'api_platform.state_provider.content_negotiation',
12✔
244
            'api_platform.state_provider.deserialize',
12✔
245
            'api_platform.state_provider.parameter',
12✔
246
            'api_platform.state_provider.read',
12✔
247
        ];
12✔
248

249
        foreach ($services as $id) {
12✔
250
            if (!$container->hasDefinition($id)) {
12✔
251
                continue;
×
252
            }
253

254
            $definition = $container->getDefinition($id);
12✔
255
            $definition->addMethodCall('setStopwatch', [new Reference('debug.stopwatch', ContainerBuilder::IGNORE_ON_INVALID_REFERENCE)]);
12✔
256
        }
257
    }
258

259
    private function registerCommonConfiguration(ContainerBuilder $container, array $config, PhpFileLoader $loader, array $formats, array $patchFormats, array $errorFormats, array $docsFormats): void
260
    {
261
        $loader->load('state/state.php');
12✔
262
        $loader->load('symfony/symfony.php');
12✔
263
        $loader->load('api.php');
12✔
264
        $loader->load('filter.php');
12✔
265

266
        if (class_exists(UuidDenormalizer::class) && class_exists(Uuid::class)) {
12✔
267
            $loader->load('ramsey_uuid.php');
12✔
268
        }
269

270
        if (class_exists(AbstractUid::class)) {
12✔
271
            $loader->load('symfony/uid.php');
12✔
272
        }
273

274
        // symfony/serializer:7.3 added the NumberNormalizer
275
        // symfony/framework-bundle:7.3 added the serializer.normalizer.number` service
276
        // if symfony/serializer >= 7.3 and symfony/framework-bundle < 7.3, the service is registred
277
        if (class_exists(NumberNormalizer::class) && !$container->has('serializer.normalizer.number')) {
12✔
278
            $numberNormalizerDefinition = new Definition(NumberNormalizer::class);
12✔
279
            $numberNormalizerDefinition->addTag('serializer.normalizer', ['built_in' => true, 'priority' => -915]);
12✔
280
            $container->setDefinition('serializer.normalizer.number', $numberNormalizerDefinition);
12✔
281
        }
282

283
        $defaultContext = ['hydra_prefix' => $config['serializer']['hydra_prefix']] + ($container->hasParameter('serializer.default_context') ? $container->getParameter('serializer.default_context') : []);
12✔
284

285
        $container->setParameter('api_platform.serializer.default_context', $defaultContext);
12✔
286
        if (!$container->hasParameter('serializer.default_context')) {
12✔
287
            $container->setParameter('serializer.default_context', $container->getParameter('api_platform.serializer.default_context'));
12✔
288
        }
289
        if ($config['use_symfony_listeners']) {
12✔
290
            $loader->load('symfony/events.php');
6✔
291
        } else {
UNCOV
292
            $loader->load('symfony/controller.php');
6✔
UNCOV
293
            $loader->load('state/provider.php');
6✔
UNCOV
294
            $loader->load('state/processor.php');
6✔
295
        }
296
        $loader->load('state/parameter_provider.php');
12✔
297

298
        $container->setParameter('api_platform.enable_entrypoint', $config['enable_entrypoint']);
12✔
299
        $container->setParameter('api_platform.enable_docs', $config['enable_docs']);
12✔
300
        $container->setParameter('api_platform.title', $config['title']);
12✔
301
        $container->setParameter('api_platform.description', $config['description']);
12✔
302
        $container->setParameter('api_platform.version', $config['version']);
12✔
303
        $container->setParameter('api_platform.show_webby', $config['show_webby']);
12✔
304
        $container->setParameter('api_platform.url_generation_strategy', $config['defaults']['url_generation_strategy'] ?? UrlGeneratorInterface::ABS_PATH);
12✔
305
        $container->setParameter('api_platform.exception_to_status', $config['exception_to_status']);
12✔
306
        $container->setParameter('api_platform.formats', $formats);
12✔
307
        $container->setParameter('api_platform.patch_formats', $patchFormats);
12✔
308
        $container->setParameter('api_platform.error_formats', $errorFormats);
12✔
309
        $container->setParameter('api_platform.docs_formats', $docsFormats);
12✔
310
        $container->setParameter('api_platform.jsonschema_formats', []);
12✔
311
        $container->setParameter('api_platform.eager_loading.enabled', $this->isConfigEnabled($container, $config['eager_loading']));
12✔
312
        $container->setParameter('api_platform.eager_loading.max_joins', $config['eager_loading']['max_joins']);
12✔
313
        $container->setParameter('api_platform.eager_loading.fetch_partial', $config['eager_loading']['fetch_partial']);
12✔
314
        $container->setParameter('api_platform.eager_loading.force_eager', $config['eager_loading']['force_eager']);
12✔
315
        $container->setParameter('api_platform.collection.exists_parameter_name', $config['collection']['exists_parameter_name']);
12✔
316
        $container->setParameter('api_platform.collection.order', $config['collection']['order']);
12✔
317
        $container->setParameter('api_platform.collection.order_parameter_name', $config['collection']['order_parameter_name']);
12✔
318
        $container->setParameter('api_platform.collection.order_nulls_comparison', $config['collection']['order_nulls_comparison']);
12✔
319
        $container->setParameter('api_platform.collection.pagination.enabled', $config['defaults']['pagination_enabled'] ?? true);
12✔
320
        $container->setParameter('api_platform.collection.pagination.partial', $config['defaults']['pagination_partial'] ?? false);
12✔
321
        $container->setParameter('api_platform.collection.pagination.client_enabled', $config['defaults']['pagination_client_enabled'] ?? false);
12✔
322
        $container->setParameter('api_platform.collection.pagination.client_items_per_page', $config['defaults']['pagination_client_items_per_page'] ?? false);
12✔
323
        $container->setParameter('api_platform.collection.pagination.client_partial', $config['defaults']['pagination_client_partial'] ?? false);
12✔
324
        $container->setParameter('api_platform.collection.pagination.items_per_page', $config['defaults']['pagination_items_per_page'] ?? 30);
12✔
325
        $container->setParameter('api_platform.collection.pagination.maximum_items_per_page', \array_key_exists('pagination_maximum_items_per_page', $config['defaults'] ?? []) ? $config['defaults']['pagination_maximum_items_per_page'] : 30);
12✔
326
        $container->setParameter('api_platform.collection.pagination.page_parameter_name', $config['defaults']['pagination_page_parameter_name'] ?? $config['collection']['pagination']['page_parameter_name']);
12✔
327
        $container->setParameter('api_platform.collection.pagination.enabled_parameter_name', $config['defaults']['pagination_enabled_parameter_name'] ?? $config['collection']['pagination']['enabled_parameter_name']);
12✔
328
        $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']);
12✔
329
        $container->setParameter('api_platform.collection.pagination.partial_parameter_name', $config['defaults']['pagination_partial_parameter_name'] ?? $config['collection']['pagination']['partial_parameter_name']);
12✔
330
        $container->setParameter('api_platform.collection.pagination', $this->getPaginationDefaults($config['defaults'] ?? [], $config['collection']['pagination']));
12✔
331
        $container->setParameter('api_platform.handle_symfony_errors', $config['handle_symfony_errors'] ?? false);
12✔
332
        $container->setParameter('api_platform.http_cache.etag', $config['defaults']['cache_headers']['etag'] ?? true);
12✔
333
        $container->setParameter('api_platform.http_cache.max_age', $config['defaults']['cache_headers']['max_age'] ?? null);
12✔
334
        $container->setParameter('api_platform.http_cache.shared_max_age', $config['defaults']['cache_headers']['shared_max_age'] ?? null);
12✔
335
        $container->setParameter('api_platform.http_cache.vary', $config['defaults']['cache_headers']['vary'] ?? ['Accept']);
12✔
336
        $container->setParameter('api_platform.http_cache.public', $config['defaults']['cache_headers']['public'] ?? $config['http_cache']['public']);
12✔
337
        $container->setParameter('api_platform.http_cache.stale_while_revalidate', $config['defaults']['cache_headers']['stale_while_revalidate'] ?? null);
12✔
338
        $container->setParameter('api_platform.http_cache.stale_if_error', $config['defaults']['cache_headers']['stale_if_error'] ?? null);
12✔
339
        $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']);
12✔
340
        $container->setParameter('api_platform.http_cache.invalidation.xkey.glue', $config['defaults']['cache_headers']['invalidation']['xkey']['glue'] ?? $config['http_cache']['invalidation']['xkey']['glue']);
12✔
341

342
        $container->setAlias('api_platform.path_segment_name_generator', $config['path_segment_name_generator']);
12✔
343
        $container->setAlias('api_platform.inflector', $config['inflector']);
12✔
344

345
        if ($config['name_converter']) {
12✔
346
            $container->setAlias('api_platform.name_converter', $config['name_converter']);
12✔
347
        }
348
        $container->setParameter('api_platform.asset_package', $config['asset_package']);
12✔
349
        $container->setParameter('api_platform.defaults', $this->normalizeDefaults($config['defaults'] ?? []));
12✔
350

351
        if ($container->getParameter('kernel.debug')) {
12✔
352
            $container->removeDefinition('api_platform.serializer.mapping.cache_class_metadata_factory');
12✔
353
        }
354
    }
355

356
    /**
357
     * This method will be removed in 3.0 when "defaults" will be the regular configuration path for the pagination.
358
     */
359
    private function getPaginationDefaults(array $defaults, array $collectionPaginationConfiguration): array
360
    {
361
        $paginationOptions = [];
12✔
362

363
        foreach ($defaults as $key => $value) {
12✔
364
            if (!str_starts_with($key, 'pagination_')) {
12✔
365
                continue;
12✔
366
            }
367

368
            $paginationOptions[str_replace('pagination_', '', $key)] = $value;
12✔
369
        }
370

371
        return array_merge($collectionPaginationConfiguration, $paginationOptions);
12✔
372
    }
373

374
    private function normalizeDefaults(array $defaults): array
375
    {
376
        $normalizedDefaults = ['extra_properties' => $defaults['extra_properties'] ?? []];
12✔
377
        unset($defaults['extra_properties']);
12✔
378

379
        $rc = new \ReflectionClass(ApiResource::class);
12✔
380
        $publicProperties = [];
12✔
381
        foreach ($rc->getConstructor()->getParameters() as $param) {
12✔
382
            $publicProperties[$param->getName()] = true;
12✔
383
        }
384

385
        $nameConverter = new CamelCaseToSnakeCaseNameConverter();
12✔
386
        foreach ($defaults as $option => $value) {
12✔
387
            if (isset($publicProperties[$nameConverter->denormalize($option)])) {
12✔
388
                $normalizedDefaults[$option] = $value;
12✔
389

390
                continue;
12✔
391
            }
392

393
            $normalizedDefaults['extra_properties'][$option] = $value;
×
394
        }
395

396
        return $normalizedDefaults;
12✔
397
    }
398

399
    private function registerMetadataConfiguration(ContainerBuilder $container, array $config, PhpFileLoader $loader): void
400
    {
401
        [$xmlResources, $yamlResources, $phpResources] = $this->getResourcesToWatch($container, $config);
12✔
402

403
        $container->setParameter('api_platform.class_name_resources', $this->getClassNameResources());
12✔
404

405
        $loader->load('metadata/resource_name.php');
12✔
406
        $loader->load('metadata/property_name.php');
12✔
407

408
        if (!empty($config['resource_class_directories'])) {
12✔
409
            $container->setParameter('api_platform.resource_class_directories', array_merge(
×
410
                $config['resource_class_directories'],
×
411
                $container->getParameter('api_platform.resource_class_directories')
×
412
            ));
×
413
        }
414

415
        // V3 metadata
416
        $loader->load('metadata/php.php');
12✔
417
        $loader->load('metadata/xml.php');
12✔
418
        $loader->load('metadata/links.php');
12✔
419
        $loader->load('metadata/property.php');
12✔
420
        $loader->load('metadata/resource.php');
12✔
421
        $loader->load('metadata/operation.php');
12✔
422
        $loader->load('metadata/mutator.php');
12✔
423

424
        $container->getDefinition('api_platform.metadata.resource_extractor.xml')->replaceArgument(0, $xmlResources);
12✔
425
        $container->getDefinition('api_platform.metadata.property_extractor.xml')->replaceArgument(0, $xmlResources);
12✔
426

427
        if ($config['enable_phpdoc_parser'] && class_exists(PhpDocParser::class)) {
12✔
428
            $loader->load('metadata/php_doc.php');
12✔
429
        }
430

431
        if (class_exists(Yaml::class)) {
12✔
432
            $loader->load('metadata/yaml.php');
12✔
433
            $container->getDefinition('api_platform.metadata.resource_extractor.yaml')->replaceArgument(0, $yamlResources);
12✔
434
            $container->getDefinition('api_platform.metadata.property_extractor.yaml')->replaceArgument(0, $yamlResources);
12✔
435
        }
436

437
        $container->getDefinition('api_platform.metadata.resource_extractor.php_file')->replaceArgument(0, $phpResources);
12✔
438
    }
439

440
    private function getClassNameResources(): array
441
    {
442
        return [
12✔
443
            Error::class,
12✔
444
            ValidationException::class,
12✔
445
        ];
12✔
446
    }
447

448
    private function getBundlesResourcesPaths(ContainerBuilder $container, array $config): array
449
    {
450
        $bundlesResourcesPaths = [];
12✔
451

452
        foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) {
12✔
453
            $dirname = $bundle['path'];
12✔
454
            $paths = [
12✔
455
                "$dirname/ApiResource",
12✔
456
                "$dirname/src/ApiResource",
12✔
457
            ];
12✔
458
            foreach (['.yaml', '.yml', '.xml', ''] as $extension) {
12✔
459
                $paths[] = "$dirname/Resources/config/api_resources$extension";
12✔
460
                $paths[] = "$dirname/config/api_resources$extension";
12✔
461
            }
462
            if ($this->isConfigEnabled($container, $config['doctrine'])) {
12✔
463
                $paths[] = "$dirname/Entity";
12✔
464
                $paths[] = "$dirname/src/Entity";
12✔
465
            }
466
            if ($this->isConfigEnabled($container, $config['doctrine_mongodb_odm'])) {
12✔
467
                $paths[] = "$dirname/Document";
×
468
                $paths[] = "$dirname/src/Document";
×
469
            }
470

471
            foreach ($paths as $path) {
12✔
472
                if ($container->fileExists($path, false)) {
12✔
473
                    $bundlesResourcesPaths[] = $path;
12✔
474
                }
475
            }
476
        }
477

478
        return $bundlesResourcesPaths;
12✔
479
    }
480

481
    private function getResourcesToWatch(ContainerBuilder $container, array $config): array
482
    {
483
        $paths = array_unique(array_merge($this->getBundlesResourcesPaths($container, $config), $config['mapping']['paths']));
12✔
484

485
        if (!$config['mapping']['paths']) {
12✔
486
            $projectDir = $container->getParameter('kernel.project_dir');
×
487
            foreach (["$projectDir/config/api_platform", "$projectDir/src/ApiResource"] as $dir) {
×
488
                if (is_dir($dir)) {
×
489
                    $paths[] = $dir;
×
490
                }
491
            }
492

493
            if ($this->isConfigEnabled($container, $config['doctrine']) && is_dir($doctrinePath = "$projectDir/src/Entity")) {
×
494
                $paths[] = $doctrinePath;
×
495
            }
496

497
            if ($this->isConfigEnabled($container, $config['doctrine_mongodb_odm']) && is_dir($documentPath = "$projectDir/src/Document")) {
×
498
                $paths[] = $documentPath;
×
499
            }
500
        }
501

502
        $resources = ['yml' => [], 'xml' => [], 'php' => [], 'dir' => []];
12✔
503

504
        foreach ($config['mapping']['imports'] ?? [] as $path) {
12✔
505
            if (is_dir($path)) {
×
506
                foreach (Finder::create()->followLinks()->files()->in($path)->name('/\.php$/')->sortByName() as $file) {
×
507
                    $resources[$file->getExtension()][] = $file->getRealPath();
×
508
                }
509

510
                $resources['dir'][] = $path;
×
511
                $container->addResource(new DirectoryResource($path, '/\.php$/'));
×
512

513
                continue;
×
514
            }
515

516
            if ($container->fileExists($path, false)) {
×
517
                if (!str_ends_with($path, '.php')) {
×
518
                    throw new RuntimeException(\sprintf('Unsupported mapping type in "%s", supported type is PHP.', $path));
×
519
                }
520

521
                $resources['php'][] = $path;
×
522

523
                continue;
×
524
            }
525

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

529
        foreach ($paths as $path) {
12✔
530
            if (is_dir($path)) {
12✔
531
                foreach (Finder::create()->followLinks()->files()->in($path)->name('/\.(xml|ya?ml)$/')->sortByName() as $file) {
12✔
532
                    $resources['yaml' === ($extension = $file->getExtension()) ? 'yml' : $extension][] = $file->getRealPath();
12✔
533
                }
534

535
                $resources['dir'][] = $path;
12✔
536
                $container->addResource(new DirectoryResource($path, '/\.(xml|ya?ml|php)$/'));
12✔
537

538
                continue;
12✔
539
            }
540

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

546
                $resources['yaml' === $matches[1] ? 'yml' : $matches[1]][] = $path;
×
547

548
                continue;
×
549
            }
550

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

554
        $container->setParameter('api_platform.resource_class_directories', $resources['dir']);
12✔
555

556
        return [$resources['xml'], $resources['yml'], $resources['php']];
12✔
557
    }
558

559
    private function registerOAuthConfiguration(ContainerBuilder $container, array $config): void
560
    {
561
        if (!$config['oauth']) {
12✔
562
            return;
×
563
        }
564

565
        $container->setParameter('api_platform.oauth.enabled', $this->isConfigEnabled($container, $config['oauth']));
12✔
566
        $container->setParameter('api_platform.oauth.clientId', $config['oauth']['clientId']);
12✔
567
        $container->setParameter('api_platform.oauth.clientSecret', $config['oauth']['clientSecret']);
12✔
568
        $container->setParameter('api_platform.oauth.type', $config['oauth']['type']);
12✔
569
        $container->setParameter('api_platform.oauth.flow', $config['oauth']['flow']);
12✔
570
        $container->setParameter('api_platform.oauth.tokenUrl', $config['oauth']['tokenUrl']);
12✔
571
        $container->setParameter('api_platform.oauth.authorizationUrl', $config['oauth']['authorizationUrl']);
12✔
572
        $container->setParameter('api_platform.oauth.refreshUrl', $config['oauth']['refreshUrl']);
12✔
573
        $container->setParameter('api_platform.oauth.scopes', $config['oauth']['scopes']);
12✔
574
        $container->setParameter('api_platform.oauth.pkce', $config['oauth']['pkce']);
12✔
575
    }
576

577
    /**
578
     * Registers the Swagger, ReDoc and Swagger UI configuration.
579
     */
580
    private function registerSwaggerConfiguration(ContainerBuilder $container, array $config, PhpFileLoader $loader): void
581
    {
582
        foreach (array_keys($config['swagger']['api_keys']) as $keyName) {
12✔
583
            if (!preg_match('/^[a-zA-Z0-9._-]+$/', $keyName)) {
12✔
584
                throw new RuntimeException(\sprintf('The swagger api_keys key "%s" is not valid, it should match "^[a-zA-Z0-9._-]+$"', $keyName));
×
585
            }
586
        }
587

588
        $container->setParameter('api_platform.swagger.versions', $config['swagger']['versions']);
12✔
589

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

594
        if (!$config['enable_swagger']) {
12✔
595
            $container->setParameter('api_platform.enable_swagger_ui', false);
×
596
            $container->setParameter('api_platform.enable_re_doc', false);
×
597

598
            return;
×
599
        }
600

601
        $loader->load('openapi.php');
12✔
602

603
        if (class_exists(Yaml::class)) {
12✔
604
            $loader->load('openapi/yaml.php');
12✔
605
        }
606

607
        if ($config['enable_swagger_ui'] || $config['enable_re_doc']) {
12✔
608
            $loader->load('swagger_ui.php');
10✔
609

610
            if ($config['use_symfony_listeners']) {
10✔
611
                $loader->load('symfony/swagger_ui.php');
5✔
612
            }
613

614
            $loader->load('state/swagger_ui.php');
10✔
615
        }
616

617
        if (!$config['enable_swagger_ui'] && !$config['enable_re_doc']) {
12✔
618
            // Remove the listener but keep the controller to allow customizing the path of the UI
619
            $container->removeDefinition('api_platform.swagger.listener.ui');
2✔
620
        }
621

622
        $container->setParameter('api_platform.enable_swagger_ui', $config['enable_swagger_ui']);
12✔
623
        $container->setParameter('api_platform.enable_re_doc', $config['enable_re_doc']);
12✔
624
        $container->setParameter('api_platform.swagger.api_keys', $config['swagger']['api_keys']);
12✔
625
        $container->setParameter('api_platform.swagger.persist_authorization', $config['swagger']['persist_authorization']);
12✔
626
        $container->setParameter('api_platform.swagger.http_auth', $config['swagger']['http_auth']);
12✔
627
        if ($config['openapi']['swagger_ui_extra_configuration'] && $config['swagger']['swagger_ui_extra_configuration']) {
12✔
628
            throw new RuntimeException('You can not set "swagger_ui_extra_configuration" twice - in "openapi" and "swagger" section.');
×
629
        }
630
        $container->setParameter('api_platform.swagger_ui.extra_configuration', $config['openapi']['swagger_ui_extra_configuration'] ?: $config['swagger']['swagger_ui_extra_configuration']);
12✔
631
    }
632

633
    private function registerJsonApiConfiguration(array $formats, PhpFileLoader $loader, array $config): void
634
    {
635
        if (!isset($formats['jsonapi'])) {
12✔
636
            return;
×
637
        }
638

639
        if (!InstalledVersions::isInstalled('api-platform/json-api')) {
12✔
640
            throw new \LogicException('JSON-API support cannot be enabled as the JSON-API component is not installed. Try running "composer require api-platform/json-api".');
×
641
        }
642

643
        $loader->load('jsonapi.php');
12✔
644
        $loader->load('state/jsonapi.php');
12✔
645
    }
646

647
    private function registerJsonLdHydraConfiguration(ContainerBuilder $container, array $formats, PhpFileLoader $loader, array $config): void
648
    {
649
        if (!isset($formats['jsonld'])) {
12✔
650
            return;
×
651
        }
652

653
        if ($config['use_symfony_listeners']) {
12✔
654
            $loader->load('symfony/jsonld.php');
6✔
655
        } else {
UNCOV
656
            $loader->load('state/jsonld.php');
6✔
657
        }
658

659
        $loader->load('state/hydra.php');
12✔
660
        $loader->load('jsonld.php');
12✔
661
        $loader->load('hydra.php');
12✔
662

663
        if (!$container->has('api_platform.json_schema.schema_factory')) {
12✔
664
            $container->removeDefinition('api_platform.hydra.json_schema.schema_factory');
×
665
        }
666
    }
667

668
    private function registerJsonHalConfiguration(array $formats, PhpFileLoader $loader): void
669
    {
670
        if (!isset($formats['jsonhal'])) {
12✔
671
            return;
×
672
        }
673

674
        if (!InstalledVersions::isInstalled('api-platform/hal')) {
12✔
675
            throw new \LogicException('HAL support cannot be enabled as the HAL component is not installed. Try running "composer require api-platform/hal".');
×
676
        }
677

678
        $loader->load('hal.php');
12✔
679
    }
680

681
    private function registerJsonProblemConfiguration(array $errorFormats, PhpFileLoader $loader): void
682
    {
683
        if (!isset($errorFormats['jsonproblem'])) {
12✔
684
            return;
×
685
        }
686

687
        $loader->load('problem.php');
12✔
688
    }
689

690
    private function registerGraphQlConfiguration(ContainerBuilder $container, array $config, PhpFileLoader $loader): void
691
    {
692
        $enabled = $this->isConfigEnabled($container, $config['graphql']);
12✔
693
        $graphqlIntrospectionEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['introspection']);
12✔
694
        $graphiqlEnabled = $enabled && $this->isConfigEnabled($container, $config['graphql']['graphiql']);
12✔
695
        $maxQueryDepth = (int) $config['graphql']['max_query_depth'];
12✔
696
        $maxQueryComplexity = (int) $config['graphql']['max_query_complexity'];
12✔
697

698
        $container->setParameter('api_platform.graphql.enabled', $enabled);
12✔
699
        $container->setParameter('api_platform.graphql.max_query_depth', $maxQueryDepth);
12✔
700
        $container->setParameter('api_platform.graphql.max_query_complexity', $maxQueryComplexity);
12✔
701
        $container->setParameter('api_platform.graphql.introspection.enabled', $graphqlIntrospectionEnabled);
12✔
702
        $container->setParameter('api_platform.graphql.graphiql.enabled', $graphiqlEnabled);
12✔
703
        $container->setParameter('api_platform.graphql.collection.pagination', $config['graphql']['collection']['pagination']);
12✔
704

705
        if (!$enabled) {
12✔
706
            return;
×
707
        }
708

709
        if (!class_exists(Executor::class)) {
12✔
710
            throw new \RuntimeException('Graphql is enabled but not installed, run: composer require "api-platform/graphql".');
×
711
        }
712

713
        $container->setParameter('api_platform.graphql.default_ide', $config['graphql']['default_ide']);
12✔
714
        $container->setParameter('api_platform.graphql.nesting_separator', $config['graphql']['nesting_separator']);
12✔
715

716
        $loader->load('graphql.php');
12✔
717

718
        if (!class_exists(Environment::class) || !isset($container->getParameter('kernel.bundles')['TwigBundle'])) {
12✔
719
            if ($graphiqlEnabled) {
×
720
                throw new RuntimeException(\sprintf('GraphiQL interfaces depend on Twig. Please activate TwigBundle for the %s environnement or disable GraphiQL.', $container->getParameter('kernel.environment')));
×
721
            }
722
            $container->removeDefinition('api_platform.graphql.action.graphiql');
×
723
        }
724

725
        $container->registerForAutoconfiguration(QueryItemResolverInterface::class)
12✔
726
            ->addTag('api_platform.graphql.resolver');
12✔
727
        $container->registerForAutoconfiguration(QueryCollectionResolverInterface::class)
12✔
728
            ->addTag('api_platform.graphql.resolver');
12✔
729
        $container->registerForAutoconfiguration(MutationResolverInterface::class)
12✔
730
            ->addTag('api_platform.graphql.resolver');
12✔
731
        $container->registerForAutoconfiguration(GraphQlTypeInterface::class)
12✔
732
            ->addTag('api_platform.graphql.type');
12✔
733
        $container->registerForAutoconfiguration(ErrorHandlerInterface::class)
12✔
734
            ->addTag('api_platform.graphql.error_handler');
12✔
735
    }
736

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

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

750
        if (!InstalledVersions::isInstalled('api-platform/doctrine-orm')) {
12✔
751
            throw new \LogicException('Doctrine support cannot be enabled as the doctrine ORM component is not installed. Try running "composer require api-platform/doctrine-orm".');
×
752
        }
753

754
        // For older versions of doctrine bridge this allows autoconfiguration for filters
755
        if (!$container->has(ManagerRegistry::class)) {
12✔
756
            $container->setAlias(ManagerRegistry::class, 'doctrine');
12✔
757
        }
758

759
        $container->registerForAutoconfiguration(QueryItemExtensionInterface::class)
12✔
760
            ->addTag('api_platform.doctrine.orm.query_extension.item');
12✔
761
        $container->registerForAutoconfiguration(DoctrineQueryCollectionExtensionInterface::class)
12✔
762
            ->addTag('api_platform.doctrine.orm.query_extension.collection');
12✔
763
        $container->registerForAutoconfiguration(DoctrineOrmAbstractFilter::class);
12✔
764

765
        $container->registerForAutoconfiguration(OrmLinksHandlerInterface::class)
12✔
766
            ->addTag('api_platform.doctrine.orm.links_handler');
12✔
767

768
        $loader->load('doctrine_orm.php');
12✔
769

770
        if ($this->isConfigEnabled($container, $config['eager_loading'])) {
12✔
771
            return;
12✔
772
        }
773

774
        $container->removeAlias(EagerLoadingExtension::class);
×
775
        $container->removeDefinition('api_platform.doctrine.orm.query_extension.eager_loading');
×
776
        $container->removeAlias(FilterEagerLoadingExtension::class);
×
777
        $container->removeDefinition('api_platform.doctrine.orm.query_extension.filter_eager_loading');
×
778
    }
779

780
    private function registerDoctrineMongoDbOdmConfiguration(ContainerBuilder $container, array $config, PhpFileLoader $loader): void
781
    {
782
        if (!$this->isConfigEnabled($container, $config['doctrine_mongodb_odm'])) {
12✔
783
            return;
12✔
784
        }
785

786
        if (!InstalledVersions::isInstalled('api-platform/doctrine-odm')) {
×
787
            throw new \LogicException('Doctrine MongoDB ODM support cannot be enabled as the doctrine ODM component is not installed. Try running "composer require api-platform/doctrine-odm".');
×
788
        }
789

790
        $container->registerForAutoconfiguration(AggregationItemExtensionInterface::class)
×
791
            ->addTag('api_platform.doctrine_mongodb.odm.aggregation_extension.item');
×
792
        $container->registerForAutoconfiguration(AggregationCollectionExtensionInterface::class)
×
793
            ->addTag('api_platform.doctrine_mongodb.odm.aggregation_extension.collection');
×
794
        $container->registerForAutoconfiguration(DoctrineMongoDbOdmAbstractFilter::class)
×
795
            ->setBindings(['$managerRegistry' => new Reference('doctrine_mongodb')]);
×
796
        $container->registerForAutoconfiguration(OdmLinksHandlerInterface::class)
×
797
            ->addTag('api_platform.doctrine.odm.links_handler');
×
798

799
        $loader->load('doctrine_mongodb_odm.php');
×
800
    }
801

802
    private function registerHttpCacheConfiguration(ContainerBuilder $container, array $config, PhpFileLoader $loader): void
803
    {
804
        $loader->load('http_cache.php');
12✔
805

806
        if (!$this->isConfigEnabled($container, $config['http_cache']['invalidation'])) {
12✔
807
            return;
×
808
        }
809

810
        if ($this->isConfigEnabled($container, $config['doctrine'])) {
12✔
811
            $loader->load('doctrine_orm_http_cache_purger.php');
12✔
812
        }
813

814
        $loader->load('state/http_cache_purger.php');
12✔
815
        $loader->load('http_cache_purger.php');
12✔
816

817
        foreach ($config['http_cache']['invalidation']['scoped_clients'] as $client) {
12✔
818
            $definition = $container->getDefinition($client);
×
819
            $definition->addTag('api_platform.http_cache.http_client');
×
820
        }
821

822
        if (!($urls = $config['http_cache']['invalidation']['urls'])) {
12✔
823
            $urls = $config['http_cache']['invalidation']['varnish_urls'];
12✔
824
        }
825

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

833
        $serviceName = $config['http_cache']['invalidation']['purger'];
12✔
834

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

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

852
        return $formats;
12✔
853
    }
854

855
    private function registerValidatorConfiguration(ContainerBuilder $container, array $config, PhpFileLoader $loader): void
856
    {
857
        if (interface_exists(ValidatorInterface::class)) {
12✔
858
            $loader->load('metadata/validator.php');
12✔
859
            $loader->load('validator/validator.php');
12✔
860

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

865
            $loader->load($config['use_symfony_listeners'] ? 'validator/events.php' : 'validator/state.php');
12✔
866

867
            $container->registerForAutoconfiguration(ValidationGroupsGeneratorInterface::class)
12✔
868
                ->addTag('api_platform.validation_groups_generator');
12✔
869
            $container->registerForAutoconfiguration(PropertySchemaRestrictionMetadataInterface::class)
12✔
870
                ->addTag('api_platform.metadata.property_schema_restriction');
12✔
871
        }
872

873
        if (!$config['validator']) {
12✔
874
            return;
×
875
        }
876

877
        $container->setParameter('api_platform.validator.serialize_payload_fields', $config['validator']['serialize_payload_fields']);
12✔
878
    }
879

880
    private function registerDataCollectorConfiguration(ContainerBuilder $container, array $config, PhpFileLoader $loader): void
881
    {
882
        if (!$config['enable_profiler']) {
12✔
883
            return;
×
884
        }
885

886
        $loader->load('data_collector.php');
12✔
887

888
        if ($container->hasParameter('kernel.debug') && $container->getParameter('kernel.debug')) {
12✔
889
            $loader->load('debug.php');
12✔
890
        }
891
    }
892

893
    private function registerMercureConfiguration(ContainerBuilder $container, array $config, PhpFileLoader $loader): void
894
    {
895
        if (!$this->isConfigEnabled($container, $config['mercure'])) {
12✔
896
            return;
×
897
        }
898

899
        if (!InstalledVersions::isInstalled('symfony/mercure-bundle')) {
12✔
900
            throw new \LogicException('Mercure support cannot be enabled as the Symfony Mercure Bundle is not installed. Try running "composer require symfony/mercure-bundle".');
×
901
        }
902

903
        $container->setParameter('api_platform.mercure.include_type', $config['mercure']['include_type']);
12✔
904
        $loader->load('state/mercure.php');
12✔
905

906
        if ($this->isConfigEnabled($container, $config['doctrine'])) {
12✔
907
            $loader->load('doctrine_orm_mercure_publisher.php');
12✔
908
        }
909
        if ($this->isConfigEnabled($container, $config['doctrine_mongodb_odm'])) {
12✔
910
            $loader->load('doctrine_odm_mercure_publisher.php');
×
911
        }
912

913
        if ($this->isConfigEnabled($container, $config['graphql'])) {
12✔
914
            $loader->load('graphql_mercure.php');
12✔
915
        }
916
    }
917

918
    private function registerMessengerConfiguration(ContainerBuilder $container, array $config, PhpFileLoader $loader): void
919
    {
920
        if (!$this->isConfigEnabled($container, $config['messenger'])) {
12✔
921
            return;
×
922
        }
923

924
        if (!InstalledVersions::isInstalled('symfony/messenger')) {
12✔
925
            throw new \LogicException('Messenger support cannot be enabled as the Symfony Messenger component is not installed. Try running "composer require symfony/messenger".');
×
926
        }
927

928
        $loader->load('messenger.php');
12✔
929
    }
930

931
    private function registerElasticsearchConfiguration(ContainerBuilder $container, array $config, PhpFileLoader $loader): void
932
    {
933
        $enabled = $this->isConfigEnabled($container, $config['elasticsearch']);
12✔
934

935
        $container->setParameter('api_platform.elasticsearch.enabled', $enabled);
12✔
936

937
        if (!$enabled) {
12✔
938
            return;
12✔
939
        }
940

941
        if (!InstalledVersions::isInstalled('api-platform/elasticsearch')) {
×
942
            throw new \LogicException('Elasticsearch support cannot be enabled as the Elasticsearch component is not installed. Try running "composer require api-platform/elasticsearch".');
×
943
        }
944

945
        $clientClass = !class_exists(\Elasticsearch\Client::class)
×
946
            // ES v7
×
947
            ? \Elastic\Elasticsearch\Client::class
×
948
            // ES v8 and up
×
949
            : \Elasticsearch\Client::class;
×
950

951
        $clientDefinition = new Definition($clientClass);
×
952
        $container->setDefinition('api_platform.elasticsearch.client', $clientDefinition);
×
953
        $container->registerForAutoconfiguration(RequestBodySearchCollectionExtensionInterface::class)
×
954
            ->addTag('api_platform.elasticsearch.request_body_search_extension.collection');
×
955
        $container->setParameter('api_platform.elasticsearch.hosts', $config['elasticsearch']['hosts']);
×
956
        $loader->load('elasticsearch.php');
×
957
    }
958

959
    private function registerSecurityConfiguration(ContainerBuilder $container, array $config, PhpFileLoader $loader): void
960
    {
961
        /** @var string[] $bundles */
962
        $bundles = $container->getParameter('kernel.bundles');
12✔
963

964
        if (!isset($bundles['SecurityBundle'])) {
12✔
965
            return;
×
966
        }
967

968
        $loader->load('security.php');
12✔
969

970
        $loader->load('state/security.php');
12✔
971

972
        if (interface_exists(ValidatorInterface::class)) {
12✔
973
            $loader->load('state/security_validator.php');
12✔
974
        }
975

976
        if ($this->isConfigEnabled($container, $config['graphql'])) {
12✔
977
            $loader->load('graphql/security.php');
12✔
978
        }
979
    }
980

981
    private function registerOpenApiConfiguration(ContainerBuilder $container, array $config, PhpFileLoader $loader): void
982
    {
983
        $container->setParameter('api_platform.openapi.termsOfService', $config['openapi']['termsOfService']);
12✔
984
        $container->setParameter('api_platform.openapi.contact.name', $config['openapi']['contact']['name']);
12✔
985
        $container->setParameter('api_platform.openapi.contact.url', $config['openapi']['contact']['url']);
12✔
986
        $container->setParameter('api_platform.openapi.contact.email', $config['openapi']['contact']['email']);
12✔
987
        $container->setParameter('api_platform.openapi.license.name', $config['openapi']['license']['name']);
12✔
988
        $container->setParameter('api_platform.openapi.license.url', $config['openapi']['license']['url']);
12✔
989
        $container->setParameter('api_platform.openapi.license.identifier', $config['openapi']['license']['identifier']);
12✔
990
        $container->setParameter('api_platform.openapi.overrideResponses', $config['openapi']['overrideResponses']);
12✔
991

992
        $tags = [];
12✔
993
        foreach ($config['openapi']['tags'] as $tag) {
12✔
994
            $tags[] = new Tag($tag['name'], $tag['description'] ?? null);
×
995
        }
996

997
        $container->setParameter('api_platform.openapi.tags', $tags);
12✔
998

999
        $container->setParameter('api_platform.openapi.errorResourceClass', $config['openapi']['error_resource_class'] ?? null);
12✔
1000
        $container->setParameter('api_platform.openapi.validationErrorResourceClass', $config['openapi']['validation_error_resource_class'] ?? null);
12✔
1001

1002
        $loader->load('json_schema.php');
12✔
1003
    }
1004

1005
    private function registerMakerConfiguration(ContainerBuilder $container, array $config, PhpFileLoader $loader): void
1006
    {
1007
        if (!$this->isConfigEnabled($container, $config['maker'])) {
12✔
1008
            return;
×
1009
        }
1010

1011
        $loader->load('maker.php');
12✔
1012
    }
1013

1014
    private function registerArgumentResolverConfiguration(PhpFileLoader $loader): void
1015
    {
1016
        $loader->load('argument_resolver.php');
12✔
1017
    }
1018

1019
    private function registerLinkSecurityConfiguration(PhpFileLoader $loader, array $config): void
1020
    {
1021
        if ($config['enable_link_security']) {
12✔
1022
            $loader->load('link_security.php');
12✔
1023
        }
1024
    }
1025

1026
    private function registerJsonStreamerConfiguration(ContainerBuilder $container, PhpFileLoader $loader, array $formats, array $config): void
1027
    {
1028
        if (!$config['enable_json_streamer']) {
12✔
1029
            return;
×
1030
        }
1031

1032
        if (!class_exists(JsonStreamWriter::class)) {
12✔
1033
            throw new RuntimeException('symfony/json-streamer is not installed.');
×
1034
        }
1035

1036
        // @TODO symfony/json-streamer:>=7.4.1 add composer conflict
1037
        if (!class_exists(ControllerHelper::class)) {
12✔
1038
            throw new RuntimeException('Symfony symfony/json-stream:^7.4 is needed.');
×
1039
        }
1040

1041
        if (isset($formats['jsonld'])) {
12✔
1042
            $container->setParameter('.json_streamer.stream_writers_dir.jsonld', '%kernel.cache_dir%/json_streamer/stream_writer/jsonld');
12✔
1043
            $container->setParameter('.json_streamer.stream_readers_dir.jsonld', '%kernel.cache_dir%/json_streamer/stream_reader/jsonld');
12✔
1044
            $container->setParameter('.json_streamer.lazy_ghosts_dir.jsonld', '%kernel.cache_dir%/json_streamer/lazy_ghost/jsonld');
12✔
1045
        }
1046

1047
        $loader->load('json_streamer/common.php');
12✔
1048

1049
        if ($config['use_symfony_listeners']) {
12✔
1050
            $loader->load('json_streamer/events.php');
6✔
1051
        } else {
UNCOV
1052
            if (isset($formats['jsonld'])) {
6✔
UNCOV
1053
                $loader->load('json_streamer/hydra.php');
6✔
1054
            }
1055

UNCOV
1056
            if (isset($formats['json'])) {
6✔
UNCOV
1057
                $loader->load('json_streamer/json.php');
6✔
1058
            }
1059
        }
1060
    }
1061
}
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