Completed
Pull Request — master (#97)
by Tobias
08:26
created

Configuration   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 345
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 98.47%

Importance

Changes 11
Bugs 0 Features 5
Metric Value
wmc 20
c 11
b 0
f 5
lcom 1
cbo 5
dl 0
loc 345
ccs 258
cts 262
cp 0.9847
rs 10

6 Methods

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