Completed
Pull Request — master (#111)
by David
07:41
created

Configuration::configureClients()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 58
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 51
CRAP Score 3.0004

Importance

Changes 5
Bugs 0 Features 4
Metric Value
c 5
b 0
f 4
dl 0
loc 58
ccs 51
cts 53
cp 0.9623
rs 9.639
cc 3
eloc 51
nc 1
nop 1
crap 3.0004

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
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 14
    public function __construct($debug)
35
    {
36 14
        $this->debug = (bool) $debug;
37 14
    }
38
39
    /**
40
     * {@inheritdoc}
41
     */
42 14
    public function getConfigTreeBuilder()
43
    {
44 14
        $treeBuilder = new TreeBuilder();
45 14
        $rootNode = $treeBuilder->root('httplug');
46
47 14
        $this->configureClients($rootNode);
48 14
        $this->configurePlugins($rootNode);
49
50
        $rootNode
51 14
            ->validate()
52
                ->ifTrue(function ($v) {
53 12
                    return !empty($v['classes']['client'])
54 12
                        || !empty($v['classes']['message_factory'])
55 9
                        || !empty($v['classes']['uri_factory'])
56 12
                        || !empty($v['classes']['stream_factory']);
57 14
                })
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 14
                })
71 14
            ->end()
72 14
            ->beforeNormalization()
73
                ->ifTrue(function ($v) {
74 14
                    return is_array($v) && array_key_exists('toolbar', $v) && is_array($v['toolbar']);
75 14
                })
76
                ->then(function ($v) {
77 4
                    if (array_key_exists('profiling', $v)) {
78 1
                        throw new InvalidConfigurationException('Can\'t configure both "toolbar" and "profiling" section. The "toolbar" config is deprecated as of version 1.3.0, please only use "profiling".');
79
                    }
80
81 3
                    @trigger_error('"httplug.toolbar" config is deprecated since version 1.3 and will be removed in 2.0. Use "httplug.profiling" instead.', E_USER_DEPRECATED);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
82
83 3
                    if (array_key_exists('enabled', $v['toolbar']) && 'auto' === $v['toolbar']['enabled']) {
84 1
                        @trigger_error('"auto" value in "httplug.toolbar" config is deprecated since version 1.3 and will be removed in 2.0. Use a boolean value instead.', E_USER_DEPRECATED);
1 ignored issue
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
85 1
                        $v['toolbar']['enabled'] = $this->debug;
86 1
                    }
87
88 3
                    $v['profiling'] = $v['toolbar'];
89
90 3
                    unset($v['toolbar']);
91
92 3
                    return $v;
93 14
                })
94 14
            ->end()
95 14
            ->fixXmlConfig('client')
96 14
            ->children()
97 14
                ->arrayNode('main_alias')
98 14
                    ->addDefaultsIfNotSet()
99 14
                    ->info('Configure which service the main alias point to.')
100 14
                    ->children()
101 14
                        ->scalarNode('client')->defaultValue('httplug.client.default')->end()
102 14
                        ->scalarNode('message_factory')->defaultValue('httplug.message_factory.default')->end()
103 14
                        ->scalarNode('uri_factory')->defaultValue('httplug.uri_factory.default')->end()
104 14
                        ->scalarNode('stream_factory')->defaultValue('httplug.stream_factory.default')->end()
105 14
                    ->end()
106 14
                ->end()
107 14
                ->arrayNode('classes')
108 14
                    ->addDefaultsIfNotSet()
109 14
                    ->info('Overwrite a service class instead of using the discovery mechanism.')
110 14
                    ->children()
111 14
                        ->scalarNode('client')->defaultNull()->end()
112 14
                        ->scalarNode('message_factory')->defaultNull()->end()
113 14
                        ->scalarNode('uri_factory')->defaultNull()->end()
114 14
                        ->scalarNode('stream_factory')->defaultNull()->end()
115 14
                    ->end()
116 14
                ->end()
117 14
                ->arrayNode('profiling')
118 14
                    ->addDefaultsIfNotSet()
119 14
                    ->treatFalseLike(['enabled' => false])
120 14
                    ->treatTrueLike(['enabled' => true])
121 14
                    ->treatNullLike(['enabled' => $this->debug])
122 14
                    ->info('Extend the debug profiler with information about requests.')
123 14
                    ->children()
124 14
                        ->booleanNode('enabled')
125 14
                            ->info('Turn the toolbar on or off. Defaults to kernel debug mode.')
126 14
                            ->defaultValue($this->debug)
127 14
                        ->end()
128 14
                        ->scalarNode('formatter')->defaultNull()->end()
129 14
                        ->integerNode('captured_body_length')
130 14
                            ->defaultValue(0)
131 14
                            ->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).')
132 14
                        ->end()
133 14
                    ->end()
134 14
                ->end()
135 14
                ->arrayNode('discovery')
136 14
                    ->addDefaultsIfNotSet()
137 14
                    ->info('Control what clients should be found by the discovery.')
138 14
                    ->children()
139 14
                        ->scalarNode('client')
140 14
                            ->defaultValue('auto')
141 14
                            ->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 14
                        ->end()
143 14
                        ->scalarNode('async_client')
144 14
                            ->defaultNull()
145 14
                            ->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.')
146 14
                        ->end()
147 14
                    ->end()
148 14
                ->end()
149 14
            ->end();
150
151 14
        return $treeBuilder;
152
    }
153
154 14
    protected function configureClients(ArrayNodeDefinition $root)
155
    {
156 14
        $root->children()
157 14
            ->arrayNode('clients')
158 14
                ->validate()
159
                    ->ifTrue(function ($clients) {
160 4
                        foreach ($clients as $name => $config) {
161
                            // Make sure we only allow one of these to be true
162 4
                            return (bool) $config['flexible_client'] + (bool) $config['http_methods_client'] + (bool) $config['batch_client'] >= 2;
163
                        }
164
165
                        return false;
166 14
                    })
167 14
                    ->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()
168 14
                ->useAttributeAsKey('name')
169 14
                ->prototype('array')
170 14
                ->children()
171 14
                    ->scalarNode('factory')
172 14
                        ->isRequired()
173 14
                        ->cannotBeEmpty()
174 14
                        ->info('The service id of a factory to use when creating the adapter.')
175 14
                    ->end()
176 14
                    ->booleanNode('flexible_client')
177 14
                        ->defaultFalse()
178 14
                        ->info('Set to true to get the client wrapped in a FlexibleHttpClient which emulates async or sync behavior.')
179 14
                    ->end()
180 14
                    ->booleanNode('http_methods_client')
181 14
                        ->defaultFalse()
182 14
                        ->info('Set to true to get the client wrapped in a HttpMethodsClient which emulates provides functions for HTTP verbs.')
183 14
                    ->end()
184 14
                    ->booleanNode('batch_client')
185 14
                        ->defaultFalse()
186 14
                        ->info('Set to true to get the client wrapped in a BatchClient which allows you to send multiple request at the same time.')
187 14
                    ->end()
188 14
                    ->arrayNode('options')
189 14
                        ->validate()
190
                            ->ifTrue(function ($options) {
191 1
                                return array_key_exists('default_host', $options) && array_key_exists('force_host', $options);
192 14
                            })
193 14
                            ->thenInvalid('You can only set one of default_host and force_host for each client')
194 14
                        ->end()
195 14
                        ->children()
196 14
                            ->scalarNode('default_host')
197 14
                                ->info('Configure the AddHostPlugin for this client. Add host if request is only for a path.')
198 14
                            ->end()
199 14
                            ->scalarNode('force_host')
200 14
                                ->info('Configure the AddHostPlugin for this client. Send all requests to this host regardless of host in request.')
201 14
                            ->end()
202 14
                        ->end()
203 14
                    ->end()
204 14
                    ->arrayNode('plugins')
205 14
                        ->info('A list of service ids of plugins. The order is important.')
206 14
                        ->prototype('scalar')->end()
207 14
                    ->end()
208 14
                    ->variableNode('config')->defaultValue([])->end()
209 14
                ->end()
210 14
            ->end();
211 14
    }
212
213
    /**
214
     * @param ArrayNodeDefinition $root
215
     */
216 14
    protected function configurePlugins(ArrayNodeDefinition $root)
217
    {
218 14
        $root->children()
219 14
            ->arrayNode('plugins')
220 14
                ->addDefaultsIfNotSet()
221 14
                ->children()
222 14
                    ->append($this->addAuthenticationPluginNode())
223
224 14
                    ->arrayNode('cache')
225 14
                    ->canBeEnabled()
226 14
                    ->addDefaultsIfNotSet()
227 14
                        ->children()
228 14
                            ->scalarNode('cache_pool')
229 14
                                ->info('This must be a service id to a service implementing Psr\Cache\CacheItemPoolInterface')
230 14
                                ->isRequired()
231 14
                                ->cannotBeEmpty()
232 14
                            ->end()
233 14
                            ->scalarNode('stream_factory')
234 14
                                ->info('This must be a service id to a service implementing Http\Message\StreamFactory')
235 14
                                ->defaultValue('httplug.stream_factory')
236 14
                                ->cannotBeEmpty()
237 14
                            ->end()
238 14
                            ->arrayNode('config')
239 14
                                ->addDefaultsIfNotSet()
240 14
                                ->children()
241 14
                                    ->scalarNode('default_ttl')->defaultNull()->end()
242 14
                                    ->scalarNode('respect_cache_headers')->defaultTrue()->end()
243 14
                                ->end()
244 14
                            ->end()
245 14
                        ->end()
246 14
                    ->end() // End cache plugin
247
248 14
                    ->arrayNode('cookie')
249 14
                    ->canBeEnabled()
250 14
                        ->children()
251 14
                            ->scalarNode('cookie_jar')
252 14
                                ->info('This must be a service id to a service implementing Http\Message\CookieJar')
253 14
                                ->isRequired()
254 14
                                ->cannotBeEmpty()
255 14
                            ->end()
256 14
                        ->end()
257 14
                    ->end() // End cookie plugin
258
259 14
                    ->arrayNode('decoder')
260 14
                    ->canBeDisabled()
261 14
                    ->addDefaultsIfNotSet()
262 14
                        ->children()
263 14
                            ->scalarNode('use_content_encoding')->defaultTrue()->end()
264 14
                        ->end()
265 14
                    ->end() // End decoder plugin
266
267 14
                    ->arrayNode('history')
268 14
                    ->canBeEnabled()
269 14
                        ->children()
270 14
                            ->scalarNode('journal')
271 14
                                ->info('This must be a service id to a service implementing Http\Client\Plugin\Journal')
272 14
                                ->isRequired()
273 14
                                ->cannotBeEmpty()
274 14
                            ->end()
275 14
                        ->end()
276 14
                    ->end() // End history plugin
277
278 14
                    ->arrayNode('logger')
279 14
                    ->canBeDisabled()
280 14
                    ->addDefaultsIfNotSet()
281 14
                        ->children()
282 14
                            ->scalarNode('logger')
283 14
                                ->info('This must be a service id to a service implementing Psr\Log\LoggerInterface')
284 14
                                ->defaultValue('logger')
285 14
                                ->cannotBeEmpty()
286 14
                            ->end()
287 14
                            ->scalarNode('formatter')
288 14
                                ->info('This must be a service id to a service implementing Http\Message\Formatter')
289 14
                                ->defaultNull()
290 14
                            ->end()
291 14
                        ->end()
292 14
                    ->end() // End logger plugin
293
294 14
                    ->arrayNode('redirect')
295 14
                    ->canBeDisabled()
296 14
                    ->addDefaultsIfNotSet()
297 14
                        ->children()
298 14
                            ->scalarNode('preserve_header')->defaultTrue()->end()
299 14
                            ->scalarNode('use_default_for_multiple')->defaultTrue()->end()
300 14
                        ->end()
301 14
                    ->end() // End redirect plugin
302
303 14
                    ->arrayNode('retry')
304 14
                    ->canBeDisabled()
305 14
                    ->addDefaultsIfNotSet()
306 14
                        ->children()
307 14
                            ->scalarNode('retry')->defaultValue(1)->end()
308 14
                        ->end()
309 14
                    ->end() // End retry plugin
310
311 14
                    ->arrayNode('stopwatch')
312 14
                    ->canBeDisabled()
313 14
                    ->addDefaultsIfNotSet()
314 14
                        ->children()
315 14
                            ->scalarNode('stopwatch')
316 14
                                ->info('This must be a service id to a service extending Symfony\Component\Stopwatch\Stopwatch')
317 14
                                ->defaultValue('debug.stopwatch')
318 14
                                ->cannotBeEmpty()
319 14
                            ->end()
320 14
                        ->end()
321 14
                    ->end() // End stopwatch plugin
322
323 14
                ->end()
324 14
            ->end()
325 14
        ->end();
326 14
    }
327
328
    /**
329
     * Add configuration for authentication plugin.
330
     *
331
     * @return ArrayNodeDefinition|\Symfony\Component\Config\Definition\Builder\NodeDefinition
332
     */
333 14
    private function addAuthenticationPluginNode()
334
    {
335 14
        $builder = new TreeBuilder();
336 14
        $node = $builder->root('authentication');
337
        $node
338 14
            ->useAttributeAsKey('name')
339 14
            ->prototype('array')
340 14
                ->validate()
341 14
                    ->always()
342 14
                    ->then(function ($config) {
343 2
                        switch ($config['type']) {
344 2
                            case 'basic':
345 1
                                $this->validateAuthenticationType(['username', 'password'], $config, 'basic');
346 1
                                break;
347 2
                            case 'bearer':
348 1
                                $this->validateAuthenticationType(['token'], $config, 'bearer');
349 1
                                break;
350 2
                            case 'service':
351 2
                                $this->validateAuthenticationType(['service'], $config, 'service');
352 1
                                break;
353 1
                            case 'wsse':
354 1
                                $this->validateAuthenticationType(['username', 'password'], $config, 'wsse');
355 1
                                break;
356 1
                        }
357
358 1
                        return $config;
359 14
                    })
360 14
                ->end()
361 14
                ->children()
362 14
                    ->enumNode('type')
363 14
                        ->values(['basic', 'bearer', 'wsse', 'service'])
364 14
                        ->isRequired()
365 14
                        ->cannotBeEmpty()
366 14
                    ->end()
367 14
                    ->scalarNode('username')->end()
368 14
                    ->scalarNode('password')->end()
369 14
                    ->scalarNode('token')->end()
370 14
                    ->scalarNode('service')->end()
371 14
                    ->end()
372 14
                ->end()
373 14
            ->end(); // End authentication plugin
374
375 14
        return $node;
376
    }
377
378
    /**
379
     * Validate that the configuration fragment has the specified keys and none other.
380
     *
381
     * @param array  $expected Fields that must exist
382
     * @param array  $actual   Actual configuration hashmap
383
     * @param string $authName Name of authentication method for error messages
384
     *
385
     * @throws InvalidConfigurationException If $actual does not have exactly the keys specified in $expected (plus 'type')
386
     */
387 2
    private function validateAuthenticationType(array $expected, array $actual, $authName)
388
    {
389 2
        unset($actual['type']);
390 2
        $actual = array_keys($actual);
391 2
        sort($actual);
392 2
        sort($expected);
393
394 2
        if ($expected === $actual) {
395 1
            return;
396
        }
397
398 1
        throw new InvalidConfigurationException(sprintf(
399 1
            'Authentication "%s" requires %s but got %s',
400 1
            $authName,
401 1
            implode(', ', $expected),
402 1
            implode(', ', $actual)
403 1
        ));
404
    }
405
}
406