Completed
Push — master ( d65c84...d30eff )
by Kévin
03:23
created

ApiPlatformExtension::prepend()   C

Complexity

Conditions 7
Paths 21

Size

Total Lines 24
Code Lines 12

Duplication

Lines 6
Ratio 25 %

Importance

Changes 0
Metric Value
dl 6
loc 24
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 12
nc 21
nop 1
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
namespace ApiPlatform\Core\Bridge\Symfony\Bundle\DependencyInjection;
13
14
use Doctrine\ORM\Version;
15
use phpDocumentor\Reflection\DocBlockFactoryInterface;
16
use Symfony\Component\Cache\Adapter\ArrayAdapter;
17
use Symfony\Component\Config\FileLocator;
18
use Symfony\Component\Config\Resource\DirectoryResource;
19
use Symfony\Component\DependencyInjection\ContainerBuilder;
20
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
21
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
22
use Symfony\Component\Finder\Finder;
23
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
24
25
/**
26
 * The extension of this bundle.
27
 *
28
 * @author Kévin Dunglas <[email protected]>
29
 */
30
final class ApiPlatformExtension extends Extension implements PrependExtensionInterface
31
{
32
    /**
33
     * {@inheritdoc}
34
     */
35
    public function prepend(ContainerBuilder $container)
36
    {
37
        if (empty($frameworkConfiguration = $container->getExtensionConfig('framework'))) {
38
            return;
39
        }
40
41
        foreach ($frameworkConfiguration as $frameworkParameters) {
42
            if (isset($frameworkParameters['serializer'])) {
43
                $serializerConfig = $serializerConfig ?? $frameworkParameters['serializer'];
44
            }
45
46
            if (isset($frameworkParameters['property_info'])) {
47
                $propertyInfoConfig = $propertyInfoConfig ?? $frameworkParameters['property_info'];
48
            }
49
        }
50
51 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...
52
            $container->prependExtensionConfig('framework', ['serializer' => ['enabled' => true]]);
53
        }
54
55 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...
56
            $container->prependExtensionConfig('framework', ['property_info' => ['enabled' => true]]);
57
        }
58
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63
    public function load(array $configs, ContainerBuilder $container)
64
    {
65
        $configuration = new Configuration();
66
        $config = $this->processConfiguration($configuration, $configs);
67
        $formats = $this->getFormats($config['formats']);
68
        $errorFormats = $this->getFormats($config['error_formats']);
69
        $this->handleConfig($container, $config, $formats, $errorFormats);
70
71
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
72
        $loader->load('api.xml');
73
        $loader->load('data_provider.xml');
74
75
        $bundles = $container->getParameter('kernel.bundles');
76
77
        $this->registerMetadataConfiguration($container, $loader);
78
        $this->registerSwaggerConfiguration($container, $config, $loader);
79
        $this->registerJsonLdConfiguration($formats, $loader);
80
        $this->registerJsonHalConfiguration($formats, $loader);
81
        $this->registerJsonProblemConfiguration($errorFormats, $loader);
82
        $this->registerLoaders($container, $bundles, $config['loader_paths']);
83
        $this->registerBundlesConfiguration($bundles, $config, $loader);
84
        $this->registerCacheConfiguration($container);
85
        $this->registerDoctrineExtensionConfiguration($container, $config);
86
    }
87
88
    /**
89
     * Handles configuration.
90
     *
91
     * @param ContainerBuilder $container
92
     * @param array            $config
93
     * @param array            $formats
94
     * @param array            $errorFormats
95
     */
96
    private function handleConfig(ContainerBuilder $container, array $config, array $formats, array $errorFormats)
97
    {
98
        $container->setParameter('api_platform.title', $config['title']);
99
        $container->setParameter('api_platform.description', $config['description']);
100
        $container->setParameter('api_platform.version', $config['version']);
101
        $container->setParameter('api_platform.exception_to_status', $config['exception_to_status']);
102
        $container->setParameter('api_platform.formats', $formats);
103
        $container->setParameter('api_platform.error_formats', $errorFormats);
104
        $container->setParameter('api_platform.eager_loading.enabled', $config['eager_loading']['enabled']);
105
        $container->setParameter('api_platform.eager_loading.max_joins', $config['eager_loading']['max_joins']);
106
        $container->setParameter('api_platform.eager_loading.force_eager', $config['eager_loading']['force_eager']);
107
        $container->setParameter('api_platform.collection.order', $config['collection']['order']);
108
        $container->setParameter('api_platform.collection.order_parameter_name', $config['collection']['order_parameter_name']);
109
        $container->setParameter('api_platform.collection.pagination.enabled', $config['collection']['pagination']['enabled']);
110
        $container->setParameter('api_platform.collection.pagination.client_enabled', $config['collection']['pagination']['client_enabled']);
111
        $container->setParameter('api_platform.collection.pagination.client_items_per_page', $config['collection']['pagination']['client_items_per_page']);
112
        $container->setParameter('api_platform.collection.pagination.items_per_page', $config['collection']['pagination']['items_per_page']);
113
        $container->setParameter('api_platform.collection.pagination.maximum_items_per_page', $config['collection']['pagination']['maximum_items_per_page']);
114
        $container->setParameter('api_platform.collection.pagination.page_parameter_name', $config['collection']['pagination']['page_parameter_name']);
115
        $container->setParameter('api_platform.collection.pagination.enabled_parameter_name', $config['collection']['pagination']['enabled_parameter_name']);
116
        $container->setParameter('api_platform.collection.pagination.items_per_page_parameter_name', $config['collection']['pagination']['items_per_page_parameter_name']);
117
118
        $container->setAlias('api_platform.operation_path_resolver.default', $config['default_operation_path_resolver']);
119
120
        if ($config['name_converter']) {
121
            $container->setAlias('api_platform.name_converter', $config['name_converter']);
122
        }
123
    }
124
125
    /**
126
     * Registers metadata configuration.
127
     *
128
     * @param ContainerBuilder $container
129
     * @param XmlFileLoader    $loader
130
     */
131
    private function registerMetadataConfiguration(ContainerBuilder $container, XmlFileLoader $loader)
132
    {
133
        $loader->load('metadata.xml');
134
135
        if (!interface_exists(DocBlockFactoryInterface::class)) {
136
            $container->removeDefinition('api_platform.metadata.resource.metadata_factory.php_doc');
137
        }
138
    }
139
140
    /**
141
     * Registers the Swagger and Swagger UI configuration.
142
     *
143
     * @param ContainerBuilder $container
144
     * @param array            $config
145
     * @param XmlFileLoader    $loader
146
     */
147
    private function registerSwaggerConfiguration(ContainerBuilder $container, array $config, XmlFileLoader $loader)
148
    {
149
        if (!$config['enable_swagger']) {
150
            return;
151
        }
152
153
        $loader->load('swagger.xml');
154
155
        if ($config['enable_swagger_ui']) {
156
            $loader->load('swagger-ui.xml');
157
            $container->setParameter('api_platform.enable_swagger_ui', $config['enable_swagger_ui']);
158
        }
159
160
        $container->setParameter('api_platform.enable_swagger', $config['enable_swagger']);
161
    }
162
163
    /**
164
     * Registers the JSON-LD and Hydra configuration.
165
     *
166
     * @param array         $formats
167
     * @param XmlFileLoader $loader
168
     */
169
    private function registerJsonLdConfiguration(array $formats, XmlFileLoader $loader)
170
    {
171
        if (!isset($formats['jsonld'])) {
172
            return;
173
        }
174
175
        $loader->load('jsonld.xml');
176
        $loader->load('hydra.xml');
177
    }
178
179
    /**
180
     * Registers the HAL configuration.
181
     *
182
     * @param array         $formats
183
     * @param XmlFileLoader $loader
184
     */
185
    private function registerJsonHalConfiguration(array $formats, XmlFileLoader $loader)
186
    {
187
        if (!isset($formats['jsonhal'])) {
188
            return;
189
        }
190
191
        $loader->load('hal.xml');
192
    }
193
194
    /**
195
     * Registers the JSON Problem configuration.
196
     *
197
     * @param array         $errorFormats
198
     * @param XmlFileLoader $loader
199
     */
200
    private function registerJsonProblemConfiguration(array $errorFormats, XmlFileLoader $loader)
201
    {
202
        if (!isset($errorFormats['jsonproblem'])) {
203
            return;
204
        }
205
206
        $loader->load('problem.xml');
207
    }
208
209
    /**
210
     * Registers configuration for integration with third-party bundles.
211
     *
212
     * @param string[]      $bundles
213
     * @param array         $config
214
     * @param XmlFileLoader $loader
215
     */
216
    private function registerBundlesConfiguration(array $bundles, array $config, XmlFileLoader $loader)
217
    {
218
        // Doctrine ORM support
219
        if (isset($bundles['DoctrineBundle']) && class_exists(Version::class)) {
220
            $loader->load('doctrine_orm.xml');
221
        }
222
223
        // FOSUser support
224
        if (isset($bundles['FOSUserBundle']) && $config['enable_fos_user']) {
225
            $loader->load('fos_user.xml');
226
        }
227
228
        // NelmioApiDoc support
229
        if (isset($bundles['NelmioApiDocBundle']) && $config['enable_nelmio_api_doc']) {
230
            $loader->load('nelmio_api_doc.xml');
231
        }
232
    }
233
234
    /**
235
     * Registers the cache configuration.
236
     *
237
     * @param ContainerBuilder $container
238
     */
239
    private function registerCacheConfiguration(ContainerBuilder $container)
240
    {
241
        // Don't use system cache pool in dev
242
        if (!$container->getParameter('kernel.debug')) {
243
            return;
244
        }
245
246
        $container->register('api_platform.cache.metadata.property', ArrayAdapter::class);
247
        $container->register('api_platform.cache.metadata.resource', ArrayAdapter::class);
248
        $container->register('api_platform.cache.route_name_resolver', ArrayAdapter::class);
249
    }
250
251
    /**
252
     * Registers configuration file loaders.
253
     *
254
     * @param ContainerBuilder $container
255
     * @param string[]         $bundles
256
     */
257
    private function registerLoaders(ContainerBuilder $container, array $bundles, array $loaderPaths)
258
    {
259
        $annotationPaths = $loaderPaths['annotation'];
260
        $yamlResources = $loaderPaths['yaml'];
261
        $xmlResources = $loaderPaths['xml'];
262
263
        foreach ($bundles as $bundle) {
264
            $bundleDirectory = dirname((new \ReflectionClass($bundle))->getFileName());
265
            $this->addFileResources($bundleDirectory, $xmlResources, $yamlResources);
266
267
            if (file_exists($entityDirectory = $bundleDirectory.'/Entity')) {
268
                $annotationPaths[] = $entityDirectory;
269
                $container->addResource(new DirectoryResource($entityDirectory, '/\.php$/'));
270
            }
271
        }
272
273
        $container->getDefinition('api_platform.metadata.resource.name_collection_factory.annotation')->addArgument($annotationPaths);
274
        $container->getDefinition('api_platform.metadata.extractor.yaml')->addArgument($yamlResources);
275
        $container->getDefinition('api_platform.metadata.extractor.xml')->addArgument($xmlResources);
276
    }
277
278
    /**
279
     * Manipulate doctrine extension services according to the configuration.
280
     *
281
     * @param ContainerBuilder $container
282
     * @param array            $config
283
     */
284
    private function registerDoctrineExtensionConfiguration(ContainerBuilder $container, array $config)
285
    {
286
        if (false === $config['eager_loading']['enabled']) {
287
            $container->removeDefinition('api_platform.doctrine.orm.query_extension.eager_loading');
288
        }
289
    }
290
291
    /**
292
     * Populates file resources lists.
293
     *
294
     * @param string   $bundleDirectory
295
     * @param string[] $xmlResources
296
     * @param string[] $yamlResources
297
     */
298
    private function addFileResources(string $bundleDirectory, array &$xmlResources, array &$yamlResources)
299
    {
300
        try {
301
            foreach (Finder::create()->files()->in($bundleDirectory.'/Resources/config/')->path('api_resources')->name('*.{yml,yaml,xml}') as $file) {
302
                if ('xml' === $file->getExtension()) {
303
                    $xmlResources[] = $file->getRealPath();
304
                } else {
305
                    $yamlResources[] = $file->getRealPath();
306
                }
307
            }
308
        } catch (\InvalidArgumentException $e) {
309
            // Ignore invalid paths
310
        }
311
    }
312
313
    /**
314
     * Normalizes the format from config to the one accepted by Symfony HttpFoundation.
315
     *
316
     * @param array $configFormats
317
     *
318
     * @return array
319
     */
320
    private function getFormats(array $configFormats): array
321
    {
322
        $formats = [];
323
        foreach ($configFormats as $format => $value) {
324
            foreach ($value['mime_types'] as $mimeType) {
325
                $formats[$format][] = $mimeType;
326
            }
327
        }
328
329
        return $formats;
330
    }
331
}
332