Completed
Push — master ( 29b346...1faa7f )
by Amrouche
19s
created

DependencyInjection/ApiPlatformExtension.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <[email protected]>
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\Core\Bridge\Symfony\Bundle\DependencyInjection;
15
16
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryCollectionExtensionInterface;
17
use ApiPlatform\Core\Bridge\Doctrine\Orm\Extension\QueryItemExtensionInterface;
18
use ApiPlatform\Core\DataProvider\CollectionDataProviderInterface;
19
use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
20
use ApiPlatform\Core\Exception\RuntimeException;
21
use Doctrine\Common\Annotations\Annotation;
22
use Doctrine\ORM\Version;
23
use phpDocumentor\Reflection\DocBlockFactoryInterface;
24
use Symfony\Component\Cache\Adapter\ArrayAdapter;
25
use Symfony\Component\Config\FileLocator;
26
use Symfony\Component\Config\Resource\DirectoryResource;
27
use Symfony\Component\DependencyInjection\ChildDefinition;
28
use Symfony\Component\DependencyInjection\ContainerBuilder;
29
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
30
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
31
use Symfony\Component\DependencyInjection\Reference;
32
use Symfony\Component\Finder\Finder;
33
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
34
use Symfony\Component\Validator\Validator\ValidatorInterface;
35
use Symfony\Component\Yaml\Yaml;
36
37
/**
38
 * The extension of this bundle.
39
 *
40
 * @author Kévin Dunglas <[email protected]>
41
 */
42
final class ApiPlatformExtension extends Extension implements PrependExtensionInterface
43
{
44
    /**
45
     * {@inheritdoc}
46
     */
47
    public function prepend(ContainerBuilder $container)
48
    {
49
        if (!$frameworkConfiguration = $container->getExtensionConfig('framework')) {
50
            return;
51
        }
52
53
        foreach ($frameworkConfiguration as $frameworkParameters) {
54
            if (isset($frameworkParameters['serializer'])) {
55
                $serializerConfig = $serializerConfig ?? $frameworkParameters['serializer'];
56
            }
57
58
            if (isset($frameworkParameters['property_info'])) {
59
                $propertyInfoConfig = $propertyInfoConfig ?? $frameworkParameters['property_info'];
60
            }
61
        }
62
63 View Code Duplication
        if (!isset($serializerConfig['enabled'])) {
64
            $container->prependExtensionConfig('framework', ['serializer' => ['enabled' => true]]);
65
        }
66
67 View Code Duplication
        if (!isset($propertyInfoConfig['enabled'])) {
68
            $container->prependExtensionConfig('framework', ['property_info' => ['enabled' => true]]);
69
        }
70
71
        if (isset($serializerConfig['name_converter'])) {
72
            $container->prependExtensionConfig('api_platform', ['name_converter' => $serializerConfig['name_converter']]);
73
        }
74
    }
75
76
    /**
77
     * {@inheritdoc}
78
     */
79
    public function load(array $configs, ContainerBuilder $container)
80
    {
81
        $configuration = new Configuration();
82
        $config = $this->processConfiguration($configuration, $configs);
83
        $formats = $this->getFormats($config['formats']);
84
        $errorFormats = $this->getFormats($config['error_formats']);
85
        $this->handleConfig($container, $config, $formats, $errorFormats);
86
87
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
88
        $loader->load('api.xml');
89
        $loader->load('data_provider.xml');
90
        $loader->load('filter.xml');
91
92
        $container->registerForAutoconfiguration(ItemDataProviderInterface::class)
93
            ->addTag('api_platform.item_data_provider');
94
        $container->registerForAutoconfiguration(CollectionDataProviderInterface::class)
95
            ->addTag('api_platform.collection_data_provider');
96
        $container->registerForAutoconfiguration(QueryItemExtensionInterface::class)
97
            ->addTag('api_platform.doctrine.orm.query_extension.item');
98
        $container->registerForAutoconfiguration(QueryCollectionExtensionInterface::class)
99
            ->addTag('api_platform.doctrine.orm.query_extension.collection');
100
101
        if (interface_exists(ValidatorInterface::class)) {
102
            $loader->load('validator.xml');
103
        }
104
105
        $bundles = $container->getParameter('kernel.bundles');
106
        if (isset($bundles['SecurityBundle'])) {
107
            $loader->load('security.xml');
108
        }
109
110
        $useDoctrine = isset($bundles['DoctrineBundle']) && class_exists(Version::class);
111
112
        $this->registerMetadataConfiguration($container, $config, $loader);
113
        $this->registerOAuthConfiguration($container, $config, $loader);
114
        $this->registerApiKeysConfiguration($container, $config, $loader);
115
        $this->registerSwaggerConfiguration($container, $config, $loader);
116
        $this->registerJsonLdConfiguration($formats, $loader);
117
        $this->registerJsonHalConfiguration($formats, $loader);
118
        $this->registerJsonProblemConfiguration($errorFormats, $loader);
119
        $this->registerBundlesConfiguration($bundles, $config, $loader, $useDoctrine);
120
        $this->registerCacheConfiguration($container);
121
        $this->registerDoctrineExtensionConfiguration($container, $config, $useDoctrine);
122
        $this->registerHttpCache($container, $config, $loader, $useDoctrine);
123
    }
124
125
    /**
126
     * Handles configuration.
127
     *
128
     * @param ContainerBuilder $container
129
     * @param array            $config
130
     * @param array            $formats
131
     * @param array            $errorFormats
132
     */
133
    private function handleConfig(ContainerBuilder $container, array $config, array $formats, array $errorFormats)
134
    {
135
        $container->setParameter('api_platform.title', $config['title']);
136
        $container->setParameter('api_platform.description', $config['description']);
137
        $container->setParameter('api_platform.version', $config['version']);
138
        $container->setParameter('api_platform.exception_to_status', $config['exception_to_status']);
139
        $container->setParameter('api_platform.formats', $formats);
140
        $container->setParameter('api_platform.error_formats', $errorFormats);
141
        $container->setParameter('api_platform.eager_loading.enabled', $config['eager_loading']['enabled']);
142
        $container->setParameter('api_platform.eager_loading.max_joins', $config['eager_loading']['max_joins']);
143
        $container->setParameter('api_platform.eager_loading.fetch_partial', $config['eager_loading']['fetch_partial']);
144
        $container->setParameter('api_platform.eager_loading.force_eager', $config['eager_loading']['force_eager']);
145
        $container->setParameter('api_platform.collection.order', $config['collection']['order']);
146
        $container->setParameter('api_platform.collection.order_parameter_name', $config['collection']['order_parameter_name']);
147
        $container->setParameter('api_platform.collection.pagination.enabled', $config['collection']['pagination']['enabled']);
148
        $container->setParameter('api_platform.collection.pagination.client_enabled', $config['collection']['pagination']['client_enabled']);
149
        $container->setParameter('api_platform.collection.pagination.client_items_per_page', $config['collection']['pagination']['client_items_per_page']);
150
        $container->setParameter('api_platform.collection.pagination.items_per_page', $config['collection']['pagination']['items_per_page']);
151
        $container->setParameter('api_platform.collection.pagination.maximum_items_per_page', $config['collection']['pagination']['maximum_items_per_page']);
152
        $container->setParameter('api_platform.collection.pagination.page_parameter_name', $config['collection']['pagination']['page_parameter_name']);
153
        $container->setParameter('api_platform.collection.pagination.enabled_parameter_name', $config['collection']['pagination']['enabled_parameter_name']);
154
        $container->setParameter('api_platform.collection.pagination.items_per_page_parameter_name', $config['collection']['pagination']['items_per_page_parameter_name']);
155
        $container->setParameter('api_platform.http_cache.etag', $config['http_cache']['etag']);
156
        $container->setParameter('api_platform.http_cache.max_age', $config['http_cache']['max_age']);
157
        $container->setParameter('api_platform.http_cache.shared_max_age', $config['http_cache']['shared_max_age']);
158
        $container->setParameter('api_platform.http_cache.vary', $config['http_cache']['vary']);
159
        $container->setParameter('api_platform.http_cache.public', $config['http_cache']['public']);
160
161
        $container->setAlias('api_platform.operation_path_resolver.default', $config['default_operation_path_resolver']);
162
163
        if ($config['name_converter']) {
164
            $container->setAlias('api_platform.name_converter', $config['name_converter']);
165
        }
166
    }
167
168
    /**
169
     * Registers metadata configuration.
170
     *
171
     * @param ContainerBuilder $container
172
     * @param array            $config
173
     * @param XmlFileLoader    $loader
174
     */
175
    private function registerMetadataConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader)
176
    {
177
        $loader->load('metadata/metadata.xml');
178
        $loader->load('metadata/xml.xml');
179
180
        list($xmlResources, $yamlResources) = $this->getResourcesToWatch($container, $config['mapping']['paths']);
181
182
        $container->getDefinition('api_platform.metadata.extractor.xml')->addArgument($xmlResources);
183
184
        if (class_exists(Annotation::class)) {
185
            $loader->load('metadata/annotation.xml');
186
        }
187
188
        if (class_exists(Yaml::class)) {
189
            $loader->load('metadata/yaml.xml');
190
            $container->getDefinition('api_platform.metadata.extractor.yaml')->addArgument($yamlResources);
191
        }
192
193
        if (interface_exists(DocBlockFactoryInterface::class)) {
194
            $loader->load('metadata/php_doc.xml');
195
        }
196
    }
197
198
    private function getBundlesResourcesPaths(ContainerBuilder $container): array
199
    {
200
        $bundlesResourcesPaths = [];
201
202
        foreach ($container->getParameter('kernel.bundles_metadata') as $bundle) {
203
            $dirname = $bundle['path'];
204
205
            foreach (['.yaml', '.yml', '.xml', ''] as $extension) {
206
                $paths[] = $dirname.'/Resources/config/api_resources'.$extension;
207
            }
208
209
            $paths[] = $dirname.'/Entity';
210
211
            foreach ($paths as $path) {
212
                if ($container->fileExists($path, false)) {
213
                    $bundlesResourcesPaths[] = $path;
214
                }
215
            }
216
        }
217
218
        return $bundlesResourcesPaths;
219
    }
220
221
    private function getResourcesToWatch(ContainerBuilder $container, array $resourcesPaths): array
222
    {
223
        $paths = array_unique(array_merge($resourcesPaths, $this->getBundlesResourcesPaths($container)));
224
        $resources = ['yml' => [], 'xml' => [], 'dir' => []];
225
226
        foreach ($paths as $path) {
227
            if (is_dir($path)) {
228
                foreach (Finder::create()->followLinks()->files()->in($path)->name('/\.(xml|ya?ml)$/') as $file) {
229
                    $resources['yaml' === ($extension = $file->getExtension()) ? 'yml' : $extension][] = $file->getRealPath();
230
                }
231
232
                $resources['dir'][] = $path;
233
                $container->addResource(new DirectoryResource($path, '/\.(xml|ya?ml|php)$/'));
234
            } elseif ($container->fileExists($path, false)) {
235
                if (!preg_match('/\.(xml|ya?ml)$/', $path, $matches)) {
236
                    throw new RuntimeException(sprintf('Unsupported mapping type in "%s", supported types are XML & Yaml.', $path));
237
                }
238
239
                $resources['yaml' === $matches[1] ? 'yml' : $matches[1]][] = $path;
240
            } else {
241
                throw new RuntimeException(sprintf('Could not open file or directory "%s".', $path));
242
            }
243
        }
244
245
        $container->setParameter('api_platform.resource_class_directories', $resources['dir']);
246
247
        return [$resources['xml'], $resources['yml']];
248
    }
249
250
    /**
251
     * Registers the OAuth configuration.
252
     *
253
     * @param ContainerBuilder $container
254
     * @param array            $config
255
     * @param XmlFileLoader    $loader
256
     */
257
    private function registerOAuthConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader)
0 ignored issues
show
The parameter $loader is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
258
    {
259
        if (!$config['oauth']) {
260
            return;
261
        }
262
263
        $container->setParameter('api_platform.oauth.enabled', $config['oauth']['enabled']);
264
        $container->setParameter('api_platform.oauth.clientId', $config['oauth']['clientId']);
265
        $container->setParameter('api_platform.oauth.clientSecret', $config['oauth']['clientSecret']);
266
        $container->setParameter('api_platform.oauth.type', $config['oauth']['type']);
267
        $container->setParameter('api_platform.oauth.flow', $config['oauth']['flow']);
268
        $container->setParameter('api_platform.oauth.tokenUrl', $config['oauth']['tokenUrl']);
269
        $container->setParameter('api_platform.oauth.authorizationUrl', $config['oauth']['authorizationUrl']);
270
        $container->setParameter('api_platform.oauth.scopes', $config['oauth']['scopes']);
271
    }
272
273
    /**
274
     * Registers the api keys configuration.
275
     *
276
     * @param ContainerBuilder $container
277
     * @param array            $config
278
     * @param XmlFileLoader    $loader
279
     */
280
    private function registerApiKeysConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader)
0 ignored issues
show
The parameter $loader is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
281
    {
282
        $container->setParameter('api_platform.swagger.api_keys', $config['swagger']['api_keys']);
283
    }
284
285
    /**
286
     * Registers the Swagger and Swagger UI configuration.
287
     *
288
     * @param ContainerBuilder $container
289
     * @param array            $config
290
     * @param XmlFileLoader    $loader
291
     */
292
    private function registerSwaggerConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader)
293
    {
294
        if (!$config['enable_swagger']) {
295
            return;
296
        }
297
298
        $loader->load('swagger.xml');
299
300
        if ($config['enable_swagger_ui']) {
301
            $loader->load('swagger-ui.xml');
302
            $container->setParameter('api_platform.enable_swagger_ui', $config['enable_swagger_ui']);
303
        }
304
305
        $container->setParameter('api_platform.enable_swagger', $config['enable_swagger']);
306
    }
307
308
    /**
309
     * Registers the JSON-LD and Hydra configuration.
310
     *
311
     * @param array         $formats
312
     * @param XmlFileLoader $loader
313
     */
314
    private function registerJsonLdConfiguration(array $formats, XmlFileLoader $loader)
315
    {
316
        if (!isset($formats['jsonld'])) {
317
            return;
318
        }
319
320
        $loader->load('jsonld.xml');
321
        $loader->load('hydra.xml');
322
    }
323
324
    /**
325
     * Registers the HAL configuration.
326
     *
327
     * @param array         $formats
328
     * @param XmlFileLoader $loader
329
     */
330
    private function registerJsonHalConfiguration(array $formats, XmlFileLoader $loader)
331
    {
332
        if (!isset($formats['jsonhal'])) {
333
            return;
334
        }
335
336
        $loader->load('hal.xml');
337
    }
338
339
    /**
340
     * Registers the JSON Problem configuration.
341
     *
342
     * @param array         $errorFormats
343
     * @param XmlFileLoader $loader
344
     */
345
    private function registerJsonProblemConfiguration(array $errorFormats, XmlFileLoader $loader)
346
    {
347
        if (!isset($errorFormats['jsonproblem'])) {
348
            return;
349
        }
350
351
        $loader->load('problem.xml');
352
    }
353
354
    /**
355
     * Registers configuration for integration with third-party bundles.
356
     *
357
     * @param string[]      $bundles
358
     * @param array         $config
359
     * @param XmlFileLoader $loader
360
     * @param bool          $useDoctrine
361
     */
362
    private function registerBundlesConfiguration(array $bundles, array $config, XmlFileLoader $loader, bool $useDoctrine)
363
    {
364
        // Doctrine ORM support
365
        if ($useDoctrine) {
366
            $loader->load('doctrine_orm.xml');
367
        }
368
369
        // FOSUser support
370
        if (isset($bundles['FOSUserBundle']) && $config['enable_fos_user']) {
371
            $loader->load('fos_user.xml');
372
        }
373
374
        // NelmioApiDoc support
375
        if (isset($bundles['NelmioApiDocBundle']) && $config['enable_nelmio_api_doc']) {
376
            $loader->load('nelmio_api_doc.xml');
377
        }
378
    }
379
380
    /**
381
     * Registers the cache configuration.
382
     *
383
     * @param ContainerBuilder $container
384
     */
385
    private function registerCacheConfiguration(ContainerBuilder $container)
386
    {
387
        // Don't use system cache pool in dev
388
        if (!$container->getParameter('kernel.debug')) {
389
            return;
390
        }
391
392
        $container->register('api_platform.cache.metadata.property', ArrayAdapter::class);
393
        $container->register('api_platform.cache.metadata.resource', ArrayAdapter::class);
394
        $container->register('api_platform.cache.route_name_resolver', ArrayAdapter::class);
395
    }
396
397
    /**
398
     * Manipulate doctrine extension services according to the configuration.
399
     *
400
     * @param ContainerBuilder $container
401
     * @param array            $config
402
     * @param bool             $useDoctrine
403
     */
404
    private function registerDoctrineExtensionConfiguration(ContainerBuilder $container, array $config, bool $useDoctrine)
405
    {
406
        if (!$useDoctrine || $config['eager_loading']['enabled']) {
407
            return;
408
        }
409
410
        $container->removeDefinition('api_platform.doctrine.orm.query_extension.eager_loading');
411
        $container->removeDefinition('api_platform.doctrine.orm.query_extension.filter_eager_loading');
412
    }
413
414
    private function registerHttpCache(ContainerBuilder $container, array $config, XmlFileLoader $loader, bool $useDoctrine)
415
    {
416
        $loader->load('http_cache.xml');
417
418
        if (!$config['http_cache']['invalidation']['enabled']) {
419
            return;
420
        }
421
422
        if ($useDoctrine) {
423
            $loader->load('doctrine_orm_http_cache_purger.xml');
424
        }
425
426
        $loader->load('http_cache_tags.xml');
427
428
        if (!$config['http_cache']['invalidation']['varnish_urls']) {
429
            return;
430
        }
431
432
        $references = [];
433
        foreach ($config['http_cache']['invalidation']['varnish_urls'] as $url) {
434
            $id = sprintf('api_platform.http_cache.purger.varnish_client.%s', $url);
435
            $references[] = new Reference($id);
436
437
            $definition = new ChildDefinition('api_platform.http_cache.purger.varnish_client');
438
            $definition->addArgument(['base_uri' => $url]);
439
            $container->setDefinition($id, $definition);
440
        }
441
442
        $container->getDefinition('api_platform.http_cache.purger.varnish')->addArgument($references);
443
        $container->setAlias('api_platform.http_cache.purger', 'api_platform.http_cache.purger.varnish');
444
    }
445
446
    /**
447
     * Normalizes the format from config to the one accepted by Symfony HttpFoundation.
448
     *
449
     * @param array $configFormats
450
     *
451
     * @return array
452
     */
453
    private function getFormats(array $configFormats): array
454
    {
455
        $formats = [];
456
        foreach ($configFormats as $format => $value) {
457
            foreach ($value['mime_types'] as $mimeType) {
458
                $formats[$format][] = $mimeType;
459
            }
460
        }
461
462
        return $formats;
463
    }
464
}
465