vendor/api-platform/core/src/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php line 478

  1. <?php
  2. /*
  3.  * This file is part of the API Platform project.
  4.  *
  5.  * (c) Kévin Dunglas <dunglas@gmail.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. declare(strict_types=1);
  11. namespace ApiPlatform\Symfony\Bundle\DependencyInjection;
  12. use ApiPlatform\Api\FilterInterface;
  13. use ApiPlatform\Api\UrlGeneratorInterface;
  14. use ApiPlatform\Doctrine\Odm\Extension\AggregationCollectionExtensionInterface;
  15. use ApiPlatform\Doctrine\Odm\Extension\AggregationItemExtensionInterface;
  16. use ApiPlatform\Doctrine\Odm\Filter\AbstractFilter as DoctrineMongoDbOdmAbstractFilter;
  17. use ApiPlatform\Doctrine\Orm\Extension\EagerLoadingExtension;
  18. use ApiPlatform\Doctrine\Orm\Extension\FilterEagerLoadingExtension;
  19. use ApiPlatform\Doctrine\Orm\Extension\QueryCollectionExtensionInterface as DoctrineQueryCollectionExtensionInterface;
  20. use ApiPlatform\Doctrine\Orm\Extension\QueryItemExtensionInterface;
  21. use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter as DoctrineOrmAbstractFilter;
  22. use ApiPlatform\Elasticsearch\Extension\RequestBodySearchCollectionExtensionInterface;
  23. use ApiPlatform\GraphQl\Error\ErrorHandlerInterface;
  24. use ApiPlatform\GraphQl\Resolver\MutationResolverInterface;
  25. use ApiPlatform\GraphQl\Resolver\QueryCollectionResolverInterface;
  26. use ApiPlatform\GraphQl\Resolver\QueryItemResolverInterface;
  27. use ApiPlatform\GraphQl\Type\Definition\TypeInterface as GraphQlTypeInterface;
  28. use ApiPlatform\Metadata\ApiResource;
  29. use ApiPlatform\State\ProcessorInterface;
  30. use ApiPlatform\State\ProviderInterface;
  31. use ApiPlatform\Symfony\GraphQl\Resolver\Factory\DataCollectorResolverFactory;
  32. use ApiPlatform\Symfony\Validator\Metadata\Property\Restriction\PropertySchemaRestrictionMetadataInterface;
  33. use ApiPlatform\Symfony\Validator\ValidationGroupsGeneratorInterface;
  34. use Doctrine\Persistence\ManagerRegistry;
  35. use phpDocumentor\Reflection\DocBlockFactoryInterface;
  36. use PHPStan\PhpDocParser\Parser\PhpDocParser;
  37. use Ramsey\Uuid\Uuid;
  38. use Symfony\Component\Config\FileLocator;
  39. use Symfony\Component\Config\Resource\DirectoryResource;
  40. use Symfony\Component\DependencyInjection\ContainerBuilder;
  41. use Symfony\Component\DependencyInjection\ContainerInterface;
  42. use Symfony\Component\DependencyInjection\Definition;
  43. use Symfony\Component\DependencyInjection\Exception\RuntimeException;
  44. use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
  45. use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
  46. use Symfony\Component\DependencyInjection\Reference;
  47. use Symfony\Component\Finder\Finder;
  48. use Symfony\Component\HttpClient\ScopingHttpClient;
  49. use Symfony\Component\HttpKernel\DependencyInjection\Extension;
  50. use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
  51. use Symfony\Component\Uid\AbstractUid;
  52. use Symfony\Component\Validator\Validator\ValidatorInterface;
  53. use Symfony\Component\Yaml\Yaml;
  54. use Twig\Environment;
  55. /**
  56.  * The extension of this bundle.
  57.  *
  58.  * @author Kévin Dunglas <dunglas@gmail.com>
  59.  */
  60. final class ApiPlatformExtension extends Extension implements PrependExtensionInterface
  61. {
  62.     /**
  63.      * {@inheritdoc}
  64.      */
  65.     public function prepend(ContainerBuilder $container): void
  66.     {
  67.         if (isset($container->getExtensions()['framework'])) {
  68.             $container->prependExtensionConfig('framework', [
  69.                 'serializer' => [
  70.                     'enabled' => true,
  71.                 ],
  72.             ]);
  73.             $container->prependExtensionConfig('framework', [
  74.                 'property_info' => [
  75.                     'enabled' => true,
  76.                 ],
  77.             ]);
  78.         }
  79.     }
  80.     /**
  81.      * {@inheritdoc}
  82.      */
  83.     public function load(array $configsContainerBuilder $container): void
  84.     {
  85.         $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
  86.         $configuration = new Configuration();
  87.         $config $this->processConfiguration($configuration$configs);
  88.         $formats $this->getFormats($config['formats']);
  89.         $patchFormats $this->getFormats($config['patch_formats']);
  90.         $errorFormats $this->getFormats($config['error_formats']);
  91.         // Backward Compatibility layer
  92.         if (isset($formats['jsonapi']) && !isset($patchFormats['jsonapi'])) {
  93.             $patchFormats['jsonapi'] = ['application/vnd.api+json'];
  94.         }
  95.         $this->registerCommonConfiguration($container$config$loader$formats$patchFormats$errorFormats);
  96.         $this->registerMetadataConfiguration($container$config$loader);
  97.         $this->registerOAuthConfiguration($container$config);
  98.         $this->registerOpenApiConfiguration($container$config$loader);
  99.         $this->registerSwaggerConfiguration($container$config$loader);
  100.         $this->registerJsonApiConfiguration($formats$loader);
  101.         $this->registerJsonLdHydraConfiguration($container$formats$loader$config);
  102.         $this->registerJsonHalConfiguration($formats$loader);
  103.         $this->registerJsonProblemConfiguration($errorFormats$loader);
  104.         $this->registerGraphQlConfiguration($container$config$loader);
  105.         $this->registerCacheConfiguration($container);
  106.         $this->registerDoctrineOrmConfiguration($container$config$loader);
  107.         $this->registerDoctrineMongoDbOdmConfiguration($container$config$loader);
  108.         $this->registerHttpCacheConfiguration($container$config$loader);
  109.         $this->registerValidatorConfiguration($container$config$loader);
  110.         $this->registerDataCollectorConfiguration($container$config$loader);
  111.         $this->registerMercureConfiguration($container$config$loader);
  112.         $this->registerMessengerConfiguration($container$config$loader);
  113.         $this->registerElasticsearchConfiguration($container$config$loader);
  114.         $this->registerSecurityConfiguration($container$loader);
  115.         $this->registerMakerConfiguration($container$config$loader);
  116.         $this->registerArgumentResolverConfiguration($loader);
  117.         $container->registerForAutoconfiguration(FilterInterface::class)
  118.             ->addTag('api_platform.filter');
  119.         $container->registerForAutoconfiguration(ProviderInterface::class)
  120.             ->addTag('api_platform.state_provider');
  121.         $container->registerForAutoconfiguration(ProcessorInterface::class)
  122.             ->addTag('api_platform.state_processor');
  123.         if (!$container->has('api_platform.state.item_provider')) {
  124.             $container->setAlias('api_platform.state.item_provider''api_platform.state_provider.object');
  125.         }
  126.     }
  127.     private function registerCommonConfiguration(ContainerBuilder $container, array $configXmlFileLoader $loader, array $formats, array $patchFormats, array $errorFormats): void
  128.     {
  129.         $loader->load('symfony/events.xml');
  130.         $loader->load('api.xml');
  131.         $loader->load('state.xml');
  132.         $loader->load('filter.xml');
  133.         if (class_exists(Uuid::class)) {
  134.             $loader->load('ramsey_uuid.xml');
  135.         }
  136.         if (class_exists(AbstractUid::class)) {
  137.             $loader->load('symfony/uid.xml');
  138.         }
  139.         $container->setParameter('api_platform.enable_entrypoint'$config['enable_entrypoint']);
  140.         $container->setParameter('api_platform.enable_docs'$config['enable_docs']);
  141.         $container->setParameter('api_platform.title'$config['title']);
  142.         $container->setParameter('api_platform.description'$config['description']);
  143.         $container->setParameter('api_platform.version'$config['version']);
  144.         $container->setParameter('api_platform.show_webby'$config['show_webby']);
  145.         $container->setParameter('api_platform.url_generation_strategy'$config['defaults']['url_generation_strategy'] ?? UrlGeneratorInterface::ABS_PATH);
  146.         $container->setParameter('api_platform.exception_to_status'$config['exception_to_status']);
  147.         $container->setParameter('api_platform.formats'$formats);
  148.         $container->setParameter('api_platform.patch_formats'$patchFormats);
  149.         $container->setParameter('api_platform.error_formats'$errorFormats);
  150.         $container->setParameter('api_platform.eager_loading.enabled'$this->isConfigEnabled($container$config['eager_loading']));
  151.         $container->setParameter('api_platform.eager_loading.max_joins'$config['eager_loading']['max_joins']);
  152.         $container->setParameter('api_platform.eager_loading.fetch_partial'$config['eager_loading']['fetch_partial']);
  153.         $container->setParameter('api_platform.eager_loading.force_eager'$config['eager_loading']['force_eager']);
  154.         $container->setParameter('api_platform.collection.exists_parameter_name'$config['collection']['exists_parameter_name']);
  155.         $container->setParameter('api_platform.collection.order'$config['collection']['order']);
  156.         $container->setParameter('api_platform.collection.order_parameter_name'$config['collection']['order_parameter_name']);
  157.         $container->setParameter('api_platform.collection.order_nulls_comparison'$config['collection']['order_nulls_comparison']);
  158.         $container->setParameter('api_platform.collection.pagination.enabled'$config['defaults']['pagination_enabled'] ?? true);
  159.         $container->setParameter('api_platform.collection.pagination.partial'$config['defaults']['pagination_partial'] ?? false);
  160.         $container->setParameter('api_platform.collection.pagination.client_enabled'$config['defaults']['pagination_client_enabled'] ?? false);
  161.         $container->setParameter('api_platform.collection.pagination.client_items_per_page'$config['defaults']['pagination_client_items_per_page'] ?? false);
  162.         $container->setParameter('api_platform.collection.pagination.client_partial'$config['defaults']['pagination_client_partial'] ?? false);
  163.         $container->setParameter('api_platform.collection.pagination.items_per_page'$config['defaults']['pagination_items_per_page'] ?? 30);
  164.         $container->setParameter('api_platform.collection.pagination.maximum_items_per_page'$config['defaults']['pagination_maximum_items_per_page'] ?? null);
  165.         $container->setParameter('api_platform.collection.pagination.page_parameter_name'$config['defaults']['pagination_page_parameter_name'] ?? $config['collection']['pagination']['page_parameter_name']);
  166.         $container->setParameter('api_platform.collection.pagination.enabled_parameter_name'$config['defaults']['pagination_enabled_parameter_name'] ?? $config['collection']['pagination']['enabled_parameter_name']);
  167.         $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']);
  168.         $container->setParameter('api_platform.collection.pagination.partial_parameter_name'$config['defaults']['pagination_partial_parameter_name'] ?? $config['collection']['pagination']['partial_parameter_name']);
  169.         $container->setParameter('api_platform.collection.pagination'$this->getPaginationDefaults($config['defaults'] ?? [], $config['collection']['pagination']));
  170.         $container->setParameter('api_platform.http_cache.etag'$config['defaults']['cache_headers']['etag'] ?? true);
  171.         $container->setParameter('api_platform.http_cache.max_age'$config['defaults']['cache_headers']['max_age'] ?? null);
  172.         $container->setParameter('api_platform.http_cache.shared_max_age'$config['defaults']['cache_headers']['shared_max_age'] ?? null);
  173.         $container->setParameter('api_platform.http_cache.vary'$config['defaults']['cache_headers']['vary'] ?? ['Accept']);
  174.         $container->setParameter('api_platform.http_cache.public'$config['defaults']['cache_headers']['public'] ?? $config['http_cache']['public']);
  175.         $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']);
  176.         $container->setParameter('api_platform.http_cache.invalidation.xkey.glue'$config['defaults']['cache_headers']['invalidation']['xkey']['glue'] ?? $config['http_cache']['invalidation']['xkey']['glue']);
  177.         $container->setAlias('api_platform.path_segment_name_generator'$config['path_segment_name_generator']);
  178.         if ($config['name_converter']) {
  179.             $container->setAlias('api_platform.name_converter'$config['name_converter']);
  180.         }
  181.         $container->setParameter('api_platform.asset_package'$config['asset_package']);
  182.         $container->setParameter('api_platform.defaults'$this->normalizeDefaults($config['defaults'] ?? []));
  183.     }
  184.     /**
  185.      * This method will be removed in 3.0 when "defaults" will be the regular configuration path for the pagination.
  186.      */
  187.     private function getPaginationDefaults(array $defaults, array $collectionPaginationConfiguration): array
  188.     {
  189.         $paginationOptions = [];
  190.         foreach ($defaults as $key => $value) {
  191.             if (!str_starts_with($key'pagination_')) {
  192.                 continue;
  193.             }
  194.             $paginationOptions[str_replace('pagination_'''$key)] = $value;
  195.         }
  196.         return array_merge($collectionPaginationConfiguration$paginationOptions);
  197.     }
  198.     private function normalizeDefaults(array $defaults): array
  199.     {
  200.         $normalizedDefaults = ['extra_properties' => $defaults['extra_properties'] ?? []];
  201.         unset($defaults['extra_properties']);
  202.         $rc = new \ReflectionClass(ApiResource::class);
  203.         $publicProperties = [];
  204.         foreach ($rc->getConstructor()->getParameters() as $param) {
  205.             $publicProperties[$param->getName()] = true;
  206.         }
  207.         $nameConverter = new CamelCaseToSnakeCaseNameConverter();
  208.         foreach ($defaults as $option => $value) {
  209.             if (isset($publicProperties[$nameConverter->denormalize($option)])) {
  210.                 $normalizedDefaults[$option] = $value;
  211.                 continue;
  212.             }
  213.             $normalizedDefaults['extra_properties'][$option] = $value;
  214.         }
  215.         return $normalizedDefaults;
  216.     }
  217.     private function registerMetadataConfiguration(ContainerBuilder $container, array $configXmlFileLoader $loader): void
  218.     {
  219.         [$xmlResources$yamlResources] = $this->getResourcesToWatch($container$config);
  220.         $loader->load('metadata/resource_name.xml');
  221.         $loader->load('metadata/property_name.xml');
  222.         if (!empty($config['resource_class_directories'])) {
  223.             $container->setParameter('api_platform.resource_class_directories'array_merge(
  224.                 $config['resource_class_directories'], $container->getParameter('api_platform.resource_class_directories')
  225.             ));
  226.         }
  227.         // V3 metadata
  228.         $loader->load('metadata/xml.xml');
  229.         $loader->load('metadata/links.xml');
  230.         $loader->load('metadata/property.xml');
  231.         $loader->load('metadata/resource.xml');
  232.         $loader->load('metadata/operation.xml');
  233.         $container->getDefinition('api_platform.metadata.resource_extractor.xml')->replaceArgument(0$xmlResources);
  234.         $container->getDefinition('api_platform.metadata.property_extractor.xml')->replaceArgument(0$xmlResources);
  235.         if (class_exists(PhpDocParser::class) || interface_exists(DocBlockFactoryInterface::class)) {
  236.             $loader->load('metadata/php_doc.xml');
  237.         }
  238.         if (class_exists(Yaml::class)) {
  239.             $loader->load('metadata/yaml.xml');
  240.             $container->getDefinition('api_platform.metadata.resource_extractor.yaml')->replaceArgument(0$yamlResources);
  241.             $container->getDefinition('api_platform.metadata.property_extractor.yaml')->replaceArgument(0$yamlResources);
  242.         }
  243.     }
  244.     private function getBundlesResourcesPaths(ContainerBuilder $container, array $config): array
  245.     {
  246.         $bundlesResourcesPaths = [];
  247.         foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) {
  248.             $dirname $bundle['path'];
  249.             $paths = [
  250.                 "$dirname/ApiResource",
  251.                 "$dirname/src/ApiResource",
  252.             ];
  253.             foreach (['.yaml''.yml''.xml'''] as $extension) {
  254.                 $paths[] = "$dirname/Resources/config/api_resources$extension";
  255.                 $paths[] = "$dirname/config/api_resources$extension";
  256.             }
  257.             if ($this->isConfigEnabled($container$config['doctrine'])) {
  258.                 $paths[] = "$dirname/Entity";
  259.                 $paths[] = "$dirname/src/Entity";
  260.             }
  261.             if ($this->isConfigEnabled($container$config['doctrine_mongodb_odm'])) {
  262.                 $paths[] = "$dirname/Document";
  263.                 $paths[] = "$dirname/src/Document";
  264.             }
  265.             foreach ($paths as $path) {
  266.                 if ($container->fileExists($pathfalse)) {
  267.                     $bundlesResourcesPaths[] = $path;
  268.                 }
  269.             }
  270.         }
  271.         return $bundlesResourcesPaths;
  272.     }
  273.     private function getResourcesToWatch(ContainerBuilder $container, array $config): array
  274.     {
  275.         $paths array_unique(array_merge($this->getBundlesResourcesPaths($container$config), $config['mapping']['paths']));
  276.         if (!$config['mapping']['paths']) {
  277.             $projectDir $container->getParameter('kernel.project_dir');
  278.             foreach (["$projectDir/config/api_platform""$projectDir/src/ApiResource"] as $dir) {
  279.                 if (is_dir($dir)) {
  280.                     $paths[] = $dir;
  281.                 }
  282.             }
  283.             if ($this->isConfigEnabled($container$config['doctrine']) && is_dir($doctrinePath "$projectDir/src/Entity")) {
  284.                 $paths[] = $doctrinePath;
  285.             }
  286.             if ($this->isConfigEnabled($container$config['doctrine_mongodb_odm']) && is_dir($documentPath "$projectDir/src/Document")) {
  287.                 $paths[] = $documentPath;
  288.             }
  289.         }
  290.         $resources = ['yml' => [], 'xml' => [], 'dir' => []];
  291.         foreach ($paths as $path) {
  292.             if (is_dir($path)) {
  293.                 foreach (Finder::create()->followLinks()->files()->in($path)->name('/\.(xml|ya?ml)$/')->sortByName() as $file) {
  294.                     $resources['yaml' === ($extension $file->getExtension()) ? 'yml' $extension][] = $file->getRealPath();
  295.                 }
  296.                 $resources['dir'][] = $path;
  297.                 $container->addResource(new DirectoryResource($path'/\.(xml|ya?ml|php)$/'));
  298.                 continue;
  299.             }
  300.             if ($container->fileExists($pathfalse)) {
  301.                 if (!preg_match('/\.(xml|ya?ml)$/', (string) $path$matches)) {
  302.                     throw new RuntimeException(sprintf('Unsupported mapping type in "%s", supported types are XML & YAML.'$path));
  303.                 }
  304.                 $resources['yaml' === $matches[1] ? 'yml' $matches[1]][] = $path;
  305.                 continue;
  306.             }
  307.             throw new RuntimeException(sprintf('Could not open file or directory "%s".'$path));
  308.         }
  309.         $container->setParameter('api_platform.resource_class_directories'$resources['dir']);
  310.         return [$resources['xml'], $resources['yml']];
  311.     }
  312.     private function registerOAuthConfiguration(ContainerBuilder $container, array $config): void
  313.     {
  314.         if (!$config['oauth']) {
  315.             return;
  316.         }
  317.         $container->setParameter('api_platform.oauth.enabled'$this->isConfigEnabled($container$config['oauth']));
  318.         $container->setParameter('api_platform.oauth.clientId'$config['oauth']['clientId']);
  319.         $container->setParameter('api_platform.oauth.clientSecret'$config['oauth']['clientSecret']);
  320.         $container->setParameter('api_platform.oauth.type'$config['oauth']['type']);
  321.         $container->setParameter('api_platform.oauth.flow'$config['oauth']['flow']);
  322.         $container->setParameter('api_platform.oauth.tokenUrl'$config['oauth']['tokenUrl']);
  323.         $container->setParameter('api_platform.oauth.authorizationUrl'$config['oauth']['authorizationUrl']);
  324.         $container->setParameter('api_platform.oauth.refreshUrl'$config['oauth']['refreshUrl']);
  325.         $container->setParameter('api_platform.oauth.scopes'$config['oauth']['scopes']);
  326.         $container->setParameter('api_platform.oauth.pkce'$config['oauth']['pkce']);
  327.         if ($container->hasDefinition('api_platform.swagger_ui.action')) {
  328.             $container->getDefinition('api_platform.swagger_ui.action')->setArgument(10$config['oauth']['pkce']);
  329.         }
  330.     }
  331.     /**
  332.      * Registers the Swagger, ReDoc and Swagger UI configuration.
  333.      */
  334.     private function registerSwaggerConfiguration(ContainerBuilder $container, array $configXmlFileLoader $loader): void
  335.     {
  336.         foreach (array_keys($config['swagger']['api_keys']) as $keyName) {
  337.             if (!preg_match('/^[a-zA-Z0-9._-]+$/'$keyName)) {
  338.                 trigger_deprecation('api-platform/core''3.1'sprintf('The swagger api_keys key "%s" is not valid with OpenAPI 3.1 it should match "^[a-zA-Z0-9._-]+$"'$keyName));
  339.             }
  340.         }
  341.         $container->setParameter('api_platform.swagger.versions'$config['swagger']['versions']);
  342.         if (!$config['enable_swagger'] && $config['enable_swagger_ui']) {
  343.             throw new RuntimeException('You can not enable the Swagger UI without enabling Swagger, fix this by enabling swagger via the configuration "enable_swagger: true".');
  344.         }
  345.         if (!$config['enable_swagger']) {
  346.             return;
  347.         }
  348.         $loader->load('openapi.xml');
  349.         $loader->load('swagger_ui.xml');
  350.         if (!$config['enable_swagger_ui'] && !$config['enable_re_doc']) {
  351.             // Remove the listener but keep the controller to allow customizing the path of the UI
  352.             $container->removeDefinition('api_platform.swagger.listener.ui');
  353.         }
  354.         $container->setParameter('api_platform.enable_swagger_ui'$config['enable_swagger_ui']);
  355.         $container->setParameter('api_platform.enable_re_doc'$config['enable_re_doc']);
  356.         $container->setParameter('api_platform.swagger.api_keys'$config['swagger']['api_keys']);
  357.         if ($config['openapi']['swagger_ui_extra_configuration'] && $config['swagger']['swagger_ui_extra_configuration']) {
  358.             throw new RuntimeException('You can not set "swagger_ui_extra_configuration" twice - in "openapi" and "swagger" section.');
  359.         }
  360.         $container->setParameter('api_platform.swagger_ui.extra_configuration'$config['openapi']['swagger_ui_extra_configuration'] ?: $config['swagger']['swagger_ui_extra_configuration']);
  361.     }
  362.     private function registerJsonApiConfiguration(array $formatsXmlFileLoader $loader): void
  363.     {
  364.         if (!isset($formats['jsonapi'])) {
  365.             return;
  366.         }
  367.         $loader->load('jsonapi.xml');
  368.     }
  369.     private function registerJsonLdHydraConfiguration(ContainerBuilder $container, array $formatsXmlFileLoader $loader, array $config): void
  370.     {
  371.         if (!isset($formats['jsonld'])) {
  372.             return;
  373.         }
  374.         $loader->load('jsonld.xml');
  375.         $loader->load('hydra.xml');
  376.         if (!$container->has('api_platform.json_schema.schema_factory')) {
  377.             $container->removeDefinition('api_platform.hydra.json_schema.schema_factory');
  378.         }
  379.         if (!$config['enable_docs']) {
  380.             $container->removeDefinition('api_platform.hydra.listener.response.add_link_header');
  381.         }
  382.     }
  383.     private function registerJsonHalConfiguration(array $formatsXmlFileLoader $loader): void
  384.     {
  385.         if (!isset($formats['jsonhal'])) {
  386.             return;
  387.         }
  388.         $loader->load('hal.xml');
  389.     }
  390.     private function registerJsonProblemConfiguration(array $errorFormatsXmlFileLoader $loader): void
  391.     {
  392.         if (!isset($errorFormats['jsonproblem'])) {
  393.             return;
  394.         }
  395.         $loader->load('problem.xml');
  396.     }
  397.     private function registerGraphQlConfiguration(ContainerBuilder $container, array $configXmlFileLoader $loader): void
  398.     {
  399.         $enabled $this->isConfigEnabled($container$config['graphql']);
  400.         $graphiqlEnabled $enabled && $this->isConfigEnabled($container$config['graphql']['graphiql']);
  401.         $graphqlPlayGroundEnabled $enabled && $this->isConfigEnabled($container$config['graphql']['graphql_playground']);
  402.         if ($graphqlPlayGroundEnabled) {
  403.             trigger_deprecation('api-platform/core''3.1''GraphQL Playground is deprecated and will be removed in API Platform 4.0. Only GraphiQL will be available in the future. Set api_platform.graphql.graphql_playground to false in the configuration to remove this deprecation.');
  404.         }
  405.         $container->setParameter('api_platform.graphql.enabled'$enabled);
  406.         $container->setParameter('api_platform.graphql.graphiql.enabled'$graphiqlEnabled);
  407.         $container->setParameter('api_platform.graphql.graphql_playground.enabled'$graphqlPlayGroundEnabled);
  408.         $container->setParameter('api_platform.graphql.collection.pagination'$config['graphql']['collection']['pagination']);
  409.         if (!$enabled) {
  410.             return;
  411.         }
  412.         $container->setParameter('api_platform.graphql.default_ide'$config['graphql']['default_ide']);
  413.         $container->setParameter('api_platform.graphql.nesting_separator'$config['graphql']['nesting_separator']);
  414.         $loader->load('graphql.xml');
  415.         // @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
  416.         if (!class_exists(Environment::class) || !isset($container->getParameter('kernel.bundles')['TwigBundle'])) {
  417.             if ($graphiqlEnabled || $graphqlPlayGroundEnabled) {
  418.                 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')));
  419.             }
  420.             $container->removeDefinition('api_platform.graphql.action.graphiql');
  421.             $container->removeDefinition('api_platform.graphql.action.graphql_playground');
  422.         }
  423.         $container->registerForAutoconfiguration(QueryItemResolverInterface::class)
  424.             ->addTag('api_platform.graphql.query_resolver');
  425.         $container->registerForAutoconfiguration(QueryCollectionResolverInterface::class)
  426.             ->addTag('api_platform.graphql.query_resolver');
  427.         $container->registerForAutoconfiguration(MutationResolverInterface::class)
  428.             ->addTag('api_platform.graphql.mutation_resolver');
  429.         $container->registerForAutoconfiguration(GraphQlTypeInterface::class)
  430.             ->addTag('api_platform.graphql.type');
  431.         $container->registerForAutoconfiguration(ErrorHandlerInterface::class)
  432.             ->addTag('api_platform.graphql.error_handler');
  433.         if (!$container->getParameter('kernel.debug')) {
  434.             return;
  435.         }
  436.         $requestStack = new Reference('request_stack'ContainerInterface::NULL_ON_INVALID_REFERENCE);
  437.         $collectionDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
  438.             ->setDecoratedService('api_platform.graphql.resolver.factory.collection')
  439.             ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.collection.inner'), $requestStack]);
  440.         $itemDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
  441.             ->setDecoratedService('api_platform.graphql.resolver.factory.item')
  442.             ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.item.inner'), $requestStack]);
  443.         $itemMutationDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
  444.             ->setDecoratedService('api_platform.graphql.resolver.factory.item_mutation')
  445.             ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.item_mutation.inner'), $requestStack]);
  446.         $itemSubscriptionDataCollectorResolverFactory = (new Definition(DataCollectorResolverFactory::class))
  447.             ->setDecoratedService('api_platform.graphql.resolver.factory.item_subscription')
  448.             ->setArguments([new Reference('api_platform.graphql.data_collector.resolver.factory.item_subscription.inner'), $requestStack]);
  449.         $container->addDefinitions([
  450.             'api_platform.graphql.data_collector.resolver.factory.collection' => $collectionDataCollectorResolverFactory,
  451.             'api_platform.graphql.data_collector.resolver.factory.item' => $itemDataCollectorResolverFactory,
  452.             'api_platform.graphql.data_collector.resolver.factory.item_mutation' => $itemMutationDataCollectorResolverFactory,
  453.             'api_platform.graphql.data_collector.resolver.factory.item_subscription' => $itemSubscriptionDataCollectorResolverFactory,
  454.         ]);
  455.     }
  456.     private function registerCacheConfiguration(ContainerBuilder $container): void
  457.     {
  458.         if (!$container->hasParameter('kernel.debug') || !$container->getParameter('kernel.debug')) {
  459.             $container->removeDefinition('api_platform.cache_warmer.cache_pool_clearer');
  460.         }
  461.     }
  462.     private function registerDoctrineOrmConfiguration(ContainerBuilder $container, array $configXmlFileLoader $loader): void
  463.     {
  464.         if (!$this->isConfigEnabled($container$config['doctrine'])) {
  465.             return;
  466.         }
  467.         // For older versions of doctrine bridge this allows autoconfiguration for filters
  468.         if (!$container->has(ManagerRegistry::class)) {
  469.             $container->setAlias(ManagerRegistry::class, 'doctrine');
  470.         }
  471.         $container->registerForAutoconfiguration(QueryItemExtensionInterface::class)
  472.             ->addTag('api_platform.doctrine.orm.query_extension.item');
  473.         $container->registerForAutoconfiguration(DoctrineQueryCollectionExtensionInterface::class)
  474.             ->addTag('api_platform.doctrine.orm.query_extension.collection');
  475.         $container->registerForAutoconfiguration(DoctrineOrmAbstractFilter::class);
  476.         $loader->load('doctrine_orm.xml');
  477.         if ($this->isConfigEnabled($container$config['eager_loading'])) {
  478.             return;
  479.         }
  480.         $container->removeAlias(EagerLoadingExtension::class);
  481.         $container->removeDefinition('api_platform.doctrine.orm.query_extension.eager_loading');
  482.         $container->removeAlias(FilterEagerLoadingExtension::class);
  483.         $container->removeDefinition('api_platform.doctrine.orm.query_extension.filter_eager_loading');
  484.     }
  485.     private function registerDoctrineMongoDbOdmConfiguration(ContainerBuilder $container, array $configXmlFileLoader $loader): void
  486.     {
  487.         if (!$this->isConfigEnabled($container$config['doctrine_mongodb_odm'])) {
  488.             return;
  489.         }
  490.         $container->registerForAutoconfiguration(AggregationItemExtensionInterface::class)
  491.             ->addTag('api_platform.doctrine_mongodb.odm.aggregation_extension.item');
  492.         $container->registerForAutoconfiguration(AggregationCollectionExtensionInterface::class)
  493.             ->addTag('api_platform.doctrine_mongodb.odm.aggregation_extension.collection');
  494.         $container->registerForAutoconfiguration(DoctrineMongoDbOdmAbstractFilter::class)
  495.             ->setBindings(['$managerRegistry' => new Reference('doctrine_mongodb')]);
  496.         $loader->load('doctrine_mongodb_odm.xml');
  497.     }
  498.     private function registerHttpCacheConfiguration(ContainerBuilder $container, array $configXmlFileLoader $loader): void
  499.     {
  500.         $loader->load('http_cache.xml');
  501.         if (!$this->isConfigEnabled($container$config['http_cache']['invalidation'])) {
  502.             return;
  503.         }
  504.         if ($this->isConfigEnabled($container$config['doctrine'])) {
  505.             $loader->load('doctrine_orm_http_cache_purger.xml');
  506.         }
  507.         $loader->load('http_cache_purger.xml');
  508.         foreach ($config['http_cache']['invalidation']['scoped_clients'] as $client) {
  509.             $definition $container->getDefinition($client);
  510.             $definition->addTag('api_platform.http_cache.http_client');
  511.         }
  512.         if (!($urls $config['http_cache']['invalidation']['urls'])) {
  513.             $urls $config['http_cache']['invalidation']['varnish_urls'];
  514.         }
  515.         foreach ($urls as $key => $url) {
  516.             $definition = new Definition(ScopingHttpClient::class, [new Reference('http_client'), $url, ['base_uri' => $url] + $config['http_cache']['invalidation']['request_options']]);
  517.             $definition->setFactory([ScopingHttpClient::class, 'forBaseUri']);
  518.             $definition->addTag('api_platform.http_cache.http_client');
  519.             $container->setDefinition('api_platform.invalidation_http_client.'.$key$definition);
  520.         }
  521.         $serviceName $config['http_cache']['invalidation']['purger'];
  522.         if (!$container->hasDefinition('api_platform.http_cache.purger')) {
  523.             $container->setAlias('api_platform.http_cache.purger'$serviceName);
  524.         }
  525.     }
  526.     /**
  527.      * Normalizes the format from config to the one accepted by Symfony HttpFoundation.
  528.      */
  529.     private function getFormats(array $configFormats): array
  530.     {
  531.         $formats = [];
  532.         foreach ($configFormats as $format => $value) {
  533.             foreach ($value['mime_types'] as $mimeType) {
  534.                 $formats[$format][] = $mimeType;
  535.             }
  536.         }
  537.         return $formats;
  538.     }
  539.     private function registerValidatorConfiguration(ContainerBuilder $container, array $configXmlFileLoader $loader): void
  540.     {
  541.         if (interface_exists(ValidatorInterface::class)) {
  542.             $loader->load('metadata/validator.xml');
  543.             $loader->load('symfony/validator.xml');
  544.             $container->registerForAutoconfiguration(ValidationGroupsGeneratorInterface::class)
  545.                 ->addTag('api_platform.validation_groups_generator');
  546.             $container->registerForAutoconfiguration(PropertySchemaRestrictionMetadataInterface::class)
  547.                 ->addTag('api_platform.metadata.property_schema_restriction');
  548.         }
  549.         if (!$config['validator']) {
  550.             return;
  551.         }
  552.         $container->setParameter('api_platform.validator.serialize_payload_fields'$config['validator']['serialize_payload_fields']);
  553.         $container->setParameter('api_platform.validator.query_parameter_validation'$config['validator']['query_parameter_validation']);
  554.         if (!$config['validator']['query_parameter_validation']) {
  555.             $container->removeDefinition('api_platform.listener.view.validate_query_parameters');
  556.             $container->removeDefinition('api_platform.validator.query_parameter_validator');
  557.         }
  558.     }
  559.     private function registerDataCollectorConfiguration(ContainerBuilder $container, array $configXmlFileLoader $loader): void
  560.     {
  561.         if (!$config['enable_profiler']) {
  562.             return;
  563.         }
  564.         $loader->load('data_collector.xml');
  565.         if ($container->hasParameter('kernel.debug') && $container->getParameter('kernel.debug')) {
  566.             $loader->load('debug.xml');
  567.         }
  568.     }
  569.     private function registerMercureConfiguration(ContainerBuilder $container, array $configXmlFileLoader $loader): void
  570.     {
  571.         if (!$this->isConfigEnabled($container$config['mercure'])) {
  572.             return;
  573.         }
  574.         $container->setParameter('api_platform.mercure.include_type'$config['mercure']['include_type']);
  575.         $loader->load('mercure.xml');
  576.         if ($this->isConfigEnabled($container$config['doctrine'])) {
  577.             $loader->load('doctrine_orm_mercure_publisher.xml');
  578.         }
  579.         if ($this->isConfigEnabled($container$config['doctrine_mongodb_odm'])) {
  580.             $loader->load('doctrine_odm_mercure_publisher.xml');
  581.         }
  582.         if ($this->isConfigEnabled($container$config['graphql'])) {
  583.             $loader->load('graphql_mercure.xml');
  584.         }
  585.     }
  586.     private function registerMessengerConfiguration(ContainerBuilder $container, array $configXmlFileLoader $loader): void
  587.     {
  588.         if (!$this->isConfigEnabled($container$config['messenger'])) {
  589.             return;
  590.         }
  591.         $loader->load('messenger.xml');
  592.     }
  593.     private function registerElasticsearchConfiguration(ContainerBuilder $container, array $configXmlFileLoader $loader): void
  594.     {
  595.         $enabled $this->isConfigEnabled($container$config['elasticsearch']);
  596.         $container->setParameter('api_platform.elasticsearch.enabled'$enabled);
  597.         if (!$enabled) {
  598.             return;
  599.         }
  600.         $loader->load('elasticsearch.xml');
  601.         $container->registerForAutoconfiguration(RequestBodySearchCollectionExtensionInterface::class)
  602.             ->addTag('api_platform.elasticsearch.request_body_search_extension.collection');
  603.         $container->setParameter('api_platform.elasticsearch.hosts'$config['elasticsearch']['hosts']);
  604.         $container->setParameter('api_platform.elasticsearch.mapping'$config['elasticsearch']['mapping']);
  605.     }
  606.     private function registerSecurityConfiguration(ContainerBuilder $containerXmlFileLoader $loader): void
  607.     {
  608.         /** @var string[] $bundles */
  609.         $bundles $container->getParameter('kernel.bundles');
  610.         if (!isset($bundles['SecurityBundle'])) {
  611.             return;
  612.         }
  613.         $loader->load('security.xml');
  614.     }
  615.     private function registerOpenApiConfiguration(ContainerBuilder $container, array $configXmlFileLoader $loader): void
  616.     {
  617.         $container->setParameter('api_platform.openapi.termsOfService'$config['openapi']['termsOfService']);
  618.         $container->setParameter('api_platform.openapi.contact.name'$config['openapi']['contact']['name']);
  619.         $container->setParameter('api_platform.openapi.contact.url'$config['openapi']['contact']['url']);
  620.         $container->setParameter('api_platform.openapi.contact.email'$config['openapi']['contact']['email']);
  621.         $container->setParameter('api_platform.openapi.license.name'$config['openapi']['license']['name']);
  622.         $container->setParameter('api_platform.openapi.license.url'$config['openapi']['license']['url']);
  623.         $loader->load('json_schema.xml');
  624.     }
  625.     private function registerMakerConfiguration(ContainerBuilder $container, array $configXmlFileLoader $loader): void
  626.     {
  627.         if (!$this->isConfigEnabled($container$config['maker'])) {
  628.             return;
  629.         }
  630.         $loader->load('maker.xml');
  631.     }
  632.     private function registerArgumentResolverConfiguration(XmlFileLoader $loader): void
  633.     {
  634.         $loader->load('argument_resolver.xml');
  635.     }
  636. }