Completed
Push — master ( 853c4f...e19839 )
by David
15:41 queued 10s
created

Configuration::getConfigTreeBuilder()   C

Complexity

Conditions 12
Paths 1

Size

Total Lines 111

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 94
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 111
ccs 94
cts 94
cp 1
rs 5.5733
c 0
b 0
f 0
cc 12
nc 1
nop 0
crap 12

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 Http\Client\Common\Plugin\Cache\Generator\CacheKeyGenerator;
6
use Http\Client\Common\Plugin\Journal;
7
use Http\Message\CookieJar;
8
use Http\Message\Formatter;
9
use Http\Message\StreamFactory;
10
use Psr\Cache\CacheItemPoolInterface;
11
use Psr\Log\LoggerInterface;
12
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
13
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
14
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
15
use Symfony\Component\Config\Definition\ConfigurationInterface;
16
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
17
use Symfony\Component\Config\Definition\Exception\InvalidTypeException;
18
19
/**
20
 * This class contains the configuration information for the bundle.
21
 *
22
 * This information is solely responsible for how the different configuration
23
 * sections are normalized, and merged.
24
 *
25
 * @author David Buchmann <[email protected]>
26
 * @author Tobias Nyholm <[email protected]>
27
 */
28
class Configuration implements ConfigurationInterface
29
{
30
    /**
31
     * Whether to use the debug mode.
32
     *
33
     * @see https://github.com/doctrine/DoctrineBundle/blob/v1.5.2/DependencyInjection/Configuration.php#L31-L41
34
     *
35
     * @var bool
36
     */
37
    private $debug;
38
39
    /**
40
     * @param bool $debug
41
     */
42 34
    public function __construct($debug)
43
    {
44 34
        $this->debug = (bool) $debug;
45 34
    }
46
47
    /**
48
     * {@inheritdoc}
49
     */
50 34
    public function getConfigTreeBuilder()
51
    {
52 34
        $treeBuilder = new TreeBuilder();
53 34
        $rootNode = $treeBuilder->root('httplug');
54
55 34
        $this->configureClients($rootNode);
56 34
        $this->configureSharedPlugins($rootNode);
57
58
        $rootNode
59 34
            ->validate()
60 34
                ->ifTrue(function ($v) {
61 28
                    return !empty($v['classes']['client'])
62 25
                        || !empty($v['classes']['message_factory'])
63 25
                        || !empty($v['classes']['uri_factory'])
64 28
                        || !empty($v['classes']['stream_factory']);
65 34
                })
66 34
                ->then(function ($v) {
67 3
                    foreach ($v['classes'] as $key => $class) {
68 3
                        if (null !== $class && !class_exists($class)) {
69 1
                            throw new InvalidConfigurationException(sprintf(
70 1
                                'Class %s specified for httplug.classes.%s does not exist.',
71 1
                                $class,
72 3
                                $key
73
                            ));
74
                        }
75
                    }
76
77 2
                    return $v;
78 34
                })
79 34
            ->end()
80 34
            ->beforeNormalization()
81 34
                ->ifTrue(function ($v) {
82 34
                    return is_array($v) && array_key_exists('toolbar', $v) && is_array($v['toolbar']);
83 34
                })
84 34
                ->then(function ($v) {
85 3
                    if (array_key_exists('profiling', $v)) {
86 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".');
87
                    }
88
89 2
                    @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);
90
91 2
                    if (array_key_exists('enabled', $v['toolbar']) && 'auto' === $v['toolbar']['enabled']) {
92 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);
93 1
                        $v['toolbar']['enabled'] = $this->debug;
94
                    }
95
96 2
                    $v['profiling'] = $v['toolbar'];
97
98 2
                    unset($v['toolbar']);
99
100 2
                    return $v;
101 34
                })
102 34
            ->end()
103 34
            ->fixXmlConfig('client')
104 34
            ->children()
105 34
                ->arrayNode('main_alias')
106 34
                    ->addDefaultsIfNotSet()
107 34
                    ->info('Configure which service the main alias point to.')
108 34
                    ->children()
109 34
                        ->scalarNode('client')->defaultValue('httplug.client.default')->end()
110 34
                        ->scalarNode('message_factory')->defaultValue('httplug.message_factory.default')->end()
111 34
                        ->scalarNode('uri_factory')->defaultValue('httplug.uri_factory.default')->end()
112 34
                        ->scalarNode('stream_factory')->defaultValue('httplug.stream_factory.default')->end()
113 34
                    ->end()
114 34
                ->end()
115 34
                ->arrayNode('classes')
116 34
                    ->addDefaultsIfNotSet()
117 34
                    ->info('Overwrite a service class instead of using the discovery mechanism.')
118 34
                    ->children()
119 34
                        ->scalarNode('client')->defaultNull()->end()
120 34
                        ->scalarNode('message_factory')->defaultNull()->end()
121 34
                        ->scalarNode('uri_factory')->defaultNull()->end()
122 34
                        ->scalarNode('stream_factory')->defaultNull()->end()
123 34
                    ->end()
124 34
                ->end()
125 34
                ->arrayNode('profiling')
126 34
                    ->addDefaultsIfNotSet()
127 34
                    ->treatFalseLike(['enabled' => false])
128 34
                    ->treatTrueLike(['enabled' => true])
129 34
                    ->treatNullLike(['enabled' => $this->debug])
130 34
                    ->info('Extend the debug profiler with information about requests.')
131 34
                    ->children()
132 34
                        ->booleanNode('enabled')
133 34
                            ->info('Turn the toolbar on or off. Defaults to kernel debug mode.')
134 34
                            ->defaultValue($this->debug)
135 34
                        ->end()
136 34
                        ->scalarNode('formatter')->defaultNull()->end()
137 34
                        ->integerNode('captured_body_length')
138 34
                            ->defaultValue(0)
139 34
                            ->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).')
140 34
                        ->end()
141 34
                    ->end()
142 34
                ->end()
143 34
                ->arrayNode('discovery')
144 34
                    ->addDefaultsIfNotSet()
145 34
                    ->info('Control what clients should be found by the discovery.')
146 34
                    ->children()
147 34
                        ->scalarNode('client')
148 34
                            ->defaultValue('auto')
149 34
                            ->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.')
150 34
                        ->end()
151 34
                        ->scalarNode('async_client')
152 34
                            ->defaultNull()
153 34
                            ->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.')
154 34
                        ->end()
155 34
                    ->end()
156 34
                ->end()
157 34
            ->end();
158
159 34
        return $treeBuilder;
160
    }
161
162 34
    private function configureClients(ArrayNodeDefinition $root)
163
    {
164 34
        $root->children()
165 34
            ->arrayNode('clients')
166 34
                ->useAttributeAsKey('name')
167 34
                ->prototype('array')
168 34
                ->fixXmlConfig('plugin')
169 34
                ->validate()
170 34
                    ->ifTrue(function ($config) {
171
                        // Make sure we only allow one of these to be true
172 16
                        return (bool) $config['flexible_client'] + (bool) $config['http_methods_client'] + (bool) $config['batch_client'] >= 2;
173 34
                    })
174 34
                    ->thenInvalid('A http client can\'t be decorated with several of FlexibleHttpClient, HttpMethodsClient and BatchClient. Only one of the following options can be true. ("flexible_client", "http_methods_client", "batch_client")')
175 34
                ->end()
176 34
                ->validate()
177 34
                    ->ifTrue(function ($config) {
178 16
                        return 'httplug.factory.auto' === $config['factory'] && !empty($config['config']);
179 34
                    })
180 34
                    ->thenInvalid('If you want to use the "config" key you must also specify a valid "factory".')
181 34
                ->end()
182 34
                ->validate()
183 34
                    ->ifTrue(function ($config) {
184 16
                        return !empty($config['service']) && ('httplug.factory.auto' !== $config['factory'] || !empty($config['config']));
185 34
                    })
186 34
                    ->thenInvalid('If you want to use the "service" key you cannot specify "factory" or "config".')
187 34
                ->end()
188 34
                ->children()
189 34
                    ->scalarNode('factory')
190 34
                        ->defaultValue('httplug.factory.auto')
191 34
                        ->cannotBeEmpty()
192 34
                        ->info('The service id of a factory to use when creating the adapter.')
193 34
                    ->end()
194 34
                    ->scalarNode('service')
195 34
                        ->defaultNull()
196 34
                        ->info('The service id of the client to use.')
197 34
                    ->end()
198 34
                    ->booleanNode('public')
199 34
                        ->defaultNull()
200 34
                        ->info('Set to true if you really cannot use dependency injection and need to make the client service public.')
201 34
                    ->end()
202 34
                    ->booleanNode('flexible_client')
203 34
                        ->defaultFalse()
204 34
                        ->info('Set to true to get the client wrapped in a FlexibleHttpClient which emulates async or sync behavior.')
205 34
                    ->end()
206 34
                    ->booleanNode('http_methods_client')
207 34
                        ->defaultFalse()
208 34
                        ->info('Set to true to get the client wrapped in a HttpMethodsClient which emulates provides functions for HTTP verbs.')
209 34
                    ->end()
210 34
                    ->booleanNode('batch_client')
211 34
                        ->defaultFalse()
212 34
                        ->info('Set to true to get the client wrapped in a BatchClient which allows you to send multiple request at the same time.')
213 34
                    ->end()
214 34
                    ->variableNode('config')->defaultValue([])->end()
215 34
                    ->append($this->createClientPluginNode())
216 34
                ->end()
217 34
            ->end()
218 34
        ->end();
219 34
    }
220
221
    /**
222
     * @param ArrayNodeDefinition $root
223
     */
224 34
    private function configureSharedPlugins(ArrayNodeDefinition $root)
225
    {
226
        $pluginsNode = $root
227 34
            ->children()
228 34
                ->arrayNode('plugins')
229 34
                ->info('Global plugin configuration. Plugins need to be explicitly added to clients.')
230 34
                ->addDefaultsIfNotSet()
231
            // don't call end to get the plugins node
232
        ;
233 34
        $this->addSharedPluginNodes($pluginsNode);
234 34
    }
235
236
    /**
237
     * Createplugins node of a client.
238
     *
239
     * @return ArrayNodeDefinition The plugin node
240
     */
241 34
    private function createClientPluginNode()
242
    {
243 34
        $builder = new TreeBuilder();
244 34
        $node = $builder->root('plugins');
245
246
        /** @var ArrayNodeDefinition $pluginList */
247
        $pluginList = $node
248 34
            ->info('A list of plugin service ids and client specific plugin definitions. The order is important.')
249 34
            ->prototype('array')
250
        ;
251
        $pluginList
252
            // support having just a service id in the list
253 34
            ->beforeNormalization()
254 34
                ->always(function ($plugin) {
255 9
                    if (is_string($plugin)) {
256
                        return [
257 7
                            'reference' => [
258
                                'enabled' => true,
259 7
                                'id' => $plugin,
260
                            ],
261
                        ];
262
                    }
263
264 8
                    return $plugin;
265 34
                })
266 34
            ->end()
267
268 34
            ->validate()
269 34
                ->always(function ($plugins) {
270 7
                    foreach ($plugins as $name => $definition) {
271 7
                        if ('authentication' === $name) {
272 7
                            if (!count($definition)) {
273 7
                                unset($plugins['authentication']);
274
                            }
275 7
                        } elseif (!$definition['enabled']) {
276 7
                            unset($plugins[$name]);
277
                        }
278
                    }
279
280 7
                    return $plugins;
281 34
                })
282 34
            ->end()
283
        ;
284 34
        $this->addSharedPluginNodes($pluginList, true);
285
286
        $pluginList
287 34
            ->children()
288 34
                ->arrayNode('reference')
289 34
                    ->canBeEnabled()
290 34
                    ->info('Reference to a plugin service')
291 34
                    ->children()
292 34
                        ->scalarNode('id')
293 34
                            ->info('Service id of a plugin')
294 34
                            ->isRequired()
295 34
                            ->cannotBeEmpty()
296 34
                        ->end()
297 34
                    ->end()
298 34
                ->end()
299 34
                ->arrayNode('add_host')
300 34
                    ->canBeEnabled()
301 34
                    ->addDefaultsIfNotSet()
302 34
                    ->info('Set scheme, host and port in the request URI.')
303 34
                    ->children()
304 34
                        ->scalarNode('host')
305 34
                            ->info('Host name including protocol and optionally the port number, e.g. https://api.local:8000')
306 34
                            ->isRequired()
307 34
                            ->cannotBeEmpty()
308 34
                        ->end()
309 34
                        ->scalarNode('replace')
310 34
                            ->info('Whether to replace the host if request already specifies one')
311 34
                            ->defaultValue(false)
312 34
                        ->end()
313 34
                    ->end()
314 34
                ->end()
315 34
                ->arrayNode('add_path')
316 34
                    ->canBeEnabled()
317 34
                    ->addDefaultsIfNotSet()
318 34
                    ->info('Add a base path to the request.')
319 34
                    ->children()
320 34
                        ->scalarNode('path')
321 34
                            ->info('Path to be added, e.g. /api/v1')
322 34
                            ->isRequired()
323 34
                            ->cannotBeEmpty()
324 34
                        ->end()
325 34
                    ->end()
326 34
                ->end()
327 34
                ->arrayNode('base_uri')
328 34
                    ->canBeEnabled()
329 34
                    ->addDefaultsIfNotSet()
330 34
                    ->info('Set a base URI to the request.')
331 34
                    ->children()
332 34
                        ->scalarNode('uri')
333 34
                            ->info('Base Uri including protocol, optionally the port number and prepend path, e.g. https://api.local:8000/api')
334 34
                            ->isRequired()
335 34
                            ->cannotBeEmpty()
336 34
                        ->end()
337 34
                        ->scalarNode('replace')
338 34
                            ->info('Whether to replace the host if request already specifies one')
339 34
                            ->defaultValue(false)
340 34
                        ->end()
341 34
                    ->end()
342 34
                ->end()
343 34
                ->arrayNode('header_append')
344 34
                    ->canBeEnabled()
345 34
                    ->info('Append headers to the request. If the header already exists the value will be appended to the current value.')
346 34
                    ->fixXmlConfig('header')
347 34
                    ->children()
348 34
                        ->arrayNode('headers')
349 34
                            ->info('Keys are the header names, values the header values')
350 34
                            ->normalizeKeys(false)
351 34
                            ->useAttributeAsKey('name')
352 34
                            ->prototype('scalar')->end()
353 34
                        ->end()
354 34
                    ->end()
355 34
                ->end()
356 34
                ->arrayNode('header_defaults')
357 34
                    ->canBeEnabled()
358 34
                    ->info('Set header to default value if it does not exist.')
359 34
                    ->fixXmlConfig('header')
360 34
                    ->children()
361 34
                        ->arrayNode('headers')
362 34
                            ->info('Keys are the header names, values the header values')
363 34
                            ->normalizeKeys(false)
364 34
                            ->useAttributeAsKey('name')
365 34
                            ->prototype('scalar')->end()
366 34
                        ->end()
367 34
                    ->end()
368 34
                ->end()
369 34
                ->arrayNode('header_set')
370 34
                    ->canBeEnabled()
371 34
                    ->info('Set headers to requests. If the header does not exist it wil be set, if the header already exists it will be replaced.')
372 34
                    ->fixXmlConfig('header')
373 34
                    ->children()
374 34
                        ->arrayNode('headers')
375 34
                            ->info('Keys are the header names, values the header values')
376 34
                            ->normalizeKeys(false)
377 34
                            ->useAttributeAsKey('name')
378 34
                            ->prototype('scalar')->end()
379 34
                        ->end()
380 34
                    ->end()
381 34
                ->end()
382 34
                ->arrayNode('header_remove')
383 34
                    ->canBeEnabled()
384 34
                    ->info('Remove headers from requests.')
385 34
                    ->fixXmlConfig('header')
386 34
                    ->children()
387 34
                        ->arrayNode('headers')
388 34
                            ->info('List of header names to remove')
389 34
                            ->prototype('scalar')->end()
390 34
                        ->end()
391 34
                    ->end()
392 34
                ->end()
393 34
            ->end()
394 34
        ->end();
395
396 34
        return $node;
397
    }
398
399
    /**
400
     * Add the definitions for shared plugin configurations.
401
     *
402
     * @param ArrayNodeDefinition $pluginNode The node to add to.
403
     * @param bool                $disableAll Some shared plugins are enabled by default. On the client, all are disabled by default.
404
     */
405 34
    private function addSharedPluginNodes(ArrayNodeDefinition $pluginNode, $disableAll = false)
406
    {
407 34
        $children = $pluginNode->children();
408
409 34
        $children->append($this->createAuthenticationPluginNode());
410 34
        $children->append($this->createCachePluginNode());
411
412
        $children
413 34
            ->arrayNode('cookie')
414 34
                ->canBeEnabled()
415 34
                ->children()
416 34
                    ->scalarNode('cookie_jar')
417 34
                        ->info('This must be a service id to a service implementing '.CookieJar::class)
418 34
                        ->isRequired()
419 34
                        ->cannotBeEmpty()
420 34
                    ->end()
421 34
                ->end()
422 34
            ->end();
423
        // End cookie plugin
424
425
        $children
426 34
            ->arrayNode('history')
427 34
                ->canBeEnabled()
428 34
                ->children()
429 34
                    ->scalarNode('journal')
430 34
                        ->info('This must be a service id to a service implementing '.Journal::class)
431 34
                        ->isRequired()
432 34
                        ->cannotBeEmpty()
433 34
                    ->end()
434 34
                ->end()
435 34
            ->end();
436
        // End history plugin
437
438 34
        $decoder = $children->arrayNode('decoder');
439 34
        $disableAll ? $decoder->canBeEnabled() : $decoder->canBeDisabled();
440 34
        $decoder->addDefaultsIfNotSet()
441 34
            ->children()
442 34
                ->scalarNode('use_content_encoding')->defaultTrue()->end()
443 34
            ->end()
444 34
        ->end();
445
        // End decoder plugin
446
447 34
        $logger = $children->arrayNode('logger');
448 34
        $disableAll ? $logger->canBeEnabled() : $logger->canBeDisabled();
449 34
        $logger->addDefaultsIfNotSet()
450 34
            ->children()
451 34
                ->scalarNode('logger')
452 34
                    ->info('This must be a service id to a service implementing '.LoggerInterface::class)
453 34
                    ->defaultValue('logger')
454 34
                    ->cannotBeEmpty()
455 34
                ->end()
456 34
                ->scalarNode('formatter')
457 34
                    ->info('This must be a service id to a service implementing '.Formatter::class)
458 34
                    ->defaultNull()
459 34
                ->end()
460 34
            ->end()
461 34
        ->end();
462
        // End logger plugin
463
464 34
        $redirect = $children->arrayNode('redirect');
465 34
        $disableAll ? $redirect->canBeEnabled() : $redirect->canBeDisabled();
466 34
        $redirect->addDefaultsIfNotSet()
467 34
            ->children()
468 34
                ->scalarNode('preserve_header')->defaultTrue()->end()
469 34
                ->scalarNode('use_default_for_multiple')->defaultTrue()->end()
470 34
            ->end()
471 34
        ->end();
472
        // End redirect plugin
473
474 34
        $retry = $children->arrayNode('retry');
475 34
        $disableAll ? $retry->canBeEnabled() : $retry->canBeDisabled();
476 34
        $retry->addDefaultsIfNotSet()
477 34
            ->children()
478 34
                ->scalarNode('retry')->defaultValue(1)->end() // TODO: should be called retries for consistency with the class
479 34
            ->end()
480 34
        ->end();
481
        // End retry plugin
482
483 34
        $stopwatch = $children->arrayNode('stopwatch');
484 34
        $disableAll ? $stopwatch->canBeEnabled() : $stopwatch->canBeDisabled();
485 34
        $stopwatch->addDefaultsIfNotSet()
486 34
            ->children()
487 34
                ->scalarNode('stopwatch')
488 34
                    ->info('This must be a service id to a service extending Symfony\Component\Stopwatch\Stopwatch')
489 34
                    ->defaultValue('debug.stopwatch')
490 34
                    ->cannotBeEmpty()
491 34
                ->end()
492 34
            ->end()
493 34
        ->end();
494
        // End stopwatch plugin
495 34
    }
496
497
    /**
498
     * Create configuration for authentication plugin.
499
     *
500
     * @return NodeDefinition Definition for the authentication node in the plugins list.
501
     */
502 34
    private function createAuthenticationPluginNode()
503
    {
504 34
        $builder = new TreeBuilder();
505 34
        $node = $builder->root('authentication');
506
        $node
507 34
            ->useAttributeAsKey('name')
508 34
            ->prototype('array')
509 34
                ->validate()
510 34
                    ->always()
511 34
                    ->then(function ($config) {
512 7
                        switch ($config['type']) {
513 7
                            case 'basic':
514 6
                                $this->validateAuthenticationType(['username', 'password'], $config, 'basic');
515
516 6
                                break;
517 2
                            case 'bearer':
518 1
                                $this->validateAuthenticationType(['token'], $config, 'bearer');
519
520 1
                                break;
521 2
                            case 'service':
522 2
                                $this->validateAuthenticationType(['service'], $config, 'service');
523
524 1
                                break;
525 1
                            case 'wsse':
526 1
                                $this->validateAuthenticationType(['username', 'password'], $config, 'wsse');
527
528 1
                                break;
529
                            case 'query_param':
530
                                $this->validateAuthenticationType(['params'], $config, 'query_param');
531
532
                                break;
533
                        }
534
535 6
                        return $config;
536 34
                    })
537 34
                ->end()
538 34
                ->children()
539 34
                    ->enumNode('type')
540 34
                        ->values(['basic', 'bearer', 'wsse', 'service', 'query_param'])
541 34
                        ->isRequired()
542 34
                        ->cannotBeEmpty()
543 34
                    ->end()
544 34
                    ->scalarNode('username')->end()
545 34
                    ->scalarNode('password')->end()
546 34
                    ->scalarNode('token')->end()
547 34
                    ->scalarNode('service')->end()
548 34
                    ->arrayNode('params')->prototype('scalar')->end()
549 34
                    ->end()
550 34
                ->end()
551 34
            ->end(); // End authentication plugin
552
553 34
        return $node;
554
    }
555
556
    /**
557
     * Validate that the configuration fragment has the specified keys and none other.
558
     *
559
     * @param array  $expected Fields that must exist
560
     * @param array  $actual   Actual configuration hashmap
561
     * @param string $authName Name of authentication method for error messages
562
     *
563
     * @throws InvalidConfigurationException If $actual does not have exactly the keys specified in $expected (plus 'type')
564
     */
565 7
    private function validateAuthenticationType(array $expected, array $actual, $authName)
566
    {
567 7
        unset($actual['type']);
568
        // Empty array is always provided, even if the config is not filled.
569 7
        if (empty($actual['params'])) {
570 7
            unset($actual['params']);
571
        }
572 7
        $actual = array_keys($actual);
573 7
        sort($actual);
574 7
        sort($expected);
575
576 7
        if ($expected === $actual) {
577 6
            return;
578
        }
579
580 1
        throw new InvalidConfigurationException(sprintf(
581 1
            'Authentication "%s" requires %s but got %s',
582 1
            $authName,
583 1
            implode(', ', $expected),
584 1
            implode(', ', $actual)
585
        ));
586
    }
587
588
    /**
589
     * Create configuration for cache plugin.
590
     *
591
     * @return NodeDefinition Definition for the cache node in the plugins list.
592
     */
593 34
    private function createCachePluginNode()
594
    {
595 34
        $builder = new TreeBuilder();
596
597 34
        $config = $builder->root('config');
598
        $config
599 34
            ->fixXmlConfig('method')
600 34
            ->fixXmlConfig('respect_response_cache_directive')
601 34
            ->addDefaultsIfNotSet()
602 34
            ->validate()
603 34
                ->ifTrue(function ($config) {
604
                    // Cannot set both respect_cache_headers and respect_response_cache_directives
605 5
                    return isset($config['respect_cache_headers'], $config['respect_response_cache_directives']);
606 34
                })
607 34
                ->thenInvalid('You can\'t provide config option "respect_cache_headers" and "respect_response_cache_directives" simultaniously. Use "respect_response_cache_directives" instead.')
608 34
            ->end()
609 34
            ->children()
610 34
                ->scalarNode('cache_key_generator')
611 34
                    ->info('This must be a service id to a service implementing '.CacheKeyGenerator::class)
612 34
                ->end()
613 34
                ->integerNode('cache_lifetime')
614 34
                    ->info('The minimum time we should store a cache item')
615 34
                ->end()
616 34
                ->integerNode('default_ttl')
617 34
                    ->info('The default max age of a Response')
618 34
                ->end()
619 34
                ->enumNode('hash_algo')
620 34
                    ->info('Hashing algorithm to use')
621 34
                    ->values(hash_algos())
622 34
                    ->cannotBeEmpty()
623 34
                ->end()
624 34
                ->arrayNode('methods')
625 34
                    ->info('Which request methods to cache')
626 34
                    ->defaultValue(['GET', 'HEAD'])
627 34
                    ->prototype('scalar')
628 34
                        ->validate()
629 34
                            ->ifTrue(function ($v) {
630
                                /* RFC7230 sections 3.1.1 and 3.2.6 except limited to uppercase characters. */
631 1
                                return preg_match('/[^A-Z0-9!#$%&\'*+\-.^_`|~]+/', $v);
632 34
                            })
633 34
                            ->thenInvalid('Invalid method: %s')
634 34
                        ->end()
635 34
                    ->end()
636 34
                ->end()
637 34
                ->scalarNode('respect_cache_headers')
638 34
                    ->info('Whether we should care about cache headers or not [DEPRECATED]')
639 34
                    ->beforeNormalization()
640 34
                        ->always(function ($v) {
641 3
                            @trigger_error('The option "respect_cache_headers" is deprecated since version 1.3 and will be removed in 2.0. Use "respect_response_cache_directives" instead.', E_USER_DEPRECATED);
642
643 3
                            return $v;
644 34
                        })
645 34
                    ->end()
646 34
                    ->validate()
647 34
                        ->ifNotInArray([null, true, false])
648 34
                        ->thenInvalid('Value for "respect_cache_headers" must be null or boolean')
649 34
                    ->end()
650 34
                ->end()
651 34
                ->variableNode('respect_response_cache_directives')
652 34
                    ->info('A list of cache directives to respect when caching responses')
653 34
                    ->validate()
654 34
                        ->always(function ($v) {
655 2
                            if (is_null($v) || is_array($v)) {
656 2
                                return $v;
657
                            }
658
659
                            throw new InvalidTypeException();
660 34
                        })
661 34
                    ->end()
662 34
                ->end()
663 34
            ->end()
664
        ;
665
666 34
        $cache = $builder->root('cache');
667
        $cache
668 34
            ->canBeEnabled()
669 34
            ->addDefaultsIfNotSet()
670 34
            ->children()
671 34
                ->scalarNode('cache_pool')
672 34
                    ->info('This must be a service id to a service implementing '.CacheItemPoolInterface::class)
673 34
                    ->isRequired()
674 34
                    ->cannotBeEmpty()
675 34
                ->end()
676 34
                ->scalarNode('stream_factory')
677 34
                    ->info('This must be a service id to a service implementing '.StreamFactory::class)
678 34
                    ->defaultValue('httplug.stream_factory')
679 34
                    ->cannotBeEmpty()
680 34
                ->end()
681 34
            ->end()
682 34
            ->append($config)
683
        ;
684
685 34
        return $cache;
686
    }
687
}
688