Completed
Push — master ( 1e4134...246601 )
by Christian
02:12 queued 11s
created

FOSRestExtension   F

Complexity

Total Complexity 69

Size/Duplication

Total Lines 401
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 2.07%

Importance

Changes 0
Metric Value
wmc 69
lcom 1
cbo 10
dl 0
loc 401
ccs 5
cts 242
cp 0.0207
rs 2.88
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A getConfiguration() 0 4 1
B load() 0 50 6
A loadForm() 0 10 2
A loadAccessDeniedListener() 0 15 4
A loadAllowedMethodsListener() 0 13 3
A loadBodyListener() 0 34 5
A loadFormatListener() 0 16 4
B loadVersioning() 0 30 7
A loadParamFetcherListener() 0 19 5
A loadBodyConverter() 0 12 3
F loadView() 0 72 12
A loadException() 0 42 4
B loadSerializer() 0 23 6
A loadZoneMatcherListener() 0 19 3
A createZoneRequestMatcher() 0 22 4

How to fix   Complexity   

Complex Class

Complex classes like FOSRestExtension often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FOSRestExtension, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the FOSRestBundle package.
5
 *
6
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace FOS\RestBundle\DependencyInjection;
13
14
use FOS\RestBundle\Inflector\DoctrineInflector;
15
use FOS\RestBundle\View\ViewHandler;
16
use Symfony\Component\Config\FileLocator;
17
use Symfony\Component\DependencyInjection\Alias;
18
use Symfony\Component\DependencyInjection\ChildDefinition;
19
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
20
use Symfony\Component\DependencyInjection\ContainerBuilder;
21
use Symfony\Component\DependencyInjection\ContainerInterface;
22
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
23
use Symfony\Component\DependencyInjection\Reference;
24
use Symfony\Component\Form\Extension\Core\Type\FormType;
25
use Symfony\Component\HttpFoundation\Response;
26
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
27
use Symfony\Component\HttpKernel\EventListener\ErrorListener;
28
use Symfony\Component\HttpKernel\EventListener\ExceptionListener as LegacyHttpKernelExceptionListener;
29
use Symfony\Component\Validator\Constraint;
30
31
/**
32
 * @internal
33
 */
34
class FOSRestExtension extends Extension
35
{
36
    /**
37
     * {@inheritdoc}
38
     */
39 1
    public function getConfiguration(array $config, ContainerBuilder $container): Configuration
40
    {
41 1
        return new Configuration($container->getParameter('kernel.debug'));
42
    }
43
44 5
    public function load(array $configs, ContainerBuilder $container): void
45
    {
46 5
        $configuration = new Configuration($container->getParameter('kernel.debug'));
47 5
        $config = $this->processConfiguration($configuration, $configs);
48
49
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
50
        $loader->load('view.xml');
51
        $loader->load('request.xml');
52
        $loader->load('serializer.xml');
53
54
        $container->getDefinition('fos_rest.routing.loader.controller')->addArgument($config['routing_loader']['default_format']);
55
56
        $container->getDefinition('fos_rest.routing.loader.yaml_collection')->replaceArgument(4, $config['routing_loader']['default_format']);
57
        $container->getDefinition('fos_rest.routing.loader.xml_collection')->replaceArgument(4, $config['routing_loader']['default_format']);
58
59
        $container->getDefinition('fos_rest.routing.loader.yaml_collection')->replaceArgument(2, $config['routing_loader']['include_format']);
60
        $container->getDefinition('fos_rest.routing.loader.xml_collection')->replaceArgument(2, $config['routing_loader']['include_format']);
61
        $container->getDefinition('fos_rest.routing.loader.reader.action')->replaceArgument(3, $config['routing_loader']['include_format']);
62
        $container->getDefinition('fos_rest.routing.loader.reader.action')->replaceArgument(5, $config['routing_loader']['prefix_methods']);
63
64
        foreach ($config['service'] as $key => $service) {
65
            if ('validator' === $service && empty($config['body_converter']['validate'])) {
66
                continue;
67
            }
68
69
            if (null !== $service) {
70
                if ('view_handler' === $key) {
71
                    $container->setAlias('fos_rest.'.$key, new Alias($service, true));
72
                } else {
73
                    $container->setAlias('fos_rest.'.$key, $service);
74
                }
75
            }
76
        }
77
78
        $this->loadForm($config, $loader, $container);
79
        $this->loadException($config, $loader, $container);
80
        $this->loadBodyConverter($config, $loader, $container);
81
        $this->loadView($config, $loader, $container);
82
83
        $this->loadBodyListener($config, $loader, $container);
84
        $this->loadFormatListener($config, $loader, $container);
85
        $this->loadVersioning($config, $loader, $container);
86
        $this->loadParamFetcherListener($config, $loader, $container);
87
        $this->loadAllowedMethodsListener($config, $loader, $container);
88
        $this->loadAccessDeniedListener($config, $loader, $container);
89
        $this->loadZoneMatcherListener($config, $loader, $container);
90
91
        // Needs RequestBodyParamConverter and View Handler loaded.
92
        $this->loadSerializer($config, $container);
93
    }
94
95
    private function loadForm(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
96
    {
97
        if (!empty($config['disable_csrf_role'])) {
98
            $loader->load('forms.xml');
99
100
            $definition = $container->getDefinition('fos_rest.form.extension.csrf_disable');
101
            $definition->replaceArgument(1, $config['disable_csrf_role']);
102
            $definition->addTag('form.type_extension', ['extended_type' => FormType::class]);
103
        }
104
    }
105
106
    private function loadAccessDeniedListener(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
107
    {
108
        if ($config['access_denied_listener']['enabled'] && !empty($config['access_denied_listener']['formats'])) {
109
            $loader->load('access_denied_listener.xml');
110
111
            $service = $container->getDefinition('fos_rest.access_denied_listener');
112
113
            if (!empty($config['access_denied_listener']['service'])) {
114
                $service->clearTag('kernel.event_subscriber');
115
            }
116
117
            $service->replaceArgument(0, $config['access_denied_listener']['formats']);
118
            $service->replaceArgument(1, $config['unauthorized_challenge']);
119
        }
120
    }
121
122
    private function loadAllowedMethodsListener(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
123
    {
124
        if ($config['allowed_methods_listener']['enabled']) {
125
            if (!empty($config['allowed_methods_listener']['service'])) {
126
                $service = $container->getDefinition('fos_rest.allowed_methods_listener');
127
                $service->clearTag('kernel.event_listener');
128
            }
129
130
            $loader->load('allowed_methods_listener.xml');
131
132
            $container->getDefinition('fos_rest.allowed_methods_loader')->replaceArgument(1, $config['cache_dir']);
133
        }
134
    }
135
136
    private function loadBodyListener(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
137
    {
138
        if ($config['body_listener']['enabled']) {
139
            $loader->load('body_listener.xml');
140
141
            $service = $container->getDefinition('fos_rest.body_listener');
142
143
            if (!empty($config['body_listener']['service'])) {
144
                $service->clearTag('kernel.event_listener');
145
            }
146
147
            $service->replaceArgument(1, $config['body_listener']['throw_exception_on_unsupported_content_type']);
148
            $service->addMethodCall('setDefaultFormat', array($config['body_listener']['default_format']));
149
150
            $container->getDefinition('fos_rest.decoder_provider')->replaceArgument(1, $config['body_listener']['decoders']);
151
152
            $decoderServicesMap = array();
153
154
            foreach ($config['body_listener']['decoders'] as $id) {
155
                $decoderServicesMap[$id] = new Reference($id);
156
            }
157
158
            $decodersServiceLocator = ServiceLocatorTagPass::register($container, $decoderServicesMap);
159
            $container->getDefinition('fos_rest.decoder_provider')->replaceArgument(0, $decodersServiceLocator);
160
161
            $arrayNormalizer = $config['body_listener']['array_normalizer'];
162
163
            if (null !== $arrayNormalizer['service']) {
164
                $bodyListener = $container->getDefinition('fos_rest.body_listener');
165
                $bodyListener->addArgument(new Reference($arrayNormalizer['service']));
166
                $bodyListener->addArgument($arrayNormalizer['forms']);
167
            }
168
        }
169
    }
170
171
    private function loadFormatListener(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
172
    {
173
        if ($config['format_listener']['enabled'] && !empty($config['format_listener']['rules'])) {
174
            $loader->load('format_listener.xml');
175
176
            if (!empty($config['format_listener']['service'])) {
177
                $service = $container->getDefinition('fos_rest.format_listener');
178
                $service->clearTag('kernel.event_listener');
179
            }
180
181
            $container->setParameter(
182
                'fos_rest.format_listener.rules',
183
                $config['format_listener']['rules']
184
            );
185
        }
186
    }
187
188
    private function loadVersioning(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
189
    {
190
        if (!empty($config['versioning']['enabled'])) {
191
            $loader->load('versioning.xml');
192
193
            $versionListener = $container->getDefinition('fos_rest.versioning.listener');
194
            $versionListener->replaceArgument(1, $config['versioning']['default_version']);
195
196
            $resolvers = [];
197
            if ($config['versioning']['resolvers']['query']['enabled']) {
198
                $resolvers['query'] = $container->getDefinition('fos_rest.versioning.query_parameter_resolver');
199
                $resolvers['query']->replaceArgument(0, $config['versioning']['resolvers']['query']['parameter_name']);
200
            }
201
            if ($config['versioning']['resolvers']['custom_header']['enabled']) {
202
                $resolvers['custom_header'] = $container->getDefinition('fos_rest.versioning.header_resolver');
203
                $resolvers['custom_header']->replaceArgument(0, $config['versioning']['resolvers']['custom_header']['header_name']);
204
            }
205
            if ($config['versioning']['resolvers']['media_type']['enabled']) {
206
                $resolvers['media_type'] = $container->getDefinition('fos_rest.versioning.media_type_resolver');
207
                $resolvers['media_type']->replaceArgument(0, $config['versioning']['resolvers']['media_type']['regex']);
208
            }
209
210
            $chainResolver = $container->getDefinition('fos_rest.versioning.chain_resolver');
211
            foreach ($config['versioning']['guessing_order'] as $resolver) {
212
                if (isset($resolvers[$resolver])) {
213
                    $chainResolver->addMethodCall('addResolver', [$resolvers[$resolver]]);
214
                }
215
            }
216
        }
217
    }
218
219
    private function loadParamFetcherListener(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
220
    {
221
        if ($config['param_fetcher_listener']['enabled']) {
222
            if (!class_exists(Constraint::class)) {
223
                throw new \LogicException('Enabling the fos_rest.param_fetcher_listener option when the Symfony Validator component is not installed is not supported. Try installing the symfony/validator package.');
224
            }
225
226
            $loader->load('param_fetcher_listener.xml');
227
228
            if (!empty($config['param_fetcher_listener']['service'])) {
229
                $service = $container->getDefinition('fos_rest.param_fetcher_listener');
230
                $service->clearTag('kernel.event_listener');
231
            }
232
233
            if ($config['param_fetcher_listener']['force']) {
234
                $container->getDefinition('fos_rest.param_fetcher_listener')->replaceArgument(1, true);
235
            }
236
        }
237
    }
238
239
    private function loadBodyConverter(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
240
    {
241
        if (!$this->isConfigEnabled($container, $config['body_converter'])) {
242
            return;
243
        }
244
245
        $loader->load('request_body_param_converter.xml');
246
247
        if (!empty($config['body_converter']['validation_errors_argument'])) {
248
            $container->getDefinition('fos_rest.converter.request_body')->replaceArgument(4, $config['body_converter']['validation_errors_argument']);
249
        }
250
    }
251
252
    private function loadView(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
253
    {
254
        if (!empty($config['view']['jsonp_handler'])) {
255
            $handler = new ChildDefinition($config['service']['view_handler']);
256
            $handler->setPublic(true);
257
258
            $jsonpHandler = new Reference('fos_rest.view_handler.jsonp');
259
            $handler->addMethodCall('registerHandler', ['jsonp', [$jsonpHandler, 'createResponse']]);
260
            $container->setDefinition('fos_rest.view_handler', $handler);
261
262
            $container->getDefinition('fos_rest.view_handler.jsonp')->replaceArgument(0, $config['view']['jsonp_handler']['callback_param']);
263
264
            if (empty($config['view']['mime_types']['jsonp'])) {
265
                $config['view']['mime_types']['jsonp'] = $config['view']['jsonp_handler']['mime_type'];
266
            }
267
        }
268
269
        if ($config['view']['mime_types']['enabled']) {
270
            $loader->load('mime_type_listener.xml');
271
272
            if (!empty($config['mime_type_listener']['service'])) {
273
                $service = $container->getDefinition('fos_rest.mime_type_listener');
274
                $service->clearTag('kernel.event_listener');
275
            }
276
277
            $container->getDefinition('fos_rest.mime_type_listener')->replaceArgument(0, $config['view']['mime_types']['formats']);
278
        }
279
280
        if ($config['view']['view_response_listener']['enabled']) {
281
            $loader->load('view_response_listener.xml');
282
            $service = $container->getDefinition('fos_rest.view_response_listener');
283
284
            if (!empty($config['view_response_listener']['service'])) {
285
                $service->clearTag('kernel.event_listener');
286
            }
287
288
            $service->replaceArgument(1, $config['view']['view_response_listener']['force']);
289
        }
290
291
        $formats = [];
292
        foreach ($config['view']['formats'] as $format => $enabled) {
293
            if ($enabled) {
294
                $formats[$format] = false;
295
            }
296
        }
297
298
        if ($config['routing_loader']['enabled']) {
299
            $container->getDefinition('fos_rest.routing.loader.yaml_collection')->replaceArgument(3, $formats);
300
            $container->getDefinition('fos_rest.routing.loader.xml_collection')->replaceArgument(3, $formats);
301
            $container->getDefinition('fos_rest.routing.loader.reader.action')->replaceArgument(4, $formats);
302
        }
303
304
        if (!is_numeric($config['view']['failed_validation'])) {
305
            $config['view']['failed_validation'] = constant(sprintf('%s::%s', Response::class, $config['view']['failed_validation']));
306
        }
307
308
        if (!is_numeric($config['view']['empty_content'])) {
309
            $config['view']['empty_content'] = constant(sprintf('%s::%s', Response::class, $config['view']['empty_content']));
310
        }
311
312
        $defaultViewHandler = $container->getDefinition('fos_rest.view_handler.default');
313
        $defaultViewHandler->setFactory([ViewHandler::class, 'create']);
314
        $defaultViewHandler->setArguments([
315
            new Reference('fos_rest.router'),
316
            new Reference('fos_rest.serializer'),
317
            new Reference('request_stack'),
318
            $formats,
319
            $config['view']['failed_validation'],
320
            $config['view']['empty_content'],
321
            $config['view']['serialize_null'],
322
        ]);
323
    }
324
325
    private function loadException(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
326
    {
327
        if ($config['exception']['enabled']) {
328
            $loader->load('exception_listener.xml');
329
330
            if (!empty($config['exception']['service'])) {
331
                $service = $container->getDefinition('fos_rest.exception_listener');
332
                $service->clearTag('kernel.event_subscriber');
333
            }
334
335
            $controller = $config['exception']['exception_controller'] ?? null;
336
337
            if (class_exists(ErrorListener::class)) {
338
                $container->register('fos_rest.error_listener', ErrorListener::class)
339
                    ->setArguments([
340
                        $controller,
341
                        new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE),
342
                        '%kernel.debug%',
343
                    ])
344
                    ->addTag('monolog.logger', ['channel' => 'request']);
345
            } else {
346
                $container->register('fos_rest.error_listener', LegacyHttpKernelExceptionListener::class)
347
                    ->setArguments([
348
                        $controller,
349
                        new Reference('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE),
350
                    ])
351
                    ->addTag('monolog.logger', ['channel' => 'request']);
352
            }
353
354
            $container->getDefinition('fos_rest.exception.codes_map')
355
                ->replaceArgument(0, $config['exception']['codes']);
356
            $container->getDefinition('fos_rest.exception.messages_map')
357
                ->replaceArgument(0, $config['exception']['messages']);
358
359
            $container->getDefinition('fos_rest.exception.controller')
360
                ->replaceArgument(2, $config['exception']['debug']);
361
            $container->getDefinition('fos_rest.serializer.exception_normalizer.jms')
362
                ->replaceArgument(1, $config['exception']['debug']);
363
            $container->getDefinition('fos_rest.serializer.exception_normalizer.symfony')
364
                ->replaceArgument(1, $config['exception']['debug']);
365
        }
366
    }
367
368
    private function loadSerializer(array $config, ContainerBuilder $container): void
369
    {
370
        $bodyConverter = $container->hasDefinition('fos_rest.converter.request_body') ? $container->getDefinition('fos_rest.converter.request_body') : null;
371
        $viewHandler = $container->getDefinition('fos_rest.view_handler.default');
372
        $options = array();
373
374
        if (!empty($config['serializer']['version'])) {
375
            if ($bodyConverter) {
376
                $bodyConverter->replaceArgument(2, $config['serializer']['version']);
377
            }
378
            $options['exclusionStrategyVersion'] = $config['serializer']['version'];
379
        }
380
381
        if (!empty($config['serializer']['groups'])) {
382
            if ($bodyConverter) {
383
                $bodyConverter->replaceArgument(1, $config['serializer']['groups']);
384
            }
385
            $options['exclusionStrategyGroups'] = $config['serializer']['groups'];
386
        }
387
388
        $options['serializeNullStrategy'] = $config['serializer']['serialize_null'];
389
        $viewHandler->addArgument($options);
390
    }
391
392
    private function loadZoneMatcherListener(array $config, XmlFileLoader $loader, ContainerBuilder $container): void
393
    {
394
        if (!empty($config['zone'])) {
395
            $loader->load('zone_matcher_listener.xml');
396
            $zoneMatcherListener = $container->getDefinition('fos_rest.zone_matcher_listener');
397
398
            foreach ($config['zone'] as $zone) {
399
                $matcher = $this->createZoneRequestMatcher(
400
                    $container,
401
                    $zone['path'],
402
                    $zone['host'],
403
                    $zone['methods'],
404
                    $zone['ips']
405
                );
406
407
                $zoneMatcherListener->addMethodCall('addRequestMatcher', array($matcher));
408
            }
409
        }
410
    }
411
412
    private function createZoneRequestMatcher(ContainerBuilder $container, ?string $path = null, ?string $host = null, array $methods = array(), array $ips = null): Reference
413
    {
414
        if ($methods) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $methods of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
415
            $methods = array_map('strtoupper', (array) $methods);
416
        }
417
418
        $serialized = serialize(array($path, $host, $methods, $ips));
419
        $id = 'fos_rest.zone_request_matcher.'.md5($serialized).sha1($serialized);
420
421
        // only add arguments that are necessary
422
        $arguments = array($path, $host, $methods, $ips);
423
        while (count($arguments) > 0 && !end($arguments)) {
424
            array_pop($arguments);
425
        }
426
427
        $container
428
            ->setDefinition($id, new ChildDefinition('fos_rest.zone_request_matcher'))
429
            ->setArguments($arguments)
430
        ;
431
432
        return new Reference($id);
433
    }
434
}
435