Completed
Push — master ( baadcd...d3edb4 )
by Tobias
09:03
created

Configuration::addSharedPluginNodes()   B

Complexity

Conditions 6
Paths 32

Size

Total Lines 91
Code Lines 74

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 91
ccs 8
cts 8
cp 1
rs 8.2585
c 0
b 0
f 0
cc 6
eloc 74
nc 32
nop 2
crap 6

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