Completed
Pull Request — master (#2227)
by
unknown
09:01 queued 07:35
created

Configuration::addViewSection()   B

Complexity

Conditions 3
Paths 1

Size

Total Lines 68

Duplication

Lines 3
Ratio 4.41 %

Code Coverage

Tests 61
CRAP Score 3

Importance

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