Completed
Pull Request — master (#87)
by Tobias
07:05
created

Configuration   A

Complexity

Total Complexity 18

Size/Duplication

Total Lines 328
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 98.5%

Importance

Changes 11
Bugs 0 Features 5
Metric Value
wmc 18
c 11
b 0
f 5
lcom 1
cbo 5
dl 0
loc 328
ccs 263
cts 267
cp 0.985
rs 10

5 Methods

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