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