Completed
Push — master ( ab38a7...a2326e )
by David
10s
created

Configuration::addDebugSection()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 20
ccs 18
cts 18
cp 1
rs 9.4285
cc 1
eloc 18
nc 1
nop 1
crap 1
1
<?php
2
3
/*
4
 * This file is part of the FOSHttpCacheBundle package.
5
 *
6
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace FOS\HttpCacheBundle\DependencyInjection;
13
14
use FOS\HttpCache\ProxyClient\Varnish;
15
use FOS\HttpCache\SymfonyCache\PurgeListener;
16
use FOS\HttpCache\SymfonyCache\PurgeTagsListener;
17
use FOS\HttpCache\TagHeaderFormatter\TagHeaderFormatter;
18
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
19
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
20
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
21
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
22
use Symfony\Component\Config\Definition\ConfigurationInterface;
23
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
24
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
25
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
26
27
/**
28
 * This class contains the configuration information for the bundle.
29
 *
30
 * This information is solely responsible for how the different configuration
31
 * sections are normalized, and merged.
32
 *
33
 * @author David de Boer <[email protected]>
34
 * @author David Buchmann <[email protected]>
35
 */
36
class Configuration implements ConfigurationInterface
37
{
38
    /**
39
     * @var bool
40
     */
41
    private $debug;
42
43
    /**
44
     * @param bool $debug Whether to use the debug mode
45
     */
46 58
    public function __construct($debug)
47
    {
48 58
        $this->debug = $debug;
49 58
    }
50
51
    /**
52
     * {@inheritdoc}
53
     */
54 58
    public function getConfigTreeBuilder()
55
    {
56 58
        $treeBuilder = new TreeBuilder();
57 58
        $rootNode = $treeBuilder->root('fos_http_cache');
58
59
        $rootNode
60 58
            ->validate()
61 58
                ->ifTrue(function ($v) {
62 56
                    return $v['cache_manager']['enabled']
63 56
                        && !isset($v['proxy_client'])
64 56
                        && !isset($v['cache_manager']['custom_proxy_client'])
65
                    ;
66 58
                })
67 58 View Code Duplication
                ->then(function ($v) {
0 ignored issues
show
Duplication introduced by David Buchmann
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
68 17
                    if ('auto' === $v['cache_manager']['enabled']) {
69 16
                        $v['cache_manager']['enabled'] = false;
70
71 16
                        return $v;
72
                    }
73
74 1
                    throw new InvalidConfigurationException('You need to configure a proxy_client or specify a custom_proxy_client to use the cache_manager.');
75 58
                })
76 58
            ->end()
77 58
            ->validate()
78 58
                ->ifTrue(function ($v) {
79 55
                    return $v['tags']['enabled'] && !$v['cache_manager']['enabled'];
80 58
                })
81 58 View Code Duplication
                ->then(function ($v) {
0 ignored issues
show
Duplication introduced by David Buchmann
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
82 18
                    if ('auto' === $v['tags']['enabled']) {
83 17
                        $v['tags']['enabled'] = false;
84
85 17
                        return $v;
86
                    }
87
88 1
                    throw new InvalidConfigurationException('You need to configure a proxy_client to get the cache_manager needed for tag handling.');
89 58
                })
90 58
            ->end()
91 58
            ->validate()
92 58
                ->ifTrue(function ($v) {
93 54
                    return $v['invalidation']['enabled'] && !$v['cache_manager']['enabled'];
94 58
                })
95 58 View Code Duplication
                ->then(function ($v) {
0 ignored issues
show
Duplication introduced by David Buchmann
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
96 17
                    if ('auto' === $v['invalidation']['enabled']) {
97 16
                        $v['invalidation']['enabled'] = false;
98
99 16
                        return $v;
100
                    }
101
102 1
                    throw new InvalidConfigurationException('You need to configure a proxy_client to get the cache_manager needed for invalidation handling.');
103 58
                })
104 58
            ->end()
105 58
            ->validate()
106 58
                ->ifTrue(
107 58
                    function ($v) {
108 53
                        return false !== $v['user_context']['logout_handler']['enabled'];
109 58
                    }
110
                )
111 58
                ->then(function ($v) {
112 52
                    if (isset($v['cache_manager']['custom_proxy_client'])) {
113 5
                        $v['user_context']['logout_handler']['enabled'] = true;
114
115 5
                        return $v;
116
                    }
117
118 47
                    if (isset($v['proxy_client']['default']) && in_array($v['proxy_client']['default'], ['varnish', 'noop'])) {
119
                        $v['user_context']['logout_handler']['enabled'] = true;
120
121
                        return $v;
122
                    }
123 47 View Code Duplication
                    if (isset($v['proxy_client']['varnish']) || isset($v['proxy_client']['noop'])) {
0 ignored issues
show
Duplication introduced by David Buchmann
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
124 23
                        $v['user_context']['logout_handler']['enabled'] = true;
125
126 23
                        return $v;
127
                    }
128
129 24 View Code Duplication
                    if ('auto' === $v['user_context']['logout_handler']['enabled']) {
0 ignored issues
show
Duplication introduced by David de Boer
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
130 22
                        $v['user_context']['logout_handler']['enabled'] = false;
131
132 22
                        return $v;
133
                    }
134
135 2
                    throw new InvalidConfigurationException('To enable the user context logout handler, you need to configure a ban capable proxy_client.');
136 58
                })
137 58
            ->end()
138
            // Determine the default tags header for the varnish client, depending on whether we use BAN or xkey
139 58
            ->validate()
140 58
                ->ifTrue(
141 58 View Code Duplication
                    function ($v) {
0 ignored issues
show
Duplication introduced by Pete Lawrence
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
142
                        return
143 51
                            array_key_exists('proxy_client', $v)
144 51
                            && array_key_exists('varnish', $v['proxy_client'])
145 51
                            && empty($v['proxy_client']['varnish']['tags_header'])
146
                        ;
147 58
                    }
148
                )
149 58
                ->then(function ($v) {
150 18
                    $v['proxy_client']['varnish']['tags_header'] =
151 18
                        (Varnish::TAG_XKEY === $v['proxy_client']['varnish']['tag_mode'])
152 1
                        ? Varnish::DEFAULT_HTTP_HEADER_CACHE_XKEY
153 17
                        : Varnish::DEFAULT_HTTP_HEADER_CACHE_TAGS;
154
155 18
                    return $v;
156 58
                })
157 58
            ->end()
158
            // Determine the default tag response header, depending on whether we use BAN or xkey
159 58
            ->validate()
160 58
                ->ifTrue(
161 58
                    function ($v) {
162 51
                        return empty($v['tags']['response_header']);
163 58
                    }
164
                )
165 58
                ->then(function ($v) {
166 47
                    $v['tags']['response_header'] = $this->isVarnishXkey($v) ? 'xkey' : TagHeaderFormatter::DEFAULT_HEADER_NAME;
167
168 47
                    return $v;
169 58
                })
170 58
            ->end()
171
            // Determine the default separator for the tags header, depending on whether we use BAN or xkey
172 58
            ->validate()
173 58
                ->ifTrue(
174 58
                    function ($v) {
175 51
                        return empty($v['tags']['separator']);
176 58
                    }
177
                )
178 58
                ->then(function ($v) {
179 48
                    $v['tags']['separator'] = $this->isVarnishXkey($v) ? ' ' : ',';
180
181 48
                    return $v;
182 58
                })
183
        ;
184
185 58
        $this->addCacheableResponseSection($rootNode);
186 58
        $this->addCacheControlSection($rootNode);
187 58
        $this->addProxyClientSection($rootNode);
188 58
        $this->addCacheManagerSection($rootNode);
189 58
        $this->addTagSection($rootNode);
190 58
        $this->addInvalidationSection($rootNode);
191 58
        $this->addUserContextListenerSection($rootNode);
192 58
        $this->addFlashMessageSection($rootNode);
193 58
        $this->addTestSection($rootNode);
194 58
        $this->addDebugSection($rootNode);
195
196 58
        return $treeBuilder;
197
    }
198
199 48 View Code Duplication
    private function isVarnishXkey(array $v): bool
0 ignored issues
show
Duplication introduced by Pete Lawrence
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
200
    {
201 48
        return array_key_exists('proxy_client', $v)
202 48
            && array_key_exists('varnish', $v['proxy_client'])
203 48
            && Varnish::TAG_XKEY === $v['proxy_client']['varnish']['tag_mode']
204
        ;
205
    }
206
207 58
    private function addCacheableResponseSection(ArrayNodeDefinition $rootNode)
208
    {
209
        $rootNode
210 58
            ->children()
211 58
                ->arrayNode('cacheable')
212 58
                    ->addDefaultsIfNotSet()
213 58
                    ->children()
214 58
                        ->arrayNode('response')
215 58
                            ->addDefaultsIfNotSet()
216 58
                            ->children()
217 58
                                ->arrayNode('additional_status')
218 58
                                    ->prototype('scalar')->end()
219 58
                                    ->info('Additional response HTTP status codes that will be considered cacheable.')
220 58
                                ->end()
221 58
                                ->scalarNode('expression')
222 58
                                    ->defaultNull()
223 58
                                    ->info('Expression to decide whether response is cacheable. Replaces the default status codes.')
224 58
                            ->end()
225 58
                        ->end()
226
227 58
                        ->validate()
228 58
                            ->ifTrue(function ($v) {
229 6
                                return !empty($v['additional_status']) && !empty($v['expression']);
230 58
                            })
231 58
                            ->thenInvalid('You may not set both additional_status and expression.')
232 58
                        ->end()
233 58
                    ->end()
234 58
                ->end()
235 58
            ->end();
236 58
    }
237
238
    /**
239
     * Cache header control main section.
240
     *
241
     * @param ArrayNodeDefinition $rootNode
242
     */
243 58
    private function addCacheControlSection(ArrayNodeDefinition $rootNode)
244
    {
245
        $rules = $rootNode
246 58
            ->children()
247 58
                ->arrayNode('cache_control')
248 58
                    ->fixXmlConfig('rule')
249 58
                    ->children()
250 58
                        ->arrayNode('defaults')
251 58
                            ->addDefaultsIfNotSet()
252 58
                            ->children()
253 58
                                ->booleanNode('overwrite')
254 58
                                    ->info('Whether to overwrite existing cache headers')
255 58
                                    ->defaultFalse()
256 58
                                ->end()
257 58
                            ->end()
258 58
                        ->end()
259 58
                        ->arrayNode('rules')
260 58
                            ->prototype('array')
261 58
                                ->children();
262
263 58
        $this->addMatch($rules, true);
264
        $rules
265 58
            ->arrayNode('headers')
266 58
                ->isRequired()
267
                // todo validate there is some header defined
268 58
                ->children()
269 58
                    ->enumNode('overwrite')
270 58
                        ->info('Whether to overwrite cache headers for this rule, defaults to the cache_control.defaults.overwrite setting')
271 58
                        ->values(['default', true, false])
272 58
                        ->defaultValue('default')
273 58
                    ->end()
274 58
                    ->arrayNode('cache_control')
275 58
                        ->info('Add the specified cache control directives.')
276 58
                        ->children()
277 58
                            ->scalarNode('max_age')->end()
278 58
                            ->scalarNode('s_maxage')->end()
279 58
                            ->booleanNode('private')->end()
280 58
                            ->booleanNode('public')->end()
281 58
                            ->booleanNode('must_revalidate')->end()
282 58
                            ->booleanNode('proxy_revalidate')->end()
283 58
                            ->booleanNode('no_transform')->end()
284 58
                            ->booleanNode('no_cache')->end()
285 58
                            ->booleanNode('no_store')->end()
286 58
                            ->scalarNode('stale_if_error')->end()
287 58
                            ->scalarNode('stale_while_revalidate')->end()
288 58
                        ->end()
289 58
                    ->end()
290 58
                    ->enumNode('etag')
291 58
                        ->defaultValue(false)
292 58
                        ->treatTrueLike('strong')
293 58
                        ->info('Set a simple ETag which is just the md5 hash of the response body. '.
294 58
                               'You can specify which type of ETag you want by passing "strong" or "weak".')
295 58
                        ->values(['weak', 'strong', false])
296 58
                    ->end()
297 58
                    ->scalarNode('last_modified')
298 58
                        ->validate()
299 58
                            ->ifTrue(function ($v) {
300 2
                                if (is_string($v)) {
301 2
                                    new \DateTime($v);
302
                                }
303
304 1
                                return false;
305 58
                            })
306 58
                            ->thenInvalid('') // this will never happen as new DateTime will throw an exception if $v is no date
307 58
                        ->end()
308 58
                        ->info('Set a default last modified timestamp if none is set yet. Value must be parseable by DateTime')
309 58
                    ->end()
310 58
                    ->scalarNode('reverse_proxy_ttl')
311 58
                        ->defaultNull()
312 58
                        ->info('Specify an X-Reverse-Proxy-TTL header with a time in seconds for a caching proxy under your control.')
313 58
                    ->end()
314 58
                    ->arrayNode('vary')
315 58
                        ->beforeNormalization()->ifString()->then(function ($v) {
316 2
                            return preg_split('/\s*,\s*/', $v);
317 58
                        })->end()
318 58
                        ->prototype('scalar')->end()
319 58
                        ->info('Define a list of additional headers on which the response varies.')
320 58
                    ->end()
321 58
                ->end()
322 58
            ->end()
323
        ;
324 58
    }
325
326
    /**
327
     * Shared configuration between cache control, tags and invalidation.
328
     *
329
     * @param NodeBuilder $rules
330
     * @param bool        $matchResponse whether to also add fields to match response
331
     */
332 58
    private function addMatch(NodeBuilder $rules, $matchResponse = false)
333
    {
334
        $match = $rules
335 58
            ->arrayNode('match')
336 58
                ->cannotBeOverwritten()
337 58
                ->isRequired()
338 58
                ->fixXmlConfig('method')
339 58
                ->fixXmlConfig('ip')
340 58
                ->fixXmlConfig('attribute')
341 58
                ->validate()
342 58
                    ->ifTrue(function ($v) {
343 14
                        return !empty($v['additional_response_status']) && !empty($v['match_response']);
344 58
                    })
345 58
                    ->thenInvalid('You may not set both additional_response_status and match_response.')
346 58
                ->end()
347 58
                ->children()
348 58
                    ->scalarNode('path')
349 58
                        ->defaultNull()
350 58
                        ->info('Request path.')
351 58
                    ->end()
352 58
                    ->scalarNode('query_string')
353 58
                        ->defaultNull()
354 58
                        ->info('Request query string.')
355 58
                    ->end()
356 58
                    ->scalarNode('host')
357 58
                        ->defaultNull()
358 58
                        ->info('Request host name.')
359 58
                    ->end()
360 58
                    ->arrayNode('methods')
361 58
                        ->beforeNormalization()->ifString()->then(function ($v) {
362 3
                            return preg_split('/\s*,\s*/', $v);
363 58
                        })->end()
364 58
                        ->useAttributeAsKey('name')
365 58
                        ->prototype('scalar')->end()
366 58
                        ->info('Request HTTP methods.')
367 58
                    ->end()
368 58
                    ->arrayNode('ips')
369 58
                        ->beforeNormalization()->ifString()->then(function ($v) {
370 3
                            return preg_split('/\s*,\s*/', $v);
371 58
                        })->end()
372 58
                        ->useAttributeAsKey('name')
373 58
                        ->prototype('scalar')->end()
374 58
                        ->info('List of client IPs.')
375 58
                    ->end()
376 58
                    ->arrayNode('attributes')
377 58
                        ->useAttributeAsKey('name')
378 58
                        ->prototype('scalar')->end()
379 58
                        ->info('Regular expressions on request attributes.')
380 58
                    ->end()
381
        ;
382 58
        if ($matchResponse) {
383
            $match
384 58
                ->arrayNode('additional_response_status')
385 58
                    ->prototype('scalar')->end()
386 58
                    ->info('Additional response HTTP status codes that will match. Replaces cacheable configuration.')
387 58
                ->end()
388 58
                ->scalarNode('match_response')
389 58
                    ->defaultNull()
390 58
                    ->info('Expression to decide whether response should be matched. Replaces cacheable configuration.')
391 58
                ->end()
392
            ;
393
        }
394 58
    }
395
396 58
    private function addProxyClientSection(ArrayNodeDefinition $rootNode)
397
    {
398
        $rootNode
399 58
            ->children()
400 58
                ->arrayNode('proxy_client')
401 58
                    ->children()
402 58
                        ->enumNode('default')
403 58
                            ->values(['varnish', 'nginx', 'symfony', 'noop'])
404 58
                            ->info('If you configure more than one proxy client, you need to specify which client is the default.')
405 58
                        ->end()
406 58
                        ->arrayNode('varnish')
407 58
                            ->fixXmlConfig('default_ban_header')
408 58
                            ->validate()
409 58
                                ->always(function ($v) {
410 22
                                    if (!count($v['default_ban_headers'])) {
411 21
                                        unset($v['default_ban_headers']);
412
                                    }
413
414 22
                                    return $v;
415 58
                                })
416 58
                            ->end()
417 58
                            ->children()
418 58
                                ->scalarNode('tags_header')
419 58
                                    ->info('HTTP header to use when sending tag invalidation requests to Varnish')
420 58
                                ->end()
421 58
                                ->scalarNode('header_length')
422 58
                                    ->info('Maximum header length when invalidating tags. If there are more tags to invalidate than fit into the header, the invalidation request is split into several requests.')
423 58
                                ->end()
424 58
                                ->arrayNode('default_ban_headers')
425 58
                                    ->useAttributeAsKey('name')
426 58
                                    ->info('Map of additional headers to include in each ban request.')
427 58
                                    ->prototype('scalar')->end()
428 58
                                ->end()
429 58
                                ->enumNode('tag_mode')
430 58
                                    ->info('If you can enable the xkey module in Varnish, use the purgekeys mode for more efficient tag handling')
431 58
                                    ->values(['ban', 'purgekeys'])
432 58
                                    ->defaultValue('ban')
433 58
                                ->end()
434 58
                                ->append($this->getHttpDispatcherNode())
435 58
                            ->end()
436 58
                        ->end()
437
438 58
                        ->arrayNode('nginx')
439 58
                            ->children()
440 58
                                ->scalarNode('purge_location')
441 58
                                    ->defaultValue(false)
442 58
                                    ->info('Path to trigger the purge on Nginx for different location purge.')
443 58
                                ->end()
444 58
                                ->append($this->getHttpDispatcherNode())
445 58
                            ->end()
446 58
                        ->end()
447
448 58
                        ->arrayNode('symfony')
449 58
                            ->children()
450 58
                                ->scalarNode('tags_header')
451 58
                                    ->defaultValue(PurgeTagsListener::DEFAULT_TAGS_HEADER)
452 58
                                    ->info('HTTP header to use when sending tag invalidation requests to Symfony HttpCache')
453 58
                                ->end()
454 58
                                ->scalarNode('tags_method')
455 58
                                    ->defaultValue(PurgeTagsListener::DEFAULT_TAGS_METHOD)
456 58
                                    ->info('HTTP method for sending tag invalidation requests to Symfony HttpCache')
457 58
                                ->end()
458 58
                                ->scalarNode('header_length')
459 58
                                    ->info('Maximum header length when invalidating tags. If there are more tags to invalidate than fit into the header, the invalidation request is split into several requests.')
460 58
                                ->end()
461 58
                                ->scalarNode('purge_method')
462 58
                                    ->defaultValue(PurgeListener::DEFAULT_PURGE_METHOD)
463 58
                                    ->info('HTTP method to use when sending purge requests to Symfony HttpCache')
464 58
                                ->end()
465 58
                                ->booleanNode('use_kernel_dispatcher')
466 58
                                    ->defaultFalse()
467 58
                                    ->info('Dispatches invalidation requests to the kernel directly instead of executing real HTTP requests. Requires special kernel setup! Refer to the documentation for more information.')
468 58
                                ->end()
469 58
                                ->append($this->getHttpDispatcherNode())
470 58
                            ->end()
471 58
                        ->end()
472
473 58
                        ->booleanNode('noop')->end()
474 58
                    ->end()
475 58
                    ->validate()
476 58
                        ->always()
477 58
                        ->then(function ($config) {
478 34
                            foreach ($config as $proxyName => $proxyConfig) {
479 34
                                $serversConfigured = isset($proxyConfig['http']) && isset($proxyConfig['http']['servers']) && \is_array($proxyConfig['http']['servers']);
480
481 34
                                if (!\in_array($proxyName, ['noop', 'default', 'symfony'])) {
482 27
                                    if (!$serversConfigured) {
483
                                        throw new \InvalidArgumentException(sprintf('The "http.servers" section must be defined for the proxy "%s"', $proxyName));
484
                                    }
485
486 27
                                    return $config;
487
                                }
488
489 7
                                if ('symfony' === $proxyName) {
490 4
                                    if (!$serversConfigured && false === $proxyConfig['use_kernel_dispatcher']) {
491 7
                                        throw new \InvalidArgumentException('Either configure the "http.servers" section or enable "proxy_client.symfony.use_kernel_dispatcher"');
492
                                    }
493
                                }
494
                            }
495
496 6
                            return $config;
497 58
                        })
498 58
                    ->end()
499 58
                ->end()
500 58
            ->end();
501 58
    }
502
503
    /**
504
     * Get the configuration node for a HTTP dispatcher in a proxy client.
505
     *
506
     * @return NodeDefinition
507
     */
508 58
    private function getHttpDispatcherNode()
509
    {
510 58
        $treeBuilder = new TreeBuilder();
511 58
        $node = $treeBuilder->root('http');
512
513
        $node
514 58
            ->fixXmlConfig('server')
515 58
            ->children()
516 58
                ->arrayNode('servers')
517 58
                    ->info('Addresses of the hosts the caching proxy is running on. May be hostname or ip, and with :port if not the default port 80.')
518 58
                    ->useAttributeAsKey('name')
519 58
                    ->isRequired()
520 58
                    ->requiresAtLeastOneElement()
521 58
                    ->prototype('scalar')->end()
522 58
                ->end()
523 58
                ->scalarNode('base_url')
524 58
                    ->defaultNull()
525 58
                    ->info('Default host name and optional path for path based invalidation.')
526 58
                ->end()
527 58
                ->scalarNode('http_client')
528 58
                    ->defaultNull()
529 58
                    ->info('Httplug async client service name to use for sending the requests.')
530 58
                ->end()
531 58
            ->end()
532
        ;
533
534 58
        return $node;
535
    }
536
537 58
    private function addTestSection(ArrayNodeDefinition $rootNode)
538
    {
539
        $rootNode
540 58
            ->children()
541 58
                ->arrayNode('test')
542 58
                    ->children()
543 58
                        ->scalarNode('cache_header')
544 58
                            ->defaultValue('X-Cache')
545 58
                            ->info('HTTP cache hit/miss header')
546 58
                        ->end()
547 58
                        ->arrayNode('proxy_server')
548 58
                            ->info('Configure how caching proxy will be run in your tests')
549 58
                            ->children()
550 58
                                ->enumNode('default')
551 58
                                    ->values(['varnish', 'nginx'])
552 58
                                    ->info('If you configure more than one proxy server, specify which client is the default.')
553 58
                                ->end()
554 58
                                ->arrayNode('varnish')
555 58
                                    ->children()
556 58
                                        ->scalarNode('config_file')->isRequired()->end()
557 58
                                        ->scalarNode('binary')->defaultValue('varnishd')->end()
558 58
                                        ->integerNode('port')->defaultValue(6181)->end()
559 58
                                        ->scalarNode('ip')->defaultValue('127.0.0.1')->end()
560 58
                                    ->end()
561 58
                                ->end()
562 58
                                ->arrayNode('nginx')
563 58
                                    ->children()
564 58
                                        ->scalarNode('config_file')->isRequired()->end()
565 58
                                        ->scalarNode('binary')->defaultValue('nginx')->end()
566 58
                                        ->integerNode('port')->defaultValue(8080)->end()
567 58
                                        ->scalarNode('ip')->defaultValue('127.0.0.1')->end()
568 58
                                    ->end()
569 58
                                ->end()
570 58
                            ->end()
571 58
                        ->end()
572 58
                    ->end()
573 58
                ->end()
574 58
            ->end();
575 58
    }
576
577
    /**
578
     * Cache manager main section.
579
     *
580
     * @param ArrayNodeDefinition $rootNode
581
     */
582 58
    private function addCacheManagerSection(ArrayNodeDefinition $rootNode)
583
    {
584
        $rootNode
585 58
            ->children()
586 58
                ->arrayNode('cache_manager')
587 58
                    ->addDefaultsIfNotSet()
588 58
                    ->beforeNormalization()
589 58
                        ->ifArray()
590 58
                        ->then(function ($v) {
591 10
                            $v['enabled'] = isset($v['enabled']) ? $v['enabled'] : true;
592
593 10
                            return $v;
594 58
                        })
595 58
                    ->end()
596 58
                    ->info('Configure the cache manager. Needs a proxy_client to be configured.')
597 58
                    ->children()
598 58
                        ->enumNode('enabled')
599 58
                            ->values([true, false, 'auto'])
600 58
                            ->defaultValue('auto')
601 58
                            ->info('Allows to disable the invalidation manager. Enabled by default if you configure a proxy client.')
602 58
                        ->end()
603 58
                        ->scalarNode('custom_proxy_client')
604 58
                            ->info('Service name of a custom proxy client to use. With a custom client, generate_url_type defaults to ABSOLUTE_URL and tag support needs to be explicitly enabled. If no custom proxy client is specified, the first proxy client you configured is used.')
605 58
                            ->cannotBeEmpty()
606 58
                        ->end()
607 58
                        ->enumNode('generate_url_type')
608 58
                            ->values([
609 58
                                'auto',
610
                                UrlGeneratorInterface::ABSOLUTE_PATH,
611
                                UrlGeneratorInterface::ABSOLUTE_URL,
612
                                UrlGeneratorInterface::NETWORK_PATH,
613
                                UrlGeneratorInterface::RELATIVE_PATH,
614
                            ])
615 58
                            ->defaultValue('auto')
616 58
                            ->info('Set what URLs to generate on invalidate/refresh Route. Auto means path if base_url is set on the default proxy client, full URL otherwise.')
617 58
                        ->end()
618 58
                    ->end()
619
        ;
620 58
    }
621
622 58
    private function addTagSection(ArrayNodeDefinition $rootNode)
623
    {
624
        $rules = $rootNode
625 58
            ->children()
626 58
                ->arrayNode('tags')
627 58
                    ->addDefaultsIfNotSet()
628 58
                    ->fixXmlConfig('rule')
629 58
                    ->children()
630 58
                        ->enumNode('enabled')
631 58
                            ->values([true, false, 'auto'])
632 58
                            ->defaultValue('auto')
633 58
                            ->info('Allows to disable the event subscriber for tag configuration and annotations when your project does not use the annotations. Enabled by default if you configured the cache manager.')
634 58
                        ->end()
635 58
                        ->booleanNode('strict')->defaultFalse()->end()
636 58
                        ->scalarNode('expression_language')
637 58
                            ->defaultNull()
638 58
                            ->info('Service name of a custom ExpressionLanugage to use.')
639 58
                        ->end()
640 58
                        ->scalarNode('response_header')
641 58
                            ->defaultNull()
642 58
                            ->info('HTTP header that contains cache tags. Defaults to xkey-softpurge for Varnish xkey or X-Cache-Tags otherwise')
643 58
                        ->end()
644 58
                        ->scalarNode('separator')
645 58
                            ->defaultNull()
646 58
                            ->info('Character(s) to use to separate multiple tags. Defaults to " " for Varnish xkey or "," otherwise')
647 58
                        ->end()
648 58
                        ->arrayNode('rules')
649 58
                            ->prototype('array')
650 58
                                ->fixXmlConfig('tag')
651 58
                                ->fixXmlConfig('tag_expression')
652 58
                                ->validate()
653 58
                                    ->ifTrue(function ($v) {
654 4
                                        return !empty($v['tag_expressions']) && !class_exists(ExpressionLanguage::class);
655 58
                                    })
656 58
                                    ->thenInvalid('Configured a tag_expression but ExpressionLanugage is not available')
657 58
                                ->end()
658 58
                                ->children()
659
                        ;
660 58
        $this->addMatch($rules);
661
662
        $rules
663 58
            ->arrayNode('tags')
664 58
                ->prototype('scalar')
665 58
                ->info('Tags to add to the response on safe requests, to invalidate on unsafe requests')
666 58
            ->end()->end()
667 58
            ->arrayNode('tag_expressions')
668 58
                ->prototype('scalar')
669 58
                ->info('Tags to add to the response on safe requests, to invalidate on unsafe requests')
670 58
            ->end()
671
        ;
672 58
    }
673
674 58
    private function addInvalidationSection(ArrayNodeDefinition $rootNode)
675
    {
676
        $rules = $rootNode
677 58
            ->children()
678 58
                ->arrayNode('invalidation')
679 58
                    ->fixXmlConfig('rule')
680 58
                    ->addDefaultsIfNotSet()
681 58
                    ->children()
682 58
                        ->enumNode('enabled')
683 58
                            ->values([true, false, 'auto'])
684 58
                            ->defaultValue('auto')
685 58
                            ->info('Allows to disable the listener for invalidation. Enabled by default if the cache manager is configured. When disabled, the cache manager is no longer flushed automatically.')
686 58
                        ->end()
687 58
                        ->scalarNode('expression_language')
688 58
                            ->defaultNull()
689 58
                            ->info('Service name of a custom ExpressionLanugage to use.')
690 58
                        ->end()
691 58
                        ->arrayNode('rules')
692 58
                            ->info('Set what requests should invalidate which target routes.')
693 58
                            ->prototype('array')
694 58
                                ->fixXmlConfig('route')
695 58
                                ->children();
696
697 58
        $this->addMatch($rules);
698
        $rules
699 58
            ->arrayNode('routes')
700 58
                ->isRequired()
701 58
                ->requiresAtLeastOneElement()
702 58
                ->useAttributeAsKey('name')
703 58
                ->info('Target routes to invalidate when request is matched')
704 58
                ->prototype('array')
705 58
                    ->children()
706 58
                        ->booleanNode('ignore_extra_params')->defaultTrue()->end()
707 58
                    ->end()
708 58
                ->end()
709 58
            ->end();
710 58
    }
711
712
    /**
713
     * User context main section.
714
     *
715
     * @param ArrayNodeDefinition $rootNode
716
     */
717 58
    private function addUserContextListenerSection(ArrayNodeDefinition $rootNode)
718
    {
719
        $rootNode
720 58
            ->children()
721 58
                ->arrayNode('user_context')
722 58
                    ->info('Listener that returns the request for the user context hash as early as possible.')
723 58
                    ->addDefaultsIfNotSet()
724 58
                    ->canBeEnabled()
725 58
                    ->fixXmlConfig('user_identifier_header')
726 58
                    ->children()
727 58
                        ->arrayNode('match')
728 58
                            ->addDefaultsIfNotSet()
729 58
                            ->children()
730 58
                                ->scalarNode('matcher_service')
731 58
                                    ->defaultValue('fos_http_cache.user_context.request_matcher')
732 58
                                    ->info('Service id of a request matcher that tells whether the request is a context hash request.')
733 58
                                ->end()
734 58
                                ->scalarNode('accept')
735 58
                                    ->defaultValue('application/vnd.fos.user-context-hash')
736 58
                                    ->info('Specify the accept HTTP header used for context hash requests.')
737 58
                                ->end()
738 58
                                ->scalarNode('method')
739 58
                                    ->defaultNull()
740 58
                                    ->info('Specify the HTTP method used for context hash requests.')
741 58
                                ->end()
742 58
                            ->end()
743 58
                        ->end()
744 58
                        ->scalarNode('hash_cache_ttl')
745 58
                            ->defaultValue(0)
746 58
                            ->info('Cache the response for the hash for the specified number of seconds. Setting this to 0 will not cache those responses at all.')
747 58
                        ->end()
748 58
                        ->booleanNode('always_vary_on_context_hash')
749 58
                            ->defaultTrue()
750 58
                            ->info('Whether to always add the user context hash header name in the response Vary header.')
751 58
                        ->end()
752 58
                        ->arrayNode('user_identifier_headers')
753 58
                            ->prototype('scalar')->end()
754 58
                            ->defaultValue(['Cookie', 'Authorization'])
755 58
                            ->info('List of headers that contain the unique identifier for the user in the hash request.')
756 58
                        ->end()
757 58
                        ->scalarNode('session_name_prefix')
758 58
                            ->defaultValue(false)
759 58
                            ->info('Prefix for session cookies. Must match your PHP session configuration. Set to false to ignore the session in user context.')
760 58
                        ->end()
761 58
                        ->scalarNode('user_hash_header')
762 58
                            ->defaultValue('X-User-Context-Hash')
763 58
                            ->info('Name of the header that contains the hash information for the context.')
764 58
                        ->end()
765 58
                        ->booleanNode('role_provider')
766 58
                            ->defaultFalse()
767 58
                            ->info('Whether to enable a provider that automatically adds all roles of the current user to the context.')
768 58
                        ->end()
769 58
                        ->arrayNode('logout_handler')
770 58
                            ->addDefaultsIfNotSet()
771 58
                            ->canBeEnabled()
772 58
                            ->children()
773 58
                                ->enumNode('enabled')
774 58
                                    ->values([true, false, 'auto'])
775 58
                                    ->defaultValue('auto')
776 58
                                    ->info('Whether to enable the user context logout handler.')
777 58
                                ->end()
778 58
                            ->end()
779 58
                        ->end()
780 58
                    ->end()
781 58
                ->end()
782 58
            ->end()
783
        ;
784 58
    }
785
786 58
    private function addFlashMessageSection(ArrayNodeDefinition $rootNode)
787
    {
788
        $rootNode
789 58
            ->children()
790 58
                ->arrayNode('flash_message')
791 58
                    ->canBeUnset()
792 58
                    ->canBeEnabled()
793 58
                    ->info('Activate the flash message listener that puts flash messages into a cookie.')
794 58
                    ->children()
795 58
                        ->scalarNode('name')
796 58
                            ->defaultValue('flashes')
797 58
                            ->info('Name of the cookie to set for flashes.')
798 58
                        ->end()
799 58
                        ->scalarNode('path')
800 58
                            ->defaultValue('/')
801 58
                            ->info('Cookie path validity.')
802 58
                        ->end()
803 58
                        ->scalarNode('host')
804 58
                            ->defaultNull()
805 58
                            ->info('Cookie host name validity.')
806 58
                        ->end()
807 58
                        ->scalarNode('secure')
808 58
                            ->defaultFalse()
809 58
                            ->info('Whether the cookie should only be transmitted over a secure HTTPS connection from the client.')
810 58
                        ->end()
811 58
                    ->end()
812 58
                ->end()
813 58
            ->end();
814 58
    }
815
816 58
    private function addDebugSection(ArrayNodeDefinition $rootNode)
817
    {
818
        $rootNode
819 58
            ->children()
820 58
                ->arrayNode('debug')
821 58
                ->addDefaultsIfNotSet()
822 58
                ->canBeEnabled()
823 58
                ->children()
824 58
                    ->booleanNode('enabled')
825 58
                        ->defaultValue($this->debug)
826 58
                        ->info('Whether to send a debug header with the response to trigger a caching proxy to send debug information. If not set, defaults to kernel.debug.')
827 58
                    ->end()
828 58
                    ->scalarNode('header')
829 58
                        ->defaultValue('X-Cache-Debug')
830 58
                        ->info('The header to send if debug is true.')
831 58
                    ->end()
832 58
                ->end()
833 58
            ->end()
834 58
        ->end();
835 58
    }
836
}
837