Completed
Push — master ( 219da4...020dbf )
by Kévin
06:06
created

ApiPlatformExtension::getResourcesToWatch()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 9.0856
c 0
b 0
f 0
cc 3
eloc 14
nc 3
nop 3
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 Doctrine\Common\Annotations\Annotation;
17
use Doctrine\ORM\Version;
18
use phpDocumentor\Reflection\DocBlockFactoryInterface;
19
use Symfony\Component\Cache\Adapter\ArrayAdapter;
20
use Symfony\Component\Config\FileLocator;
21
use Symfony\Component\Config\Resource\DirectoryResource;
22
use Symfony\Component\DependencyInjection\ContainerBuilder;
23
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
24
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
25
use Symfony\Component\Finder\Finder;
26
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
27
use Symfony\Component\Validator\Validator\ValidatorInterface;
28
use Symfony\Component\Yaml\Yaml;
29
30
/**
31
 * The extension of this bundle.
32
 *
33
 * @author Kévin Dunglas <[email protected]>
34
 */
35
final class ApiPlatformExtension extends Extension implements PrependExtensionInterface
36
{
37
    /**
38
     * {@inheritdoc}
39
     */
40
    public function prepend(ContainerBuilder $container)
41
    {
42
        if (!$frameworkConfiguration = $container->getExtensionConfig('framework')) {
43
            return;
44
        }
45
46
        foreach ($frameworkConfiguration as $frameworkParameters) {
47
            if (isset($frameworkParameters['serializer'])) {
48
                $serializerConfig = $serializerConfig ?? $frameworkParameters['serializer'];
49
            }
50
51
            if (isset($frameworkParameters['property_info'])) {
52
                $propertyInfoConfig = $propertyInfoConfig ?? $frameworkParameters['property_info'];
53
            }
54
        }
55
56 View Code Duplication
        if (!isset($serializerConfig['enabled'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
57
            $container->prependExtensionConfig('framework', ['serializer' => ['enabled' => true]]);
58
        }
59
60 View Code Duplication
        if (!isset($propertyInfoConfig['enabled'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
61
            $container->prependExtensionConfig('framework', ['property_info' => ['enabled' => true]]);
62
        }
63
    }
64
65
    /**
66
     * {@inheritdoc}
67
     */
68
    public function load(array $configs, ContainerBuilder $container)
69
    {
70
        $configuration = new Configuration();
71
        $config = $this->processConfiguration($configuration, $configs);
72
        $formats = $this->getFormats($config['formats']);
73
        $errorFormats = $this->getFormats($config['error_formats']);
74
        $this->handleConfig($container, $config, $formats, $errorFormats);
75
76
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
77
        $loader->load('api.xml');
78
        $loader->load('data_provider.xml');
79
        if (interface_exists(ValidatorInterface::class)) {
80
            $loader->load('validator.xml');
81
        }
82
83
        $bundles = $container->getParameter('kernel.bundles');
84
85
        $this->registerMetadataConfiguration($container, $loader, $bundles, $config['loader_paths']);
86
        $this->registerOAuthConfiguration($container, $config, $loader);
87
        $this->registerSwaggerConfiguration($container, $config, $loader);
88
        $this->registerJsonLdConfiguration($formats, $loader);
89
        $this->registerJsonHalConfiguration($formats, $loader);
90
        $this->registerJsonProblemConfiguration($errorFormats, $loader);
91
        $this->registerBundlesConfiguration($bundles, $config, $loader);
92
        $this->registerCacheConfiguration($container);
93
        $this->registerDoctrineExtensionConfiguration($container, $config);
94
    }
95
96
    /**
97
     * Handles configuration.
98
     *
99
     * @param ContainerBuilder $container
100
     * @param array            $config
101
     * @param array            $formats
102
     * @param array            $errorFormats
103
     */
104
    private function handleConfig(ContainerBuilder $container, array $config, array $formats, array $errorFormats)
105
    {
106
        $container->setParameter('api_platform.title', $config['title']);
107
        $container->setParameter('api_platform.description', $config['description']);
108
        $container->setParameter('api_platform.version', $config['version']);
109
        $container->setParameter('api_platform.exception_to_status', $config['exception_to_status']);
110
        $container->setParameter('api_platform.formats', $formats);
111
        $container->setParameter('api_platform.error_formats', $errorFormats);
112
        $container->setParameter('api_platform.eager_loading.enabled', $config['eager_loading']['enabled']);
113
        $container->setParameter('api_platform.eager_loading.max_joins', $config['eager_loading']['max_joins']);
114
        $container->setParameter('api_platform.eager_loading.force_eager', $config['eager_loading']['force_eager']);
115
        $container->setParameter('api_platform.collection.order', $config['collection']['order']);
116
        $container->setParameter('api_platform.collection.order_parameter_name', $config['collection']['order_parameter_name']);
117
        $container->setParameter('api_platform.collection.pagination.enabled', $config['collection']['pagination']['enabled']);
118
        $container->setParameter('api_platform.collection.pagination.client_enabled', $config['collection']['pagination']['client_enabled']);
119
        $container->setParameter('api_platform.collection.pagination.client_items_per_page', $config['collection']['pagination']['client_items_per_page']);
120
        $container->setParameter('api_platform.collection.pagination.items_per_page', $config['collection']['pagination']['items_per_page']);
121
        $container->setParameter('api_platform.collection.pagination.maximum_items_per_page', $config['collection']['pagination']['maximum_items_per_page']);
122
        $container->setParameter('api_platform.collection.pagination.page_parameter_name', $config['collection']['pagination']['page_parameter_name']);
123
        $container->setParameter('api_platform.collection.pagination.enabled_parameter_name', $config['collection']['pagination']['enabled_parameter_name']);
124
        $container->setParameter('api_platform.collection.pagination.items_per_page_parameter_name', $config['collection']['pagination']['items_per_page_parameter_name']);
125
126
        $container->setAlias('api_platform.operation_path_resolver.default', $config['default_operation_path_resolver']);
127
128
        if ($config['name_converter']) {
129
            $container->setAlias('api_platform.name_converter', $config['name_converter']);
130
        }
131
    }
132
133
    /**
134
     * Registers metadata configuration.
135
     *
136
     * @param ContainerBuilder $container
137
     * @param XmlFileLoader    $loader
138
     * @param string[]         $bundles
139
     */
140
    private function registerMetadataConfiguration(ContainerBuilder $container, XmlFileLoader $loader, array $bundles, array $loaderPaths)
141
    {
142
        $loader->load('metadata/metadata.xml');
143
        $loader->load('metadata/xml.xml');
144
145
        list($xmlResources, $yamlResources) = $this->getResourcesToWatch($container, $bundles, $loaderPaths);
146
        $container->getDefinition('api_platform.metadata.extractor.xml')->addArgument($xmlResources);
147
148
        if (class_exists(Annotation::class)) {
149
            $loader->load('metadata/annotation.xml');
150
        }
151
152
        if (class_exists(Yaml::class)) {
153
            $loader->load('metadata/yaml.xml');
154
            $container->getDefinition('api_platform.metadata.extractor.yaml')->addArgument($yamlResources);
155
        }
156
157
        if (interface_exists(DocBlockFactoryInterface::class)) {
158
            $loader->load('metadata/php_doc.xml');
159
        }
160
    }
161
162
    private function getResourcesToWatch(ContainerBuilder $container, array $bundles, array $loaderPaths): array
163
    {
164
        $resourceClassDirectories = $loaderPaths['annotation'];
165
        $xmlResources = $loaderPaths['xml'];
166
        $yamlResources = $loaderPaths['yaml'];
167
168
        foreach ($bundles as $bundle) {
169
            $bundleDirectory = dirname((new \ReflectionClass($bundle))->getFileName());
170
            list($newXmlResources, $newYamlResources) = $this->findResources($bundleDirectory);
171
172
            $xmlResources = array_merge($xmlResources, $newXmlResources);
173
            $yamlResources = array_merge($yamlResources, $newYamlResources);
174
175
            if (file_exists($entityDirectory = $bundleDirectory.'/Entity')) {
176
                $resourceClassDirectories[] = $entityDirectory;
177
                $container->addResource(new DirectoryResource($entityDirectory, '/\.php$/'));
178
            }
179
        }
180
181
        $container->setParameter('api_platform.resource_class_directories', $resourceClassDirectories);
182
183
        return [$xmlResources, $yamlResources];
184
    }
185
186
    private function findResources(string $bundleDirectory): array
187
    {
188
        $xmlResources = $yamlResources = [];
189
190
        try {
191
            foreach (Finder::create()->files()->in($bundleDirectory.'/Resources/config/')->path('api_resources')->name('*.{yml,yaml,xml}') as $file) {
192
                if ('xml' === $file->getExtension()) {
193
                    $xmlResources[] = $file->getRealPath();
194
                } else {
195
                    $yamlResources[] = $file->getRealPath();
196
                }
197
            }
198
        } catch (\InvalidArgumentException $e) {
199
            // Ignore invalid paths
200
        }
201
202
        return [$xmlResources, $yamlResources];
203
    }
204
205
    /**
206
     * Registers the OAuth configuration.
207
     *
208
     * @param ContainerBuilder $container
209
     * @param array            $config
210
     * @param XmlFileLoader    $loader
211
     */
212
    private function registerOAuthConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader)
0 ignored issues
show
Unused Code introduced by
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...
213
    {
214
        if (!$config['oauth']) {
215
            return;
216
        }
217
218
        $container->setParameter('api_platform.oauth.enabled', $config['oauth']['enabled']);
219
        $container->setParameter('api_platform.oauth.clientId', $config['oauth']['clientId']);
220
        $container->setParameter('api_platform.oauth.clientSecret', $config['oauth']['clientSecret']);
221
        $container->setParameter('api_platform.oauth.type', $config['oauth']['type']);
222
        $container->setParameter('api_platform.oauth.flow', $config['oauth']['flow']);
223
        $container->setParameter('api_platform.oauth.tokenUrl', $config['oauth']['tokenUrl']);
224
        $container->setParameter('api_platform.oauth.authorizationUrl', $config['oauth']['authorizationUrl']);
225
        $container->setParameter('api_platform.oauth.scopes', $config['oauth']['scopes']);
226
    }
227
228
    /**
229
     * Registers the Swagger and Swagger UI configuration.
230
     *
231
     * @param ContainerBuilder $container
232
     * @param array            $config
233
     * @param XmlFileLoader    $loader
234
     */
235
    private function registerSwaggerConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader)
236
    {
237
        if (!$config['enable_swagger']) {
238
            return;
239
        }
240
241
        $loader->load('swagger.xml');
242
243
        if ($config['enable_swagger_ui']) {
244
            $loader->load('swagger-ui.xml');
245
            $container->setParameter('api_platform.enable_swagger_ui', $config['enable_swagger_ui']);
246
        }
247
248
        $container->setParameter('api_platform.enable_swagger', $config['enable_swagger']);
249
    }
250
251
    /**
252
     * Registers the JSON-LD and Hydra configuration.
253
     *
254
     * @param array         $formats
255
     * @param XmlFileLoader $loader
256
     */
257
    private function registerJsonLdConfiguration(array $formats, XmlFileLoader $loader)
258
    {
259
        if (!isset($formats['jsonld'])) {
260
            return;
261
        }
262
263
        $loader->load('jsonld.xml');
264
        $loader->load('hydra.xml');
265
    }
266
267
    /**
268
     * Registers the HAL configuration.
269
     *
270
     * @param array         $formats
271
     * @param XmlFileLoader $loader
272
     */
273
    private function registerJsonHalConfiguration(array $formats, XmlFileLoader $loader)
274
    {
275
        if (!isset($formats['jsonhal'])) {
276
            return;
277
        }
278
279
        $loader->load('hal.xml');
280
    }
281
282
    /**
283
     * Registers the JSON Problem configuration.
284
     *
285
     * @param array         $errorFormats
286
     * @param XmlFileLoader $loader
287
     */
288
    private function registerJsonProblemConfiguration(array $errorFormats, XmlFileLoader $loader)
289
    {
290
        if (!isset($errorFormats['jsonproblem'])) {
291
            return;
292
        }
293
294
        $loader->load('problem.xml');
295
    }
296
297
    /**
298
     * Registers configuration for integration with third-party bundles.
299
     *
300
     * @param string[]      $bundles
301
     * @param array         $config
302
     * @param XmlFileLoader $loader
303
     */
304
    private function registerBundlesConfiguration(array $bundles, array $config, XmlFileLoader $loader)
305
    {
306
        // Doctrine ORM support
307
        if (isset($bundles['DoctrineBundle']) && class_exists(Version::class)) {
308
            $loader->load('doctrine_orm.xml');
309
        }
310
311
        // FOSUser support
312
        if (isset($bundles['FOSUserBundle']) && $config['enable_fos_user']) {
313
            $loader->load('fos_user.xml');
314
        }
315
316
        // NelmioApiDoc support
317
        if (isset($bundles['NelmioApiDocBundle']) && $config['enable_nelmio_api_doc']) {
318
            $loader->load('nelmio_api_doc.xml');
319
        }
320
    }
321
322
    /**
323
     * Registers the cache configuration.
324
     *
325
     * @param ContainerBuilder $container
326
     */
327
    private function registerCacheConfiguration(ContainerBuilder $container)
328
    {
329
        // Don't use system cache pool in dev
330
        if (!$container->getParameter('kernel.debug')) {
331
            return;
332
        }
333
334
        $container->register('api_platform.cache.metadata.property', ArrayAdapter::class);
335
        $container->register('api_platform.cache.metadata.resource', ArrayAdapter::class);
336
        $container->register('api_platform.cache.route_name_resolver', ArrayAdapter::class);
337
    }
338
339
    /**
340
     * Manipulate doctrine extension services according to the configuration.
341
     *
342
     * @param ContainerBuilder $container
343
     * @param array            $config
344
     */
345
    private function registerDoctrineExtensionConfiguration(ContainerBuilder $container, array $config)
346
    {
347
        if (false === $config['eager_loading']['enabled']) {
348
            $container->removeDefinition('api_platform.doctrine.orm.query_extension.eager_loading');
349
            $container->removeDefinition('api_platform.doctrine.orm.query_extension.filter_eager_loading');
350
        }
351
    }
352
353
    /**
354
     * Normalizes the format from config to the one accepted by Symfony HttpFoundation.
355
     *
356
     * @param array $configFormats
357
     *
358
     * @return array
359
     */
360
    private function getFormats(array $configFormats): array
361
    {
362
        $formats = [];
363
        foreach ($configFormats as $format => $value) {
364
            foreach ($value['mime_types'] as $mimeType) {
365
                $formats[$format][] = $mimeType;
366
            }
367
        }
368
369
        return $formats;
370
    }
371
}
372