Completed
Pull Request — master (#105)
by Márk
08:08
created

Configuration::getConfigTreeBuilder()   D

Complexity

Conditions 13
Paths 1

Size

Total Lines 107
Code Lines 89

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 89
CRAP Score 13.0017

Importance

Changes 9
Bugs 1 Features 3
Metric Value
c 9
b 1
f 3
dl 0
loc 107
ccs 89
cts 91
cp 0.978
rs 4.9922
cc 13
eloc 89
nc 1
nop 0
crap 13.0017

How to fix   Long Method    Complexity   

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
namespace Http\HttplugBundle\DependencyInjection;
4
5
use Symfony\Component\Config\Definition\ArrayNode;
6
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
7
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
8
use Symfony\Component\Config\Definition\ConfigurationInterface;
9
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
10
11
/**
12
 * This class contains the configuration information for the bundle.
13
 *
14
 * This information is solely responsible for how the different configuration
15
 * sections are normalized, and merged.
16
 *
17
 * @author David Buchmann <[email protected]>
18
 * @author Tobias Nyholm <[email protected]>
19
 */
20
class Configuration implements ConfigurationInterface
21
{
22
    /**
23
     * Whether to use the debug mode.
24
     *
25
     * @see https://github.com/doctrine/DoctrineBundle/blob/v1.5.2/DependencyInjection/Configuration.php#L31-L41
26
     *
27
     * @var bool
28
     */
29
    private $debug;
30
31
    /**
32
     * @param bool $debug
33
     */
34 12
    public function __construct($debug)
35
    {
36 12
        $this->debug = (bool) $debug;
37 12
    }
38
39
    /**
40
     * {@inheritdoc}
41
     */
42 12
    public function getConfigTreeBuilder()
43
    {
44 12
        $treeBuilder = new TreeBuilder();
45 12
        $rootNode = $treeBuilder->root('httplug');
46
47 12
        $this->configureClients($rootNode);
48 12
        $this->configurePlugins($rootNode);
49
50
        $rootNode
51 12
            ->validate()
52
                ->ifTrue(function ($v) {
53 11
                    return !empty($v['classes']['client'])
54 11
                        || !empty($v['classes']['message_factory'])
55 8
                        || !empty($v['classes']['uri_factory'])
56 11
                        || !empty($v['classes']['stream_factory']);
57 12
                })
58
                ->then(function ($v) {
59 3
                    foreach ($v['classes'] as $key => $class) {
60 3
                        if (null !== $class && !class_exists($class)) {
61 1
                            throw new InvalidConfigurationException(sprintf(
62 1
                                'Class %s specified for httplug.classes.%s does not exist.',
63 1
                                $class,
64
                                $key
65 1
                            ));
66
                        }
67 2
                    }
68
69 2
                    return $v;
70 12
                })
71 12
            ->end()
72 12
            ->beforeNormalization() // @deprecated toolbar in 1.3.0, use profiling instead
73 12
                ->ifTrue(function ($v) {
74 12
                    return is_array($v) && array_key_exists('toolbar', $v) && is_array($v['toolbar']);
75 12
                })
76 12
                ->then(function ($v) {
77 12
                    if (array_key_exists('enabled', $v['toolbar']) && 'auto' === $v['toolbar']['enabled']) {
78 12
                        $v['toolbar']['enabled'] = $this->debug; // @deprecated value auto in 1.3.0
79 12
                    }
80 12
81 12
                    if (array_key_exists('profiling', $v) && is_array($v['profiling'])) {
82 12
                        $v['profiling'] = array_merge($v['profiling'], $v['toolbar']);
83 12
                    } else {
84 12
                        $v['profiling'] = $v['toolbar'];
85 12
                    }
86 12
87 12
                    unset($v['toolbar']);
88 12
89 12
                    return $v;
90 12
                })
91 12
            ->end()
92 12
            ->children()
93 12
                ->arrayNode('main_alias')
94 12
                    ->addDefaultsIfNotSet()
95 12
                    ->info('Configure which service the main alias point to.')
96 12
                    ->children()
97 12
                        ->scalarNode('client')->defaultValue('httplug.client.default')->end()
98 12
                        ->scalarNode('message_factory')->defaultValue('httplug.message_factory.default')->end()
99 12
                        ->scalarNode('uri_factory')->defaultValue('httplug.uri_factory.default')->end()
100
                        ->scalarNode('stream_factory')->defaultValue('httplug.stream_factory.default')->end()
101 1
                    ->end()
102 12
                ->end()
103 12
                ->arrayNode('classes')
104 12
                    ->addDefaultsIfNotSet()
105 12
                    ->info('Overwrite a service class instead of using the discovery mechanism.')
106 12
                    ->children()
107 12
                        ->scalarNode('client')->defaultNull()->end()
108 12
                        ->scalarNode('message_factory')->defaultNull()->end()
109 12
                        ->scalarNode('uri_factory')->defaultNull()->end()
110 12
                        ->scalarNode('stream_factory')->defaultNull()->end()
111 12
                    ->end()
112 12
                ->end()
113 12
                ->arrayNode('profiling')
114 12
                    ->addDefaultsIfNotSet()
115 12
                    ->treatFalseLike(['enabled' => false])
116 12
                    ->treatTrueLike(['enabled' => true])
117 12
                    ->treatNullLike(['enabled' => $this->debug])
118 12
                    ->info('Extend the debug profiler with information about requests.')
119 12
                    ->children()
120 12
                        ->booleanNode('enabled')
121 12
                            ->info('Turn the toolbar on or off. Defaults to kernel debug mode.')
122 12
                            ->defaultValue($this->debug)
123 12
                        ->end()
124 12
                        ->scalarNode('formatter')->defaultNull()->end()
125 12
                        ->integerNode('captured_body_length')
126 12
                            ->defaultValue(0)
127 12
                            ->info('Limit long HTTP message bodies to x characters. If set to 0 we do not read the message body. Only available with the default formatter (FullHttpMessageFormatter).')
128 12
                        ->end()
129 12
                    ->end()
130
                ->end()
131 12
                ->arrayNode('discovery')
132
                    ->addDefaultsIfNotSet()
133
                    ->info('Control what clients should be found by the discovery.')
134 12
                    ->children()
135
                        ->scalarNode('client')
136 12
                            ->defaultValue('auto')
137 12
                            ->info('Set to "auto" to see auto discovered client in the web profiler. If provided a service id for a client then this client will be found by auto discovery.')
138 12
                        ->end()
139
                        ->scalarNode('async_client')
140 3
                            ->defaultNull()
141 3
                            ->info('Set to "auto" to see auto discovered client in the web profiler. If provided a service id for a client then this client will be found by auto discovery.')
142
                        ->end()
143
                    ->end()
144
                ->end()
145 12
            ->end();
146 12
147 12
        return $treeBuilder;
148 12
    }
149 12
150 12
    protected function configureClients(ArrayNodeDefinition $root)
151 12
    {
152 12
        $root->children()
153 12
            ->arrayNode('clients')
154 12
                ->validate()
155 12
                    ->ifTrue(function ($clients) {
156 12
                        foreach ($clients as $name => $config) {
157 12
                            // Make sure we only allow one of these to be true
158 12
                            return (bool) $config['flexible_client'] + (bool) $config['http_methods_client'] + (bool) $config['batch_client'] >= 2;
159 12
                        }
160 12
161 12
                        return false;
162 12
                    })
163 12
                    ->thenInvalid('A http client can\'t be decorated with both FlexibleHttpClient and HttpMethodsClient. Only one of the following options can be true. ("flexible_client", "http_methods_client")')->end()
164 12
                ->useAttributeAsKey('name')
165 12
                ->prototype('array')
166 12
                ->children()
167 12
                    ->scalarNode('factory')
168 12
                        ->isRequired()
169 12
                        ->cannotBeEmpty()
170 12
                        ->info('The service id of a factory to use when creating the adapter.')
171
                    ->end()
172
                    ->booleanNode('flexible_client')
173
                        ->defaultFalse()
174
                        ->info('Set to true to get the client wrapped in a FlexibleHttpClient which emulates async or sync behavior.')
175 12
                    ->end()
176
                    ->booleanNode('http_methods_client')
177 12
                        ->defaultFalse()
178 12
                        ->info('Set to true to get the client wrapped in a HttpMethodsClient which emulates provides functions for HTTP verbs.')
179 12
                    ->end()
180 12
                    ->booleanNode('batch_client')
181 12
                        ->defaultFalse()
182
                        ->info('Set to true to get the client wrapped in a BatchClient which allows you to send multiple request at the same time.')
183 12
                    ->end()
184 12
                    ->arrayNode('plugins')
185 12
                        ->info('A list of service ids of plugins. The order is important.')
186 12
                        ->prototype('scalar')->end()
187 12
                    ->end()
188 12
                    ->variableNode('config')->defaultValue([])->end()
189 12
                ->end()
190 12
            ->end();
191 12
    }
192 12
193 12
    /**
194 12
     * @param ArrayNodeDefinition $root
195 12
     */
196 12
    protected function configurePlugins(ArrayNodeDefinition $root)
197 12
    {
198 12
        $root->children()
199 12
            ->arrayNode('plugins')
200 12
                ->addDefaultsIfNotSet()
201 12
                ->children()
202 12
                    ->append($this->addAuthenticationPluiginNode())
203 12
204 12
                    ->arrayNode('cache')
205 12
                    ->canBeEnabled()
206
                    ->addDefaultsIfNotSet()
207 12
                        ->children()
208 12
                            ->scalarNode('cache_pool')
209 12
                                ->info('This must be a service id to a service implementing Psr\Cache\CacheItemPoolInterface')
210 12
                                ->isRequired()
211 12
                                ->cannotBeEmpty()
212 12
                            ->end()
213 12
                            ->scalarNode('stream_factory')
214 12
                                ->info('This must be a service id to a service implementing Http\Message\StreamFactory')
215 12
                                ->defaultValue('httplug.stream_factory')
216 12
                                ->cannotBeEmpty()
217
                            ->end()
218 12
                            ->arrayNode('config')
219 12
                                ->addDefaultsIfNotSet()
220 12
                                ->children()
221 12
                                    ->scalarNode('default_ttl')->defaultNull()->end()
222 12
                                    ->scalarNode('respect_cache_headers')->defaultTrue()->end()
223 12
                                ->end()
224 12
                            ->end()
225
                        ->end()
226 12
                    ->end() // End cache plugin
227 12
228 12
                    ->arrayNode('cookie')
229 12
                    ->canBeEnabled()
230 12
                        ->children()
231 12
                            ->scalarNode('cookie_jar')
232 12
                                ->info('This must be a service id to a service implementing Http\Message\CookieJar')
233 12
                                ->isRequired()
234 12
                                ->cannotBeEmpty()
235 12
                            ->end()
236
                        ->end()
237 12
                    ->end() // End cookie plugin
238 12
239 12
                    ->arrayNode('decoder')
240 12
                    ->canBeDisabled()
241 12
                    ->addDefaultsIfNotSet()
242 12
                        ->children()
243 12
                            ->scalarNode('use_content_encoding')->defaultTrue()->end()
244 12
                        ->end()
245 12
                    ->end() // End decoder plugin
246 12
247 12
                    ->arrayNode('history')
248 12
                    ->canBeEnabled()
249 12
                        ->children()
250 12
                            ->scalarNode('journal')
251 12
                                ->info('This must be a service id to a service implementing Http\Client\Plugin\Journal')
252
                                ->isRequired()
253 12
                                ->cannotBeEmpty()
254 12
                            ->end()
255 12
                        ->end()
256 12
                    ->end() // End history plugin
257 12
258 12
                    ->arrayNode('logger')
259 12
                    ->canBeDisabled()
260 12
                    ->addDefaultsIfNotSet()
261
                        ->children()
262 12
                            ->scalarNode('logger')
263 12
                                ->info('This must be a service id to a service implementing Psr\Log\LoggerInterface')
264 12
                                ->defaultValue('logger')
265 12
                                ->cannotBeEmpty()
266 12
                            ->end()
267 12
                            ->scalarNode('formatter')
268 12
                                ->info('This must be a service id to a service implementing Http\Message\Formatter')
269
                                ->defaultNull()
270 12
                            ->end()
271 12
                        ->end()
272 12
                    ->end() // End logger plugin
273 12
274 12
                    ->arrayNode('redirect')
275 12
                    ->canBeDisabled()
276 12
                    ->addDefaultsIfNotSet()
277 12
                        ->children()
278 12
                            ->scalarNode('preserve_header')->defaultTrue()->end()
279 12
                            ->scalarNode('use_default_for_multiple')->defaultTrue()->end()
280 12
                        ->end()
281
                    ->end() // End redirect plugin
282 12
283 12
                    ->arrayNode('retry')
284 12
                    ->canBeDisabled()
285 12
                    ->addDefaultsIfNotSet()
286
                        ->children()
287
                            ->scalarNode('retry')->defaultValue(1)->end()
288
                        ->end()
289
                    ->end() // End retry plugin
290
291
                    ->arrayNode('stopwatch')
292 12
                    ->canBeDisabled()
293
                    ->addDefaultsIfNotSet()
294 12
                        ->children()
295 12
                            ->scalarNode('stopwatch')
296
                                ->info('This must be a service id to a service extending Symfony\Component\Stopwatch\Stopwatch')
297 12
                                ->defaultValue('debug.stopwatch')
298 12
                                ->cannotBeEmpty()
299 12
                            ->end()
300 12
                        ->end()
301 12
                    ->end() // End stopwatch plugin
302 2
303 2
                ->end()
304 1
            ->end()
305 1
        ->end();
306 2
    }
307 1
308 1
    /**
309 2
     * Add configuration for authentication plugin.
310 2
     *
311 1
     * @return ArrayNodeDefinition|\Symfony\Component\Config\Definition\Builder\NodeDefinition
312 1
     */
313 1
    private function addAuthenticationPluiginNode()
314 1
    {
315 1
        $builder = new TreeBuilder();
316
        $node = $builder->root('authentication');
317 1
        $node
318 12
            ->useAttributeAsKey('name')
319 12
            ->prototype('array')
320 12
                ->validate()
321 12
                    ->always()
322 12
                    ->then(function ($config) {
323 12
                        switch ($config['type']) {
324 12
                            case 'basic':
325 12
                                $this->validateAuthenticationType(['username', 'password'], $config, 'basic');
326 12
                                break;
327 12
                            case 'bearer':
328 12
                                $this->validateAuthenticationType(['token'], $config, 'bearer');
329 12
                                break;
330 12
                            case 'service':
331 12
                                $this->validateAuthenticationType(['service'], $config, 'service');
332 12
                                break;
333
                            case 'wsse':
334 12
                                $this->validateAuthenticationType(['username', 'password'], $config, 'wsse');
335
                                break;
336
                        }
337
338
                        return $config;
339
                    })
340
                ->end()
341
                ->children()
342
                    ->enumNode('type')
343
                        ->values(['basic', 'bearer', 'wsse', 'service'])
344
                        ->isRequired()
345
                        ->cannotBeEmpty()
346 2
                    ->end()
347
                    ->scalarNode('username')->end()
348 2
                    ->scalarNode('password')->end()
349 2
                    ->scalarNode('token')->end()
350 2
                    ->scalarNode('service')->end()
351 2
                    ->end()
352
                ->end()
353 2
            ->end(); // End authentication plugin
354 1
355
        return $node;
356
    }
357 1
358 1
    /**
359 1
     * Validate that the configuration fragment has the specified keys and none other.
360 1
     *
361 1
     * @param array  $expected Fields that must exist
362 1
     * @param array  $actual   Actual configuration hashmap
363
     * @param string $authName Name of authentication method for error messages
364
     *
365
     * @throws InvalidConfigurationException If $actual does not have exactly the keys specified in $expected (plus 'type')
366
     */
367
    private function validateAuthenticationType(array $expected, array $actual, $authName)
368
    {
369
        unset($actual['type']);
370
        $actual = array_keys($actual);
371
        sort($actual);
372
        sort($expected);
373
374
        if ($expected === $actual) {
375
            return;
376
        }
377
378
        throw new InvalidConfigurationException(sprintf(
379
            'Authentication "%s" requires %s but got %s',
380
            $authName,
381
            implode(', ', $expected),
382
            implode(', ', $actual)
383
        ));
384
    }
385
}
386