Completed
Push — master ( 6219f7...d44e26 )
by David
13s
created

Configuration::addTagSection()   B

Complexity

Conditions 2
Paths 1

Size

Total Lines 55

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 49
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 55
ccs 49
cts 49
cp 1
rs 8.9818
c 0
b 0
f 0
cc 2
nc 1
nop 1
crap 2

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
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 59
    public function __construct($debug)
47
    {
48 59
        $this->debug = $debug;
49 59
    }
50
51
    /**
52
     * {@inheritdoc}
53
     */
54 59
    public function getConfigTreeBuilder()
55
    {
56 59
        if (method_exists(TreeBuilder::class, 'getRootNode')) {
57
            $treeBuilder = new TreeBuilder('fos_http_cache');
0 ignored issues
show
Unused Code introduced by
The call to TreeBuilder::__construct() has too many arguments starting with 'fos_http_cache'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
58
            $rootNode = $treeBuilder->getRootNode();
0 ignored issues
show
Bug introduced by
The method getRootNode() does not seem to exist on object<Symfony\Component...on\Builder\TreeBuilder>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
59
        } else {
60 59
            $treeBuilder = new TreeBuilder();
61 59
            $rootNode = $treeBuilder->root('fos_http_cache');
62
        }
63
64
        $rootNode
65 59
            ->validate()
66 59
                ->ifTrue(function ($v) {
67 57
                    return $v['cache_manager']['enabled']
68 57
                        && !isset($v['proxy_client'])
69 57
                        && !isset($v['cache_manager']['custom_proxy_client'])
70
                    ;
71 59
                })
72 59 View Code Duplication
                ->then(function ($v) {
0 ignored issues
show
Duplication introduced by
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...
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 59
                })
81 59
            ->end()
82 59
            ->validate()
83 59
                ->ifTrue(function ($v) {
84 56
                    return $v['tags']['enabled'] && !$v['cache_manager']['enabled'];
85 59
                })
86 59 View Code Duplication
                ->then(function ($v) {
0 ignored issues
show
Duplication introduced by
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...
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 59
                })
95 59
            ->end()
96 59
            ->validate()
97 59
                ->ifTrue(function ($v) {
98 55
                    return $v['invalidation']['enabled'] && !$v['cache_manager']['enabled'];
99 59
                })
100 59 View Code Duplication
                ->then(function ($v) {
0 ignored issues
show
Duplication introduced by
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...
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 59
                })
109 59
            ->end()
110 59
            ->validate()
111 59
                ->ifTrue(
112 59
                    function ($v) {
113 54
                        return false !== $v['user_context']['logout_handler']['enabled'];
114 59
                    }
115
                )
116 59
                ->then(function ($v) {
117 53
                    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 48
                    if (isset($v['proxy_client']['default'])
124 48
                        && in_array($v['proxy_client']['default'], ['varnish', 'symfony', 'noop'])
125
                    ) {
126
                        $v['user_context']['logout_handler']['enabled'] = true;
127
128
                        return $v;
129
                    }
130 48
                    if (isset($v['proxy_client']['varnish'])
131 26
                        || isset($v['proxy_client']['symfony'])
132 48
                        || isset($v['proxy_client']['noop'])
133
                    ) {
134 27
                        $v['user_context']['logout_handler']['enabled'] = true;
135
136 27
                        return $v;
137
                    }
138
139 21
                    if ('auto' === $v['user_context']['logout_handler']['enabled']) {
140 19
                        $v['user_context']['logout_handler']['enabled'] = false;
141
142 19
                        return $v;
143
                    }
144
145 2
                    throw new InvalidConfigurationException('To enable the user context logout handler, you need to configure a ban capable proxy_client.');
146 59
                })
147 59
            ->end()
148
            // Determine the default tags header for the varnish client, depending on whether we use BAN or xkey
149 59
            ->validate()
150 59
                ->ifTrue(
151 59 View Code Duplication
                    function ($v) {
0 ignored issues
show
Duplication introduced by
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...
152
                        return
153 52
                            array_key_exists('proxy_client', $v)
154 52
                            && array_key_exists('varnish', $v['proxy_client'])
155 52
                            && empty($v['proxy_client']['varnish']['tags_header'])
156
                        ;
157 59
                    }
158
                )
159 59
                ->then(function ($v) {
160 19
                    $v['proxy_client']['varnish']['tags_header'] =
161 19
                        (Varnish::TAG_XKEY === $v['proxy_client']['varnish']['tag_mode'])
162 1
                        ? Varnish::DEFAULT_HTTP_HEADER_CACHE_XKEY
163 18
                        : Varnish::DEFAULT_HTTP_HEADER_CACHE_TAGS;
164
165 19
                    return $v;
166 59
                })
167 59
            ->end()
168
            // Determine the default tag response header, depending on whether we use BAN or xkey
169 59
            ->validate()
170 59
                ->ifTrue(
171 59
                    function ($v) {
172 52
                        return empty($v['tags']['response_header']);
173 59
                    }
174
                )
175 59
                ->then(function ($v) {
176 48
                    $v['tags']['response_header'] = $this->isVarnishXkey($v) ? 'xkey' : TagHeaderFormatter::DEFAULT_HEADER_NAME;
177
178 48
                    return $v;
179 59
                })
180 59
            ->end()
181
            // Determine the default separator for the tags header, depending on whether we use BAN or xkey
182 59
            ->validate()
183 59
                ->ifTrue(
184 59
                    function ($v) {
185 52
                        return empty($v['tags']['separator']);
186 59
                    }
187
                )
188 59
                ->then(function ($v) {
189 49
                    $v['tags']['separator'] = $this->isVarnishXkey($v) ? ' ' : ',';
190
191 49
                    return $v;
192 59
                })
193
        ;
194
195 59
        $this->addCacheableResponseSection($rootNode);
196 59
        $this->addCacheControlSection($rootNode);
197 59
        $this->addProxyClientSection($rootNode);
198 59
        $this->addCacheManagerSection($rootNode);
199 59
        $this->addTagSection($rootNode);
200 59
        $this->addInvalidationSection($rootNode);
201 59
        $this->addUserContextListenerSection($rootNode);
202 59
        $this->addFlashMessageSection($rootNode);
203 59
        $this->addTestSection($rootNode);
204 59
        $this->addDebugSection($rootNode);
205
206 59
        return $treeBuilder;
207
    }
208
209 49 View Code Duplication
    private function isVarnishXkey(array $v): bool
0 ignored issues
show
Duplication introduced by
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...
210
    {
211 49
        return array_key_exists('proxy_client', $v)
212 49
            && array_key_exists('varnish', $v['proxy_client'])
213 49
            && Varnish::TAG_XKEY === $v['proxy_client']['varnish']['tag_mode']
214
        ;
215
    }
216
217 59
    private function addCacheableResponseSection(ArrayNodeDefinition $rootNode)
218
    {
219
        $rootNode
220 59
            ->children()
221 59
                ->arrayNode('cacheable')
222 59
                    ->addDefaultsIfNotSet()
223 59
                    ->children()
224 59
                        ->arrayNode('response')
225 59
                            ->addDefaultsIfNotSet()
226 59
                            ->children()
227 59
                                ->arrayNode('additional_status')
228 59
                                    ->prototype('scalar')->end()
229 59
                                    ->info('Additional response HTTP status codes that will be considered cacheable.')
230 59
                                ->end()
231 59
                                ->scalarNode('expression')
232 59
                                    ->defaultNull()
233 59
                                    ->info('Expression to decide whether response is cacheable. Replaces the default status codes.')
234 59
                            ->end()
235 59
                        ->end()
236
237 59
                        ->validate()
238 59
                            ->ifTrue(function ($v) {
239 6
                                return !empty($v['additional_status']) && !empty($v['expression']);
240 59
                            })
241 59
                            ->thenInvalid('You may not set both additional_status and expression.')
242 59
                        ->end()
243 59
                    ->end()
244 59
                ->end()
245 59
            ->end();
246 59
    }
247
248
    /**
249
     * Cache header control main section.
250
     *
251
     * @param ArrayNodeDefinition $rootNode
252
     */
253 59
    private function addCacheControlSection(ArrayNodeDefinition $rootNode)
254
    {
255
        $rules = $rootNode
256 59
            ->children()
257 59
                ->arrayNode('cache_control')
258 59
                    ->fixXmlConfig('rule')
259 59
                    ->children()
260 59
                        ->arrayNode('defaults')
261 59
                            ->addDefaultsIfNotSet()
262 59
                            ->children()
263 59
                                ->booleanNode('overwrite')
264 59
                                    ->info('Whether to overwrite existing cache headers')
265 59
                                    ->defaultFalse()
266 59
                                ->end()
267 59
                            ->end()
268 59
                        ->end()
269 59
                        ->arrayNode('rules')
270 59
                            ->prototype('array')
271 59
                                ->children();
272
273 59
        $this->addMatch($rules, true);
274
        $rules
275 59
            ->arrayNode('headers')
276 59
                ->isRequired()
277
                // todo validate there is some header defined
278 59
                ->children()
279 59
                    ->enumNode('overwrite')
280 59
                        ->info('Whether to overwrite cache headers for this rule, defaults to the cache_control.defaults.overwrite setting')
281 59
                        ->values(['default', true, false])
282 59
                        ->defaultValue('default')
283 59
                    ->end()
284 59
                    ->arrayNode('cache_control')
285 59
                        ->info('Add the specified cache control directives.')
286 59
                        ->children()
287 59
                            ->scalarNode('max_age')->end()
288 59
                            ->scalarNode('s_maxage')->end()
289 59
                            ->booleanNode('private')->end()
290 59
                            ->booleanNode('public')->end()
291 59
                            ->booleanNode('must_revalidate')->end()
292 59
                            ->booleanNode('proxy_revalidate')->end()
293 59
                            ->booleanNode('no_transform')->end()
294 59
                            ->booleanNode('no_cache')->end()
295 59
                            ->booleanNode('no_store')->end()
296 59
                            ->scalarNode('stale_if_error')->end()
297 59
                            ->scalarNode('stale_while_revalidate')->end()
298 59
                        ->end()
299 59
                    ->end()
300 59
                    ->enumNode('etag')
301 59
                        ->defaultValue(false)
302 59
                        ->treatTrueLike('strong')
303 59
                        ->info('Set a simple ETag which is just the md5 hash of the response body. '.
304 59
                               'You can specify which type of ETag you want by passing "strong" or "weak".')
305 59
                        ->values(['weak', 'strong', false])
306 59
                    ->end()
307 59
                    ->scalarNode('last_modified')
308 59
                        ->validate()
309 59
                            ->ifTrue(function ($v) {
310 2
                                if (is_string($v)) {
311 2
                                    new \DateTime($v);
312
                                }
313
314 1
                                return false;
315 59
                            })
316 59
                            ->thenInvalid('') // this will never happen as new DateTime will throw an exception if $v is no date
317 59
                        ->end()
318 59
                        ->info('Set a default last modified timestamp if none is set yet. Value must be parseable by DateTime')
319 59
                    ->end()
320 59
                    ->scalarNode('reverse_proxy_ttl')
321 59
                        ->defaultNull()
322 59
                        ->info('Specify an X-Reverse-Proxy-TTL header with a time in seconds for a caching proxy under your control.')
323 59
                    ->end()
324 59
                    ->arrayNode('vary')
325 59
                        ->beforeNormalization()->ifString()->then(function ($v) {
326 2
                            return preg_split('/\s*,\s*/', $v);
327 59
                        })->end()
328 59
                        ->prototype('scalar')->end()
329 59
                        ->info('Define a list of additional headers on which the response varies.')
330 59
                    ->end()
331 59
                ->end()
332 59
            ->end()
333
        ;
334 59
    }
335
336
    /**
337
     * Shared configuration between cache control, tags and invalidation.
338
     *
339
     * @param NodeBuilder $rules
340
     * @param bool        $matchResponse whether to also add fields to match response
341
     */
342 59
    private function addMatch(NodeBuilder $rules, $matchResponse = false)
343
    {
344
        $match = $rules
345 59
            ->arrayNode('match')
346 59
                ->cannotBeOverwritten()
347 59
                ->isRequired()
348 59
                ->fixXmlConfig('method')
349 59
                ->fixXmlConfig('ip')
350 59
                ->fixXmlConfig('attribute')
351 59
                ->validate()
352 59
                    ->ifTrue(function ($v) {
353 14
                        return !empty($v['additional_response_status']) && !empty($v['match_response']);
354 59
                    })
355 59
                    ->thenInvalid('You may not set both additional_response_status and match_response.')
356 59
                ->end()
357 59
                ->children()
358 59
                    ->scalarNode('path')
359 59
                        ->defaultNull()
360 59
                        ->info('Request path.')
361 59
                    ->end()
362 59
                    ->scalarNode('query_string')
363 59
                        ->defaultNull()
364 59
                        ->info('Request query string.')
365 59
                    ->end()
366 59
                    ->scalarNode('host')
367 59
                        ->defaultNull()
368 59
                        ->info('Request host name.')
369 59
                    ->end()
370 59
                    ->arrayNode('methods')
371 59
                        ->beforeNormalization()->ifString()->then(function ($v) {
372 3
                            return preg_split('/\s*,\s*/', $v);
373 59
                        })->end()
374 59
                        ->useAttributeAsKey('name')
375 59
                        ->prototype('scalar')->end()
376 59
                        ->info('Request HTTP methods.')
377 59
                    ->end()
378 59
                    ->arrayNode('ips')
379 59
                        ->beforeNormalization()->ifString()->then(function ($v) {
380 3
                            return preg_split('/\s*,\s*/', $v);
381 59
                        })->end()
382 59
                        ->useAttributeAsKey('name')
383 59
                        ->prototype('scalar')->end()
384 59
                        ->info('List of client IPs.')
385 59
                    ->end()
386 59
                    ->arrayNode('attributes')
387 59
                        ->useAttributeAsKey('name')
388 59
                        ->prototype('scalar')->end()
389 59
                        ->info('Regular expressions on request attributes.')
390 59
                    ->end()
391
        ;
392 59
        if ($matchResponse) {
393
            $match
394 59
                ->arrayNode('additional_response_status')
395 59
                    ->prototype('scalar')->end()
396 59
                    ->info('Additional response HTTP status codes that will match. Replaces cacheable configuration.')
397 59
                ->end()
398 59
                ->scalarNode('match_response')
399 59
                    ->defaultNull()
400 59
                    ->info('Expression to decide whether response should be matched. Replaces cacheable configuration.')
401 59
                ->end()
402
            ;
403
        }
404 59
    }
405
406 59
    private function addProxyClientSection(ArrayNodeDefinition $rootNode)
407
    {
408
        $rootNode
409 59
            ->children()
410 59
                ->arrayNode('proxy_client')
411 59
                    ->children()
412 59
                        ->enumNode('default')
413 59
                            ->values(['varnish', 'nginx', 'symfony', 'noop'])
414 59
                            ->info('If you configure more than one proxy client, you need to specify which client is the default.')
415 59
                        ->end()
416 59
                        ->arrayNode('varnish')
417 59
                            ->fixXmlConfig('default_ban_header')
418 59
                            ->validate()
419 59
                                ->always(function ($v) {
420 23
                                    if (!count($v['default_ban_headers'])) {
421 22
                                        unset($v['default_ban_headers']);
422
                                    }
423
424 23
                                    return $v;
425 59
                                })
426 59
                            ->end()
427 59
                            ->children()
428 59
                                ->scalarNode('tags_header')
429 59
                                    ->info('HTTP header to use when sending tag invalidation requests to Varnish')
430 59
                                ->end()
431 59
                                ->scalarNode('header_length')
432 59
                                    ->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.')
433 59
                                ->end()
434 59
                                ->arrayNode('default_ban_headers')
435 59
                                    ->useAttributeAsKey('name')
436 59
                                    ->info('Map of additional headers to include in each ban request.')
437 59
                                    ->prototype('scalar')->end()
438 59
                                ->end()
439 59
                                ->enumNode('tag_mode')
440 59
                                    ->info('If you can enable the xkey module in Varnish, use the purgekeys mode for more efficient tag handling')
441 59
                                    ->values(['ban', 'purgekeys'])
442 59
                                    ->defaultValue('ban')
443 59
                                ->end()
444 59
                                ->append($this->getHttpDispatcherNode())
445 59
                            ->end()
446 59
                        ->end()
447
448 59
                        ->arrayNode('nginx')
449 59
                            ->children()
450 59
                                ->scalarNode('purge_location')
451 59
                                    ->defaultValue(false)
452 59
                                    ->info('Path to trigger the purge on Nginx for different location purge.')
453 59
                                ->end()
454 59
                                ->append($this->getHttpDispatcherNode())
455 59
                            ->end()
456 59
                        ->end()
457
458 59
                        ->arrayNode('symfony')
459 59
                            ->children()
460 59
                                ->scalarNode('tags_header')
461 59
                                    ->defaultValue(PurgeTagsListener::DEFAULT_TAGS_HEADER)
462 59
                                    ->info('HTTP header to use when sending tag invalidation requests to Symfony HttpCache')
463 59
                                ->end()
464 59
                                ->scalarNode('tags_method')
465 59
                                    ->defaultValue(PurgeTagsListener::DEFAULT_TAGS_METHOD)
466 59
                                    ->info('HTTP method for sending tag invalidation requests to Symfony HttpCache')
467 59
                                ->end()
468 59
                                ->scalarNode('header_length')
469 59
                                    ->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.')
470 59
                                ->end()
471 59
                                ->scalarNode('purge_method')
472 59
                                    ->defaultValue(PurgeListener::DEFAULT_PURGE_METHOD)
473 59
                                    ->info('HTTP method to use when sending purge requests to Symfony HttpCache')
474 59
                                ->end()
475 59
                                ->booleanNode('use_kernel_dispatcher')
476 59
                                    ->defaultFalse()
477 59
                                    ->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.')
478 59
                                ->end()
479 59
                                ->append($this->getHttpDispatcherNode())
480 59
                            ->end()
481 59
                        ->end()
482
483 59
                        ->booleanNode('noop')->end()
484 59
                    ->end()
485 59
                    ->validate()
486 59
                        ->always()
487 59
                        ->then(function ($config) {
488 35
                            foreach ($config as $proxyName => $proxyConfig) {
489 35
                                $serversConfigured = isset($proxyConfig['http']) && isset($proxyConfig['http']['servers']) && \is_array($proxyConfig['http']['servers']);
490
491 35
                                if (!\in_array($proxyName, ['noop', 'default', 'symfony'])) {
492 28
                                    if (!$serversConfigured) {
493
                                        throw new \InvalidArgumentException(sprintf('The "http.servers" section must be defined for the proxy "%s"', $proxyName));
494
                                    }
495
496 28
                                    return $config;
497
                                }
498
499 7
                                if ('symfony' === $proxyName) {
500 4
                                    if (!$serversConfigured && false === $proxyConfig['use_kernel_dispatcher']) {
501 7
                                        throw new \InvalidArgumentException('Either configure the "http.servers" section or enable "proxy_client.symfony.use_kernel_dispatcher"');
502
                                    }
503
                                }
504
                            }
505
506 6
                            return $config;
507 59
                        })
508 59
                    ->end()
509 59
                ->end()
510 59
            ->end();
511 59
    }
512
513
    /**
514
     * Get the configuration node for a HTTP dispatcher in a proxy client.
515
     *
516
     * @return NodeDefinition
517
     */
518 59
    private function getHttpDispatcherNode()
519
    {
520 59
        if (method_exists(TreeBuilder::class, 'getRootNode')) {
521
            $treeBuilder = new TreeBuilder('http');
0 ignored issues
show
Unused Code introduced by
The call to TreeBuilder::__construct() has too many arguments starting with 'http'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
522
            $node = $treeBuilder->getRootNode();
0 ignored issues
show
Bug introduced by
The method getRootNode() does not seem to exist on object<Symfony\Component...on\Builder\TreeBuilder>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
523
        } else {
524 59
            $treeBuilder = new TreeBuilder();
525 59
            $node = $treeBuilder->root('http');
526
        }
527
528
        $node
529 59
            ->fixXmlConfig('server')
530 59
            ->children()
531 59
                ->arrayNode('servers')
532 59
                    ->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.')
533 59
                    ->useAttributeAsKey('name')
534 59
                    ->isRequired()
535 59
                    ->requiresAtLeastOneElement()
536 59
                    ->prototype('scalar')->end()
537 59
                ->end()
538 59
                ->scalarNode('base_url')
539 59
                    ->defaultNull()
540 59
                    ->info('Default host name and optional path for path based invalidation.')
541 59
                ->end()
542 59
                ->scalarNode('http_client')
543 59
                    ->defaultNull()
544 59
                    ->info('Httplug async client service name to use for sending the requests.')
545 59
                ->end()
546 59
            ->end()
547
        ;
548
549 59
        return $node;
550
    }
551
552 59
    private function addTestSection(ArrayNodeDefinition $rootNode)
553
    {
554
        $rootNode
555 59
            ->children()
556 59
                ->arrayNode('test')
557 59
                    ->children()
558 59
                        ->scalarNode('cache_header')
559 59
                            ->defaultValue('X-Cache')
560 59
                            ->info('HTTP cache hit/miss header')
561 59
                        ->end()
562 59
                        ->arrayNode('proxy_server')
563 59
                            ->info('Configure how caching proxy will be run in your tests')
564 59
                            ->children()
565 59
                                ->enumNode('default')
566 59
                                    ->values(['varnish', 'nginx'])
567 59
                                    ->info('If you configure more than one proxy server, specify which client is the default.')
568 59
                                ->end()
569 59
                                ->arrayNode('varnish')
570 59
                                    ->children()
571 59
                                        ->scalarNode('config_file')->isRequired()->end()
572 59
                                        ->scalarNode('binary')->defaultValue('varnishd')->end()
573 59
                                        ->integerNode('port')->defaultValue(6181)->end()
574 59
                                        ->scalarNode('ip')->defaultValue('127.0.0.1')->end()
575 59
                                    ->end()
576 59
                                ->end()
577 59
                                ->arrayNode('nginx')
578 59
                                    ->children()
579 59
                                        ->scalarNode('config_file')->isRequired()->end()
580 59
                                        ->scalarNode('binary')->defaultValue('nginx')->end()
581 59
                                        ->integerNode('port')->defaultValue(8080)->end()
582 59
                                        ->scalarNode('ip')->defaultValue('127.0.0.1')->end()
583 59
                                    ->end()
584 59
                                ->end()
585 59
                            ->end()
586 59
                        ->end()
587 59
                    ->end()
588 59
                ->end()
589 59
            ->end();
590 59
    }
591
592
    /**
593
     * Cache manager main section.
594
     *
595
     * @param ArrayNodeDefinition $rootNode
596
     */
597 59
    private function addCacheManagerSection(ArrayNodeDefinition $rootNode)
598
    {
599
        $rootNode
600 59
            ->children()
601 59
                ->arrayNode('cache_manager')
602 59
                    ->addDefaultsIfNotSet()
603 59
                    ->beforeNormalization()
604 59
                        ->ifArray()
605 59
                        ->then(function ($v) {
606 10
                            $v['enabled'] = isset($v['enabled']) ? $v['enabled'] : true;
607
608 10
                            return $v;
609 59
                        })
610 59
                    ->end()
611 59
                    ->info('Configure the cache manager. Needs a proxy_client to be configured.')
612 59
                    ->children()
613 59
                        ->enumNode('enabled')
614 59
                            ->values([true, false, 'auto'])
615 59
                            ->defaultValue('auto')
616 59
                            ->info('Allows to disable the invalidation manager. Enabled by default if you configure a proxy client.')
617 59
                        ->end()
618 59
                        ->scalarNode('custom_proxy_client')
619 59
                            ->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.')
620 59
                            ->cannotBeEmpty()
621 59
                        ->end()
622 59
                        ->enumNode('generate_url_type')
623 59
                            ->values([
624 59
                                'auto',
625
                                UrlGeneratorInterface::ABSOLUTE_PATH,
626
                                UrlGeneratorInterface::ABSOLUTE_URL,
627
                                UrlGeneratorInterface::NETWORK_PATH,
628
                                UrlGeneratorInterface::RELATIVE_PATH,
629
                            ])
630 59
                            ->defaultValue('auto')
631 59
                            ->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.')
632 59
                        ->end()
633 59
                    ->end()
634
        ;
635 59
    }
636
637 59
    private function addTagSection(ArrayNodeDefinition $rootNode)
638
    {
639
        $rules = $rootNode
640 59
            ->children()
641 59
                ->arrayNode('tags')
642 59
                    ->addDefaultsIfNotSet()
643 59
                    ->fixXmlConfig('rule')
644 59
                    ->children()
645 59
                        ->enumNode('enabled')
646 59
                            ->values([true, false, 'auto'])
647 59
                            ->defaultValue('auto')
648 59
                            ->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.')
649 59
                        ->end()
650 59
                        ->booleanNode('strict')->defaultFalse()->end()
651 59
                        ->scalarNode('expression_language')
652 59
                            ->defaultNull()
653 59
                            ->info('Service name of a custom ExpressionLanugage to use.')
654 59
                        ->end()
655 59
                        ->scalarNode('response_header')
656 59
                            ->defaultNull()
657 59
                            ->info('HTTP header that contains cache tags. Defaults to xkey-softpurge for Varnish xkey or X-Cache-Tags otherwise')
658 59
                        ->end()
659 59
                        ->scalarNode('separator')
660 59
                            ->defaultNull()
661 59
                            ->info('Character(s) to use to separate multiple tags. Defaults to " " for Varnish xkey or "," otherwise')
662 59
                        ->end()
663 59
                        ->scalarNode('max_header_value_length')
664 59
                            ->defaultNull()
665 59
                            ->info('If configured the tag header value will be split into multiple response headers of the same name (see "response_header" configuration key) that all do not exceed the configured "max_header_value_length" (recommended is 4KB = 4096) - configure in bytes.')
666 59
                        ->end()
667 59
                        ->arrayNode('rules')
668 59
                            ->prototype('array')
669 59
                                ->fixXmlConfig('tag')
670 59
                                ->fixXmlConfig('tag_expression')
671 59
                                ->validate()
672 59
                                    ->ifTrue(function ($v) {
673 4
                                        return !empty($v['tag_expressions']) && !class_exists(ExpressionLanguage::class);
674 59
                                    })
675 59
                                    ->thenInvalid('Configured a tag_expression but ExpressionLanugage is not available')
676 59
                                ->end()
677 59
                                ->children()
678
                        ;
679 59
        $this->addMatch($rules);
680
681
        $rules
682 59
            ->arrayNode('tags')
683 59
                ->prototype('scalar')
684 59
                ->info('Tags to add to the response on safe requests, to invalidate on unsafe requests')
685 59
            ->end()->end()
686 59
            ->arrayNode('tag_expressions')
687 59
                ->prototype('scalar')
688 59
                ->info('Tags to add to the response on safe requests, to invalidate on unsafe requests')
689 59
            ->end()
690
        ;
691 59
    }
692
693 59
    private function addInvalidationSection(ArrayNodeDefinition $rootNode)
694
    {
695
        $rules = $rootNode
696 59
            ->children()
697 59
                ->arrayNode('invalidation')
698 59
                    ->fixXmlConfig('rule')
699 59
                    ->addDefaultsIfNotSet()
700 59
                    ->children()
701 59
                        ->enumNode('enabled')
702 59
                            ->values([true, false, 'auto'])
703 59
                            ->defaultValue('auto')
704 59
                            ->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.')
705 59
                        ->end()
706 59
                        ->scalarNode('expression_language')
707 59
                            ->defaultNull()
708 59
                            ->info('Service name of a custom ExpressionLanugage to use.')
709 59
                        ->end()
710 59
                        ->arrayNode('rules')
711 59
                            ->info('Set what requests should invalidate which target routes.')
712 59
                            ->prototype('array')
713 59
                                ->fixXmlConfig('route')
714 59
                                ->children();
715
716 59
        $this->addMatch($rules);
717
        $rules
718 59
            ->arrayNode('routes')
719 59
                ->isRequired()
720 59
                ->requiresAtLeastOneElement()
721 59
                ->useAttributeAsKey('name')
722 59
                ->info('Target routes to invalidate when request is matched')
723 59
                ->prototype('array')
724 59
                    ->children()
725 59
                        ->booleanNode('ignore_extra_params')->defaultTrue()->end()
726 59
                    ->end()
727 59
                ->end()
728 59
            ->end();
729 59
    }
730
731
    /**
732
     * User context main section.
733
     *
734
     * @param ArrayNodeDefinition $rootNode
735
     */
736 59
    private function addUserContextListenerSection(ArrayNodeDefinition $rootNode)
737
    {
738
        $rootNode
739 59
            ->children()
740 59
                ->arrayNode('user_context')
741 59
                    ->info('Listener that returns the request for the user context hash as early as possible.')
742 59
                    ->addDefaultsIfNotSet()
743 59
                    ->canBeEnabled()
744 59
                    ->fixXmlConfig('user_identifier_header')
745 59
                    ->children()
746 59
                        ->arrayNode('match')
747 59
                            ->addDefaultsIfNotSet()
748 59
                            ->children()
749 59
                                ->scalarNode('matcher_service')
750 59
                                    ->defaultValue('fos_http_cache.user_context.request_matcher')
751 59
                                    ->info('Service id of a request matcher that tells whether the request is a context hash request.')
752 59
                                ->end()
753 59
                                ->scalarNode('accept')
754 59
                                    ->defaultValue('application/vnd.fos.user-context-hash')
755 59
                                    ->info('Specify the accept HTTP header used for context hash requests.')
756 59
                                ->end()
757 59
                                ->scalarNode('method')
758 59
                                    ->defaultNull()
759 59
                                    ->info('Specify the HTTP method used for context hash requests.')
760 59
                                ->end()
761 59
                            ->end()
762 59
                        ->end()
763 59
                        ->scalarNode('hash_cache_ttl')
764 59
                            ->defaultValue(0)
765 59
                            ->info('Cache the response for the hash for the specified number of seconds. Setting this to 0 will not cache those responses at all.')
766 59
                        ->end()
767 59
                        ->booleanNode('always_vary_on_context_hash')
768 59
                            ->defaultTrue()
769 59
                            ->info('Whether to always add the user context hash header name in the response Vary header.')
770 59
                        ->end()
771 59
                        ->arrayNode('user_identifier_headers')
772 59
                            ->prototype('scalar')->end()
773 59
                            ->defaultValue(['Cookie', 'Authorization'])
774 59
                            ->info('List of headers that contain the unique identifier for the user in the hash request.')
775 59
                        ->end()
776 59
                        ->scalarNode('session_name_prefix')
777 59
                            ->defaultValue(false)
778 59
                            ->info('Prefix for session cookies. Must match your PHP session configuration. Set to false to ignore the session in user context.')
779 59
                        ->end()
780 59
                        ->scalarNode('user_hash_header')
781 59
                            ->defaultValue('X-User-Context-Hash')
782 59
                            ->info('Name of the header that contains the hash information for the context.')
783 59
                        ->end()
784 59
                        ->booleanNode('role_provider')
785 59
                            ->defaultFalse()
786 59
                            ->info('Whether to enable a provider that automatically adds all roles of the current user to the context.')
787 59
                        ->end()
788 59
                        ->arrayNode('logout_handler')
789 59
                            ->addDefaultsIfNotSet()
790 59
                            ->canBeEnabled()
791 59
                            ->children()
792 59
                                ->enumNode('enabled')
793 59
                                    ->values([true, false, 'auto'])
794 59
                                    ->defaultValue('auto')
795 59
                                    ->info('Whether to enable the user context logout handler.')
796 59
                                ->end()
797 59
                            ->end()
798 59
                        ->end()
799 59
                    ->end()
800 59
                ->end()
801 59
            ->end()
802
        ;
803 59
    }
804
805 59
    private function addFlashMessageSection(ArrayNodeDefinition $rootNode)
806
    {
807
        $rootNode
808 59
            ->children()
809 59
                ->arrayNode('flash_message')
810 59
                    ->canBeUnset()
811 59
                    ->canBeEnabled()
812 59
                    ->info('Activate the flash message listener that puts flash messages into a cookie.')
813 59
                    ->children()
814 59
                        ->scalarNode('name')
815 59
                            ->defaultValue('flashes')
816 59
                            ->info('Name of the cookie to set for flashes.')
817 59
                        ->end()
818 59
                        ->scalarNode('path')
819 59
                            ->defaultValue('/')
820 59
                            ->info('Cookie path validity.')
821 59
                        ->end()
822 59
                        ->scalarNode('host')
823 59
                            ->defaultNull()
824 59
                            ->info('Cookie host name validity.')
825 59
                        ->end()
826 59
                        ->scalarNode('secure')
827 59
                            ->defaultFalse()
828 59
                            ->info('Whether the cookie should only be transmitted over a secure HTTPS connection from the client.')
829 59
                        ->end()
830 59
                    ->end()
831 59
                ->end()
832 59
            ->end();
833 59
    }
834
835 59
    private function addDebugSection(ArrayNodeDefinition $rootNode)
836
    {
837
        $rootNode
838 59
            ->children()
839 59
                ->arrayNode('debug')
840 59
                ->addDefaultsIfNotSet()
841 59
                ->canBeEnabled()
842 59
                ->children()
843 59
                    ->booleanNode('enabled')
844 59
                        ->defaultValue($this->debug)
845 59
                        ->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.')
846 59
                    ->end()
847 59
                    ->scalarNode('header')
848 59
                        ->defaultValue('X-Cache-Debug')
849 59
                        ->info('The header to send if debug is true.')
850 59
                    ->end()
851 59
                ->end()
852 59
            ->end()
853 59
        ->end();
854 59
    }
855
}
856