Completed
Push — master ( a69311...5b5097 )
by Christian
02:43
created

Configuration::addFormatListenerSection()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 53

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 45
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 53
ccs 45
cts 46
cp 0.9783
rs 9.0254
c 0
b 0
f 0
cc 3
nc 1
nop 1
crap 3

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
15
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
16
use Symfony\Component\Config\Definition\ConfigurationInterface;
17
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
18
use Symfony\Component\HttpFoundation\Response;
19
use Symfony\Component\OptionsResolver\OptionsResolver;
20
use Symfony\Component\Serializer\Encoder\XmlEncoder;
21
22
/**
23
 * This class contains the configuration information for the bundle.
24
 *
25
 * This information is solely responsible for how the different configuration
26
 * sections are normalized, and merged.
27
 *
28
 * @author Lukas Kahwe Smith <[email protected]>
29
 *
30
 * @internal
31
 */
32
final class Configuration implements ConfigurationInterface
33
{
34
    private $debug;
35
36 60
    public function __construct(bool $debug)
37
    {
38 60
        $this->debug = $debug;
39 60
    }
40
41 59
    public function getConfigTreeBuilder(): TreeBuilder
42
    {
43 59
        $treeBuilder = new TreeBuilder('fos_rest');
44
45 59
        $rootNode = $treeBuilder->getRootNode();
46
47
        $rootNode
48 59
            ->children()
49 59
                ->scalarNode('disable_csrf_role')->defaultNull()->end()
50 59
                ->arrayNode('access_denied_listener')
51 59
                    ->canBeEnabled()
52 59
                    ->beforeNormalization()
53 View Code Duplication
                        ->ifArray()->then(function ($v) {
54
                            if (!empty($v) && empty($v['formats'])) {
55
                                unset($v['enabled']);
56
                                $v = ['enabled' => true, 'formats' => $v];
57
                            }
58
59
                            return $v;
60 59
                        })
61 59
                    ->end()
62 59
                    ->fixXmlConfig('format', 'formats')
63 59
                    ->children()
64 59
                        ->scalarNode('service')->defaultNull()->end()
65 59
                        ->arrayNode('formats')
66 59
                            ->useAttributeAsKey('name')
67 59
                            ->prototype('boolean')->end()
68 59
                        ->end()
69 59
                    ->end()
70 59
                ->end()
71 59
                ->scalarNode('unauthorized_challenge')->defaultNull()->end()
72 59
                ->arrayNode('param_fetcher_listener')
73 59
                    ->beforeNormalization()
74 59
                        ->ifString()
75 View Code Duplication
                        ->then(function ($v) {
76 1
                            return ['enabled' => in_array($v, ['force', 'true']), 'force' => 'force' === $v];
77 59
                        })
78 59
                    ->end()
79 59
                    ->canBeEnabled()
80 59
                    ->children()
81 59
                        ->booleanNode('force')->defaultFalse()->end()
82 59
                        ->scalarNode('service')->defaultNull()->end()
83 59
                    ->end()
84 59
                ->end()
85 59
                ->scalarNode('cache_dir')->cannotBeEmpty()->defaultValue('%kernel.cache_dir%/fos_rest')->end()
86 59
                ->arrayNode('allowed_methods_listener')
87 59
                    ->canBeEnabled()
88 59
                    ->children()
89 59
                        ->scalarNode('service')->defaultNull()->end()
90 59
                    ->end()
91 59
                ->end()
92 59
                ->booleanNode('routing_loader')
93 59
                    ->defaultValue(false)
94 59
                    ->validate()
95 59
                        ->ifTrue()
96 59
                        ->thenInvalid('only "false" is supported')
97 59
                    ->end()
98 59
                ->end()
99 59
                ->arrayNode('body_converter')
100 59
                    ->canBeEnabled()
101 59
                    ->children()
102 59
                        ->scalarNode('validate')
103 59
                            ->defaultFalse()
104 59
                            ->beforeNormalization()
105 59
                                ->ifTrue()
106
                                ->then(function ($value) {
107 1
                                    if (!class_exists(OptionsResolver::class)) {
108
                                        throw new InvalidConfigurationException("'body_converter.validate: true' requires OptionsResolver component installation ( composer require symfony/options-resolver )");
109
                                    }
110
111 1
                                    return $value;
112 59
                                })
113 59
                            ->end()
114 59
                        ->end()
115 59
                        ->scalarNode('validation_errors_argument')->defaultValue('validationErrors')->end()
116 59
                    ->end()
117 59
                ->end()
118 59
                ->arrayNode('service')
119 59
                    ->addDefaultsIfNotSet()
120 59
                    ->children()
121 59
                        ->scalarNode('serializer')->defaultNull()->end()
122 59
                        ->scalarNode('view_handler')->defaultValue('fos_rest.view_handler.default')->end()
123 59
                        ->scalarNode('validator')->defaultValue('validator')->end()
124 59
                    ->end()
125 59
                ->end()
126 59
                ->arrayNode('serializer')
127 59
                    ->addDefaultsIfNotSet()
128 59
                    ->children()
129 59
                        ->scalarNode('version')->defaultNull()->end()
130 59
                        ->arrayNode('groups')
131 59
                            ->prototype('scalar')->end()
132 59
                        ->end()
133 59
                        ->booleanNode('serialize_null')->defaultFalse()->end()
134 59
                    ->end()
135 59
                ->end()
136 59
                ->arrayNode('zone')
137 59
                    ->cannotBeOverwritten()
138 59
                    ->prototype('array')
139 59
                    ->fixXmlConfig('ip')
140 59
                    ->children()
141 59
                        ->scalarNode('path')
142 59
                            ->defaultNull()
143 59
                            ->info('use the urldecoded format')
144 59
                            ->example('^/path to resource/')
145 59
                        ->end()
146 59
                        ->scalarNode('host')->defaultNull()->end()
147 59
                        ->arrayNode('methods')
148
                            ->beforeNormalization()->ifString()->then(function ($v) {
149
                                return preg_split('/\s*,\s*/', $v);
150 59
                            })->end()
151 59
                            ->prototype('scalar')->end()
152 59
                        ->end()
153 59
                        ->arrayNode('ips')
154
                            ->beforeNormalization()->ifString()->then(function ($v) {
155 1
                                return array($v);
156 59
                            })->end()
157 59
                            ->prototype('scalar')->end()
158 59
                        ->end()
159 59
                    ->end()
160 59
                ->end()
161 59
            ->end()
162 59
        ->end();
163
164 59
        $this->addViewSection($rootNode);
0 ignored issues
show
Compatibility introduced by
$rootNode of type object<Symfony\Component...Builder\NodeDefinition> is not a sub-type of object<Symfony\Component...er\ArrayNodeDefinition>. It seems like you assume a child class of the class Symfony\Component\Config...\Builder\NodeDefinition to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
165 59
        $this->addExceptionSection($rootNode);
0 ignored issues
show
Compatibility introduced by
$rootNode of type object<Symfony\Component...Builder\NodeDefinition> is not a sub-type of object<Symfony\Component...er\ArrayNodeDefinition>. It seems like you assume a child class of the class Symfony\Component\Config...\Builder\NodeDefinition to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
166 59
        $this->addBodyListenerSection($rootNode);
0 ignored issues
show
Compatibility introduced by
$rootNode of type object<Symfony\Component...Builder\NodeDefinition> is not a sub-type of object<Symfony\Component...er\ArrayNodeDefinition>. It seems like you assume a child class of the class Symfony\Component\Config...\Builder\NodeDefinition to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
167 59
        $this->addFormatListenerSection($rootNode);
0 ignored issues
show
Compatibility introduced by
$rootNode of type object<Symfony\Component...Builder\NodeDefinition> is not a sub-type of object<Symfony\Component...er\ArrayNodeDefinition>. It seems like you assume a child class of the class Symfony\Component\Config...\Builder\NodeDefinition to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
168 59
        $this->addVersioningSection($rootNode);
0 ignored issues
show
Compatibility introduced by
$rootNode of type object<Symfony\Component...Builder\NodeDefinition> is not a sub-type of object<Symfony\Component...er\ArrayNodeDefinition>. It seems like you assume a child class of the class Symfony\Component\Config...\Builder\NodeDefinition to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
169
170 59
        return $treeBuilder;
171
    }
172
173 59
    private function addViewSection(ArrayNodeDefinition $rootNode): void
174
    {
175
        $rootNode
176 59
            ->children()
177 59
                ->arrayNode('view')
178 59
                    ->fixXmlConfig('format', 'formats')
179 59
                    ->fixXmlConfig('mime_type', 'mime_types')
180 59
                    ->addDefaultsIfNotSet()
181 59
                    ->children()
182 59
                        ->arrayNode('mime_types')
183 59
                            ->canBeEnabled()
184 59
                            ->beforeNormalization()
185 View Code Duplication
                                ->ifArray()->then(function ($v) {
186 1
                                    if (!empty($v) && empty($v['formats'])) {
187 1
                                        unset($v['enabled']);
188 1
                                        $v = ['enabled' => true, 'formats' => $v];
189
                                    }
190
191 1
                                    return $v;
192 59
                                })
193 59
                            ->end()
194 59
                            ->fixXmlConfig('format', 'formats')
195 59
                            ->children()
196 59
                                ->scalarNode('service')->defaultNull()->end()
197 59
                                ->arrayNode('formats')
198 59
                                    ->useAttributeAsKey('name')
199 59
                                    ->prototype('array')
200 59
                                        ->beforeNormalization()
201 59
                                            ->ifString()
202
                                            ->then(function ($v) { return array($v); })
203 59
                                        ->end()
204 59
                                        ->prototype('scalar')->end()
205 59
                                    ->end()
206 59
                                ->end()
207 59
                            ->end()
208 59
                        ->end()
209 59
                        ->arrayNode('formats')
210 59
                            ->useAttributeAsKey('name')
211 59
                            ->defaultValue(['json' => true, 'xml' => true])
212 59
                            ->prototype('boolean')->end()
213 59
                        ->end()
214 59
                        ->arrayNode('view_response_listener')
215 59
                            ->beforeNormalization()
216 59
                                ->ifString()
217 View Code Duplication
                                ->then(function ($v) {
218 4
                                    return ['enabled' => in_array($v, ['force', 'true']), 'force' => 'force' === $v];
219 59
                                })
220 59
                            ->end()
221 59
                            ->canBeEnabled()
222 59
                            ->children()
223 59
                                ->booleanNode('force')->defaultFalse()->end()
224 59
                                ->scalarNode('service')->defaultNull()->end()
225 59
                            ->end()
226 59
                        ->end()
227 59
                        ->scalarNode('failed_validation')->defaultValue(Response::HTTP_BAD_REQUEST)->end()
228 59
                        ->scalarNode('empty_content')->defaultValue(Response::HTTP_NO_CONTENT)->end()
229 59
                        ->booleanNode('serialize_null')->defaultFalse()->end()
230 59
                        ->arrayNode('jsonp_handler')
231 59
                            ->canBeUnset()
232 59
                            ->children()
233 59
                                ->scalarNode('callback_param')->defaultValue('callback')->end()
234 59
                                ->scalarNode('mime_type')->defaultValue('application/javascript+jsonp')->end()
235 59
                            ->end()
236 59
                        ->end()
237 59
                    ->end()
238 59
                ->end()
239 59
            ->end();
240 59
    }
241
242 59
    private function addBodyListenerSection(ArrayNodeDefinition $rootNode): void
243
    {
244 59
        $decodersDefaultValue = ['json' => 'fos_rest.decoder.json'];
245 59
        if (class_exists(XmlEncoder::class)) {
246 59
            $decodersDefaultValue['xml'] = 'fos_rest.decoder.xml';
247
        }
248
        $rootNode
249 59
            ->children()
250 59
                ->arrayNode('body_listener')
251 59
                    ->fixXmlConfig('decoder', 'decoders')
252 59
                    ->addDefaultsIfNotSet()
253 59
                    ->canBeUnset()
254 59
                    ->canBeEnabled()
255 59
                    ->children()
256 59
                        ->scalarNode('service')->defaultNull()->end()
257 59
                        ->scalarNode('default_format')->defaultNull()->end()
258 59
                        ->booleanNode('throw_exception_on_unsupported_content_type')
259 59
                            ->defaultFalse()
260 59
                        ->end()
261 59
                        ->arrayNode('decoders')
262 59
                            ->useAttributeAsKey('name')
263 59
                            ->defaultValue($decodersDefaultValue)
264 59
                            ->prototype('scalar')->end()
265 59
                        ->end()
266 59
                        ->arrayNode('array_normalizer')
267 59
                            ->addDefaultsIfNotSet()
268 59
                            ->beforeNormalization()
269
                                ->ifString()->then(function ($v) {
270 1
                                    return ['service' => $v];
271 59
                                })
272 59
                            ->end()
273 59
                            ->children()
274 59
                                ->scalarNode('service')->defaultNull()->end()
275 59
                                ->booleanNode('forms')->defaultFalse()->end()
276 59
                            ->end()
277 59
                        ->end()
278 59
                    ->end()
279 59
                ->end()
280 59
            ->end();
281 59
    }
282
283 59
    private function addFormatListenerSection(ArrayNodeDefinition $rootNode): void
284
    {
285
        $rootNode
286 59
            ->children()
287 59
                ->arrayNode('format_listener')
288 59
                    ->fixXmlConfig('rule', 'rules')
289 59
                    ->addDefaultsIfNotSet()
290 59
                    ->canBeUnset()
291 59
                    ->beforeNormalization()
292
                        ->ifTrue(function ($v) {
293
                            // check if we got an assoc array in rules
294 6
                            return isset($v['rules'])
295 6
                                && is_array($v['rules'])
296 6
                                && array_keys($v['rules']) !== range(0, count($v['rules']) - 1);
297 59
                        })
298
                        ->then(function ($v) {
299 1
                            $v['rules'] = [$v['rules']];
300
301 1
                            return $v;
302 59
                        })
303 59
                    ->end()
304 59
                    ->canBeEnabled()
305 59
                    ->children()
306 59
                        ->scalarNode('service')->defaultNull()->end()
307 59
                        ->arrayNode('rules')
308 59
                            ->performNoDeepMerging()
309 59
                            ->prototype('array')
310 59
                                ->fixXmlConfig('priority', 'priorities')
311 59
                                ->fixXmlConfig('attribute', 'attributes')
312 59
                                ->children()
313 59
                                    ->scalarNode('path')->defaultNull()->info('URL path info')->end()
314 59
                                    ->scalarNode('host')->defaultNull()->info('URL host name')->end()
315 59
                                    ->variableNode('methods')->defaultNull()->info('Method for URL')->end()
316 59
                                    ->arrayNode('attributes')
317 59
                                        ->useAttributeAsKey('name')
318 59
                                        ->prototype('variable')->end()
319 59
                                    ->end()
320 59
                                    ->booleanNode('stop')->defaultFalse()->end()
321 59
                                    ->booleanNode('prefer_extension')->defaultTrue()->end()
322 59
                                    ->scalarNode('fallback_format')->defaultValue('html')->end()
323 59
                                    ->arrayNode('priorities')
324
                                        ->beforeNormalization()->ifString()->then(function ($v) {
325
                                            return preg_split('/\s*,\s*/', $v);
326 59
                                        })->end()
327 59
                                        ->prototype('scalar')->end()
328 59
                                    ->end()
329 59
                                ->end()
330 59
                            ->end()
331 59
                        ->end()
332 59
                    ->end()
333 59
                ->end()
334 59
            ->end();
335 59
    }
336
337 59
    private function addVersioningSection(ArrayNodeDefinition $rootNode): void
338
    {
339
        $rootNode
340 59
        ->children()
341 59
            ->arrayNode('versioning')
342 59
                ->canBeEnabled()
343 59
                ->children()
344 59
                    ->scalarNode('default_version')->defaultNull()->end()
345 59
                    ->arrayNode('resolvers')
346 59
                        ->addDefaultsIfNotSet()
347 59
                        ->children()
348 59
                            ->arrayNode('query')
349 59
                                ->canBeDisabled()
350 59
                                ->children()
351 59
                                    ->scalarNode('parameter_name')->defaultValue('version')->end()
352 59
                                ->end()
353 59
                            ->end()
354 59
                            ->arrayNode('custom_header')
355 59
                                ->canBeDisabled()
356 59
                                ->children()
357 59
                                    ->scalarNode('header_name')->defaultValue('X-Accept-Version')->end()
358 59
                                ->end()
359 59
                            ->end()
360 59
                            ->arrayNode('media_type')
361 59
                                ->canBeDisabled()
362 59
                                ->children()
363 59
                                    ->scalarNode('regex')->defaultValue('/(v|version)=(?P<version>[0-9\.]+)/')->end()
364 59
                                ->end()
365 59
                            ->end()
366 59
                        ->end()
367 59
                    ->end()
368 59
                    ->arrayNode('guessing_order')
369 59
                        ->defaultValue(['query', 'custom_header', 'media_type'])
370 59
                        ->validate()
371
                            ->ifTrue(function ($v) {
372
                                foreach ($v as $resolver) {
373
                                    if (!in_array($resolver, ['query', 'custom_header', 'media_type'])) {
374
                                        return true;
375
                                    }
376
                                }
377 59
                            })
378 59
                            ->thenInvalid('Versioning guessing order can only contain "query", "custom_header", "media_type".')
379 59
                        ->end()
380 59
                        ->prototype('scalar')->end()
381 59
                    ->end()
382 59
                ->end()
383 59
            ->end()
384 59
        ->end();
385 59
    }
386
387 59
    private function addExceptionSection(ArrayNodeDefinition $rootNode): void
388
    {
389
        $rootNode
390 59
            ->children()
391 59
                ->arrayNode('exception')
392 59
                    ->fixXmlConfig('code', 'codes')
393 59
                    ->fixXmlConfig('message', 'messages')
394 59
                    ->addDefaultsIfNotSet()
395 59
                    ->canBeEnabled()
396 59
                    ->children()
397 59
                        ->booleanNode('map_exception_codes')
398 59
                            ->defaultFalse()
399 59
                            ->info('Enables an event listener that maps exception codes to response status codes based on the map configured with the "fos_rest.exception.codes" option.')
400 59
                        ->end()
401 59
                        ->booleanNode('exception_listener')
402 59
                            ->defaultValue(false)
403 59
                            ->validate()
404 59
                                ->ifTrue()
405 59
                                ->thenInvalid('only "false" is supported')
406 59
                            ->end()
407 59
                        ->end()
408 59
                        ->booleanNode('serialize_exceptions')
409 59
                            ->defaultValue(false)
410 59
                            ->validate()
411 59
                                ->ifTrue()
412 59
                                ->thenInvalid('only "false" is supported')
413 59
                            ->end()
414 59
                        ->end()
415 59
                        ->enumNode('flatten_exception_format')
416 59
                            ->defaultValue('legacy')
417 59
                            ->values(['legacy', 'rfc7807'])
418 59
                        ->end()
419 59
                        ->booleanNode('serializer_error_renderer')->defaultValue(false)->end()
420 59
                        ->arrayNode('codes')
421 59
                            ->useAttributeAsKey('name')
422 59
                            ->beforeNormalization()
423 59
                                ->ifArray()
424
                                ->then(function (array $items) {
425 13
                                    foreach ($items as &$item) {
426 13
                                        if (is_int($item)) {
427 3
                                            continue;
428
                                        }
429
430 10
                                        if (!defined(sprintf('%s::%s', Response::class, $item))) {
431 9
                                            throw new InvalidConfigurationException(sprintf('Invalid HTTP code in fos_rest.exception.codes, see %s for all valid codes.', Response::class));
432
                                        }
433
434 1
                                        $item = constant(sprintf('%s::%s', Response::class, $item));
435
                                    }
436
437 4
                                    return $items;
438 59
                                })
439 59
                            ->end()
440 59
                            ->prototype('integer')->end()
441
442 59
                            ->validate()
443 59
                            ->ifArray()
444
                                ->then(function (array $items) {
445 4
                                    foreach ($items as $class => $code) {
446 4
                                        $this->testExceptionExists($class);
447
                                    }
448
449 3
                                    return $items;
450 59
                                })
451 59
                            ->end()
452 59
                        ->end()
453 59
                        ->arrayNode('messages')
454 59
                            ->useAttributeAsKey('name')
455 59
                            ->prototype('boolean')->end()
456 59
                            ->validate()
457 59
                                ->ifArray()
458
                                ->then(function (array $items) {
459 9
                                    foreach ($items as $class => $nomatter) {
460 9
                                        $this->testExceptionExists($class);
461
                                    }
462
463 8
                                    return $items;
464 59
                                })
465 59
                            ->end()
466 59
                        ->end()
467 59
                        ->booleanNode('debug')
468 59
                            ->defaultValue($this->debug)
469 59
                        ->end()
470 59
                    ->end()
471 59
                ->end()
472 59
            ->end();
473 59
    }
474
475 13
    private function testExceptionExists(string $throwable): void
476
    {
477 13
        if (!is_subclass_of($throwable, \Throwable::class)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \Throwable::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
478 2
            throw new InvalidConfigurationException(sprintf('FOSRestBundle exception mapper: Could not load class "%s" or the class does not extend from "%s". Most probably this is a configuration problem.', $throwable, \Throwable::class));
479
        }
480 11
    }
481
}
482