Completed
Push — master ( ea729d...2dd925 )
by David
03:38 queued 47s
created

src/DependencyInjection/Configuration.php (2 issues)

duplicate/similar code.

Duplication Informational

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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