Completed
Push — master ( 007cba...a69311 )
by Christian
01:55 queued 11s
created

Configuration::getConfigTreeBuilder()   B

Complexity

Conditions 4
Paths 1

Size

Total Lines 131

Duplication

Lines 11
Ratio 8.4 %

Code Coverage

Tests 105
CRAP Score 4.0105

Importance

Changes 0
Metric Value
dl 11
loc 131
ccs 105
cts 115
cp 0.913
rs 8
c 0
b 0
f 0
cc 4
nc 1
nop 0
crap 4.0105

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 16
    public function __construct(bool $debug)
37
    {
38 16
        $this->debug = $debug;
39 16
    }
40
41 15
    public function getConfigTreeBuilder(): TreeBuilder
42
    {
43 15
        $treeBuilder = new TreeBuilder('fos_rest');
44
45 15
        $rootNode = $treeBuilder->getRootNode();
46
47
        $rootNode
48 15
            ->children()
49 15
                ->scalarNode('disable_csrf_role')->defaultNull()->end()
50 15
                ->arrayNode('access_denied_listener')
51 15
                    ->canBeEnabled()
52 15
                    ->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 15
                        })
61 15
                    ->end()
62 15
                    ->fixXmlConfig('format', 'formats')
63 15
                    ->children()
64 15
                        ->scalarNode('service')->defaultNull()->end()
65 15
                        ->arrayNode('formats')
66 15
                            ->useAttributeAsKey('name')
67 15
                            ->prototype('boolean')->end()
68 15
                        ->end()
69 15
                    ->end()
70 15
                ->end()
71 15
                ->scalarNode('unauthorized_challenge')->defaultNull()->end()
72 15
                ->arrayNode('param_fetcher_listener')
73 15
                    ->beforeNormalization()
74 15
                        ->ifString()
75 View Code Duplication
                        ->then(function ($v) {
76
                            return ['enabled' => in_array($v, ['force', 'true']), 'force' => 'force' === $v];
77 15
                        })
78 15
                    ->end()
79 15
                    ->canBeEnabled()
80 15
                    ->children()
81 15
                        ->booleanNode('force')->defaultFalse()->end()
82 15
                        ->scalarNode('service')->defaultNull()->end()
83 15
                    ->end()
84 15
                ->end()
85 15
                ->scalarNode('cache_dir')->cannotBeEmpty()->defaultValue('%kernel.cache_dir%/fos_rest')->end()
86 15
                ->arrayNode('allowed_methods_listener')
87 15
                    ->canBeEnabled()
88 15
                    ->children()
89 15
                        ->scalarNode('service')->defaultNull()->end()
90 15
                    ->end()
91 15
                ->end()
92 15
                ->booleanNode('routing_loader')
93 15
                    ->defaultValue(false)
94 15
                    ->validate()
95 15
                        ->ifTrue()
96 15
                        ->thenInvalid('only "false" is supported')
97 15
                    ->end()
98 15
                ->end()
99 15
                ->arrayNode('body_converter')
100 15
                    ->canBeEnabled()
101 15
                    ->children()
102 15
                        ->scalarNode('validate')
103 15
                            ->defaultFalse()
104 15
                            ->beforeNormalization()
105 15
                                ->ifTrue()
106
                                ->then(function ($value) {
107
                                    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
                                    return $value;
112 15
                                })
113 15
                            ->end()
114 15
                        ->end()
115 15
                        ->scalarNode('validation_errors_argument')->defaultValue('validationErrors')->end()
116 15
                    ->end()
117 15
                ->end()
118 15
                ->arrayNode('service')
119 15
                    ->addDefaultsIfNotSet()
120 15
                    ->children()
121 15
                        ->scalarNode('serializer')->defaultNull()->end()
122 15
                        ->scalarNode('view_handler')->defaultValue('fos_rest.view_handler.default')->end()
123 15
                        ->scalarNode('validator')->defaultValue('validator')->end()
124 15
                    ->end()
125 15
                ->end()
126 15
                ->arrayNode('serializer')
127 15
                    ->addDefaultsIfNotSet()
128 15
                    ->children()
129 15
                        ->scalarNode('version')->defaultNull()->end()
130 15
                        ->arrayNode('groups')
131 15
                            ->prototype('scalar')->end()
132 15
                        ->end()
133 15
                        ->booleanNode('serialize_null')->defaultFalse()->end()
134 15
                    ->end()
135 15
                ->end()
136 15
                ->arrayNode('zone')
137 15
                    ->cannotBeOverwritten()
138 15
                    ->prototype('array')
139 15
                    ->fixXmlConfig('ip')
140 15
                    ->children()
141 15
                        ->scalarNode('path')
142 15
                            ->defaultNull()
143 15
                            ->info('use the urldecoded format')
144 15
                            ->example('^/path to resource/')
145 15
                        ->end()
146 15
                        ->scalarNode('host')->defaultNull()->end()
147 15
                        ->arrayNode('methods')
148
                            ->beforeNormalization()->ifString()->then(function ($v) {
149
                                return preg_split('/\s*,\s*/', $v);
150 15
                            })->end()
151 15
                            ->prototype('scalar')->end()
152 15
                        ->end()
153 15
                        ->arrayNode('ips')
154
                            ->beforeNormalization()->ifString()->then(function ($v) {
155
                                return array($v);
156 15
                            })->end()
157 15
                            ->prototype('scalar')->end()
158 15
                        ->end()
159 15
                    ->end()
160 15
                ->end()
161 15
            ->end()
162 15
        ->end();
163
164 15
        $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 15
        $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 15
        $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 15
        $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 15
        $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 15
        return $treeBuilder;
171
    }
172
173 15
    private function addViewSection(ArrayNodeDefinition $rootNode): void
174
    {
175
        $rootNode
176 15
            ->children()
177 15
                ->arrayNode('view')
178 15
                    ->fixXmlConfig('format', 'formats')
179 15
                    ->fixXmlConfig('mime_type', 'mime_types')
180 15
                    ->addDefaultsIfNotSet()
181 15
                    ->children()
182 15
                        ->arrayNode('mime_types')
183 15
                            ->canBeEnabled()
184 15
                            ->beforeNormalization()
185 View Code Duplication
                                ->ifArray()->then(function ($v) {
186
                                    if (!empty($v) && empty($v['formats'])) {
187
                                        unset($v['enabled']);
188
                                        $v = ['enabled' => true, 'formats' => $v];
189
                                    }
190
191
                                    return $v;
192 15
                                })
193 15
                            ->end()
194 15
                            ->fixXmlConfig('format', 'formats')
195 15
                            ->children()
196 15
                                ->scalarNode('service')->defaultNull()->end()
197 15
                                ->arrayNode('formats')
198 15
                                    ->useAttributeAsKey('name')
199 15
                                    ->prototype('array')
200 15
                                        ->beforeNormalization()
201 15
                                            ->ifString()
202
                                            ->then(function ($v) { return array($v); })
203 15
                                        ->end()
204 15
                                        ->prototype('scalar')->end()
205 15
                                    ->end()
206 15
                                ->end()
207 15
                            ->end()
208 15
                        ->end()
209 15
                        ->arrayNode('formats')
210 15
                            ->useAttributeAsKey('name')
211 15
                            ->defaultValue(['json' => true, 'xml' => true])
212 15
                            ->prototype('boolean')->end()
213 15
                        ->end()
214 15
                        ->arrayNode('view_response_listener')
215 15
                            ->beforeNormalization()
216 15
                                ->ifString()
217 View Code Duplication
                                ->then(function ($v) {
218
                                    return ['enabled' => in_array($v, ['force', 'true']), 'force' => 'force' === $v];
219 15
                                })
220 15
                            ->end()
221 15
                            ->canBeEnabled()
222 15
                            ->children()
223 15
                                ->booleanNode('force')->defaultFalse()->end()
224 15
                                ->scalarNode('service')->defaultNull()->end()
225 15
                            ->end()
226 15
                        ->end()
227 15
                        ->scalarNode('failed_validation')->defaultValue(Response::HTTP_BAD_REQUEST)->end()
228 15
                        ->scalarNode('empty_content')->defaultValue(Response::HTTP_NO_CONTENT)->end()
229 15
                        ->booleanNode('serialize_null')->defaultFalse()->end()
230 15
                        ->arrayNode('jsonp_handler')
231 15
                            ->canBeUnset()
232 15
                            ->children()
233 15
                                ->scalarNode('callback_param')->defaultValue('callback')->end()
234 15
                                ->scalarNode('mime_type')->defaultValue('application/javascript+jsonp')->end()
235 15
                            ->end()
236 15
                        ->end()
237 15
                    ->end()
238 15
                ->end()
239 15
            ->end();
240 15
    }
241
242 15
    private function addBodyListenerSection(ArrayNodeDefinition $rootNode): void
243
    {
244 15
        $decodersDefaultValue = ['json' => 'fos_rest.decoder.json'];
245 15
        if (class_exists(XmlEncoder::class)) {
246 15
            $decodersDefaultValue['xml'] = 'fos_rest.decoder.xml';
247
        }
248
        $rootNode
249 15
            ->children()
250 15
                ->arrayNode('body_listener')
251 15
                    ->fixXmlConfig('decoder', 'decoders')
252 15
                    ->addDefaultsIfNotSet()
253 15
                    ->canBeUnset()
254 15
                    ->canBeEnabled()
255 15
                    ->children()
256 15
                        ->scalarNode('service')->defaultNull()->end()
257 15
                        ->scalarNode('default_format')->defaultNull()->end()
258 15
                        ->booleanNode('throw_exception_on_unsupported_content_type')
259 15
                            ->defaultFalse()
260 15
                        ->end()
261 15
                        ->arrayNode('decoders')
262 15
                            ->useAttributeAsKey('name')
263 15
                            ->defaultValue($decodersDefaultValue)
264 15
                            ->prototype('scalar')->end()
265 15
                        ->end()
266 15
                        ->arrayNode('array_normalizer')
267 15
                            ->addDefaultsIfNotSet()
268 15
                            ->beforeNormalization()
269
                                ->ifString()->then(function ($v) {
270
                                    return ['service' => $v];
271 15
                                })
272 15
                            ->end()
273 15
                            ->children()
274 15
                                ->scalarNode('service')->defaultNull()->end()
275 15
                                ->booleanNode('forms')->defaultFalse()->end()
276 15
                            ->end()
277 15
                        ->end()
278 15
                    ->end()
279 15
                ->end()
280 15
            ->end();
281 15
    }
282
283 15
    private function addFormatListenerSection(ArrayNodeDefinition $rootNode): void
284
    {
285
        $rootNode
286 15
            ->children()
287 15
                ->arrayNode('format_listener')
288 15
                    ->fixXmlConfig('rule', 'rules')
289 15
                    ->addDefaultsIfNotSet()
290 15
                    ->canBeUnset()
291 15
                    ->beforeNormalization()
292
                        ->ifTrue(function ($v) {
293
                            // check if we got an assoc array in rules
294 2
                            return isset($v['rules'])
295 2
                                && is_array($v['rules'])
296 2
                                && array_keys($v['rules']) !== range(0, count($v['rules']) - 1);
297 15
                        })
298
                        ->then(function ($v) {
299
                            $v['rules'] = [$v['rules']];
300
301
                            return $v;
302 15
                        })
303 15
                    ->end()
304 15
                    ->canBeEnabled()
305 15
                    ->children()
306 15
                        ->scalarNode('service')->defaultNull()->end()
307 15
                        ->arrayNode('rules')
308 15
                            ->performNoDeepMerging()
309 15
                            ->prototype('array')
310 15
                                ->fixXmlConfig('priority', 'priorities')
311 15
                                ->fixXmlConfig('attribute', 'attributes')
312 15
                                ->children()
313 15
                                    ->scalarNode('path')->defaultNull()->info('URL path info')->end()
314 15
                                    ->scalarNode('host')->defaultNull()->info('URL host name')->end()
315 15
                                    ->variableNode('methods')->defaultNull()->info('Method for URL')->end()
316 15
                                    ->arrayNode('attributes')
317 15
                                        ->useAttributeAsKey('name')
318 15
                                        ->prototype('variable')->end()
319 15
                                    ->end()
320 15
                                    ->booleanNode('stop')->defaultFalse()->end()
321 15
                                    ->booleanNode('prefer_extension')->defaultTrue()->end()
322 15
                                    ->scalarNode('fallback_format')->defaultValue('html')->end()
323 15
                                    ->arrayNode('priorities')
324
                                        ->beforeNormalization()->ifString()->then(function ($v) {
325
                                            return preg_split('/\s*,\s*/', $v);
326 15
                                        })->end()
327 15
                                        ->prototype('scalar')->end()
328 15
                                    ->end()
329 15
                                ->end()
330 15
                            ->end()
331 15
                        ->end()
332 15
                    ->end()
333 15
                ->end()
334 15
            ->end();
335 15
    }
336
337 15
    private function addVersioningSection(ArrayNodeDefinition $rootNode): void
338
    {
339
        $rootNode
340 15
        ->children()
341 15
            ->arrayNode('versioning')
342 15
                ->canBeEnabled()
343 15
                ->children()
344 15
                    ->scalarNode('default_version')->defaultNull()->end()
345 15
                    ->arrayNode('resolvers')
346 15
                        ->addDefaultsIfNotSet()
347 15
                        ->children()
348 15
                            ->arrayNode('query')
349 15
                                ->canBeDisabled()
350 15
                                ->children()
351 15
                                    ->scalarNode('parameter_name')->defaultValue('version')->end()
352 15
                                ->end()
353 15
                            ->end()
354 15
                            ->arrayNode('custom_header')
355 15
                                ->canBeDisabled()
356 15
                                ->children()
357 15
                                    ->scalarNode('header_name')->defaultValue('X-Accept-Version')->end()
358 15
                                ->end()
359 15
                            ->end()
360 15
                            ->arrayNode('media_type')
361 15
                                ->canBeDisabled()
362 15
                                ->children()
363 15
                                    ->scalarNode('regex')->defaultValue('/(v|version)=(?P<version>[0-9\.]+)/')->end()
364 15
                                ->end()
365 15
                            ->end()
366 15
                        ->end()
367 15
                    ->end()
368 15
                    ->arrayNode('guessing_order')
369 15
                        ->defaultValue(['query', 'custom_header', 'media_type'])
370 15
                        ->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 15
                            })
378 15
                            ->thenInvalid('Versioning guessing order can only contain "query", "custom_header", "media_type".')
379 15
                        ->end()
380 15
                        ->prototype('scalar')->end()
381 15
                    ->end()
382 15
                ->end()
383 15
            ->end()
384 15
        ->end();
385 15
    }
386
387 15
    private function addExceptionSection(ArrayNodeDefinition $rootNode): void
388
    {
389
        $rootNode
390 15
            ->children()
391 15
                ->arrayNode('exception')
392 15
                    ->fixXmlConfig('code', 'codes')
393 15
                    ->fixXmlConfig('message', 'messages')
394 15
                    ->addDefaultsIfNotSet()
395 15
                    ->canBeEnabled()
396 15
                    ->children()
397 15
                        ->booleanNode('map_exception_codes')
398 15
                            ->defaultFalse()
399 15
                            ->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 15
                        ->end()
401 15
                        ->booleanNode('exception_listener')
402 15
                            ->defaultValue(false)
403 15
                            ->validate()
404 15
                                ->ifTrue()
405 15
                                ->thenInvalid('only "false" is supported')
406 15
                            ->end()
407 15
                        ->end()
408 15
                        ->booleanNode('serialize_exceptions')
409 15
                            ->defaultValue(false)
410 15
                            ->validate()
411 15
                                ->ifTrue()
412 15
                                ->thenInvalid('only "false" is supported')
413 15
                            ->end()
414 15
                        ->end()
415 15
                        ->enumNode('flatten_exception_format')
416 15
                            ->defaultValue('legacy')
417 15
                            ->values(['legacy', 'rfc7807'])
418 15
                        ->end()
419 15
                        ->booleanNode('serializer_error_renderer')->defaultValue(false)->end()
420 15
                        ->arrayNode('codes')
421 15
                            ->useAttributeAsKey('name')
422 15
                            ->beforeNormalization()
423 15
                                ->ifArray()
424
                                ->then(function (array $items) {
425 12
                                    foreach ($items as &$item) {
426 12
                                        if (is_int($item)) {
427 2
                                            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 3
                                    return $items;
438 15
                                })
439 15
                            ->end()
440 15
                            ->prototype('integer')->end()
441
442 15
                            ->validate()
443 15
                            ->ifArray()
444
                                ->then(function (array $items) {
445 3
                                    foreach ($items as $class => $code) {
446 3
                                        $this->testExceptionExists($class);
447
                                    }
448
449 2
                                    return $items;
450 15
                                })
451 15
                            ->end()
452 15
                        ->end()
453 15
                        ->arrayNode('messages')
454 15
                            ->useAttributeAsKey('name')
455 15
                            ->prototype('boolean')->end()
456 15
                            ->validate()
457 15
                                ->ifArray()
458
                                ->then(function (array $items) {
459 1
                                    foreach ($items as $class => $nomatter) {
460 1
                                        $this->testExceptionExists($class);
461
                                    }
462
463
                                    return $items;
464 15
                                })
465 15
                            ->end()
466 15
                        ->end()
467 15
                        ->booleanNode('debug')
468 15
                            ->defaultValue($this->debug)
469 15
                        ->end()
470 15
                    ->end()
471 15
                ->end()
472 15
            ->end();
473 15
    }
474
475 4
    private function testExceptionExists(string $throwable): void
476
    {
477 4
        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 2
    }
481
}
482