Completed
Push — master ( ee5c21...b9bd8f )
by David
11:16
created

Configuration::addCacheControlSection()   B

Complexity

Conditions 2
Paths 1

Size

Total Lines 79
Code Lines 71

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 71
CRAP Score 2

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 79
ccs 71
cts 71
cp 1
rs 8.8701
cc 2
eloc 71
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\TagHeaderFormatter\TagHeaderFormatter;
16
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
17
use Symfony\Component\Config\Definition\Builder\NodeBuilder;
18
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
19
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
20
use Symfony\Component\Config\Definition\ConfigurationInterface;
21
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
22
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
23
24
/**
25
 * This class contains the configuration information for the bundle.
26
 *
27
 * This information is solely responsible for how the different configuration
28
 * sections are normalized, and merged.
29
 *
30
 * @author David de Boer <[email protected]>
31
 * @author David Buchmann <[email protected]>
32
 */
33
class Configuration implements ConfigurationInterface
34
{
35
    /**
36
     * @var bool
37
     */
38
    private $debug;
39
40
    /**
41
     * @param bool $debug Whether to use the debug mode
42
     */
43 39
    public function __construct($debug)
44
    {
45 39
        $this->debug = $debug;
46 39
    }
47
48
    /**
49
     * {@inheritdoc}
50
     */
51 39
    public function getConfigTreeBuilder()
52
    {
53 39
        $treeBuilder = new TreeBuilder();
54 39
        $rootNode = $treeBuilder->root('fos_http_cache');
55
56
        $rootNode
57 39
            ->validate()
58 39
                ->ifTrue(function ($v) {
59 38
                    return $v['cache_manager']['enabled']
60 38
                        && !isset($v['proxy_client'])
61 38
                        && !isset($v['cache_manager']['custom_proxy_client'])
62
                    ;
63 39
                })
64 39
                ->then(function ($v) {
65 12
                    if ('auto' === $v['cache_manager']['enabled']) {
66 11
                        $v['cache_manager']['enabled'] = false;
67
68 11
                        return $v;
69
                    }
70 1
                    throw new InvalidConfigurationException('You need to configure a proxy_client or specify a custom_proxy_client to use the cache_manager.');
71 39
                })
72 39
            ->end()
73 39
            ->validate()
74 39
                ->ifTrue(function ($v) {
75 37
                    return $v['tags']['enabled'] && !$v['cache_manager']['enabled'];
76 39
                })
77 39
                ->then(function ($v) {
78 13
                    if ('auto' === $v['tags']['enabled']) {
79 12
                        $v['tags']['enabled'] = false;
80
81 12
                        return $v;
82
                    }
83 1
                    throw new InvalidConfigurationException('You need to configure a proxy_client to get the cache_manager needed for tag handling.');
84 39
                })
85 39
            ->end()
86 39
            ->validate()
87 39
                ->ifTrue(function ($v) {
88 36
                    return $v['invalidation']['enabled'] && !$v['cache_manager']['enabled'];
89 39
                })
90 39
                ->then(function ($v) {
91 12
                    if ('auto' === $v['invalidation']['enabled']) {
92 11
                        $v['invalidation']['enabled'] = false;
93
94 11
                        return $v;
95
                    }
96 1
                    throw new InvalidConfigurationException('You need to configure a proxy_client to get the cache_manager needed for invalidation handling.');
97 39
                })
98 39
            ->end()
99 39
            ->validate()
100 39
                ->ifTrue(
101 39
                    function ($v) {
102 35
                        return $v['user_context']['logout_handler']['enabled']
103 35
                            && !isset($v['proxy_client']);
104 39
                    }
105
                )
106 39
                ->then(function ($v) {
107 13
                    if ('auto' === $v['user_context']['logout_handler']['enabled']) {
108 13
                        $v['user_context']['logout_handler']['enabled'] = false;
109
110 13
                        return $v;
111
                    }
112
                    throw new InvalidConfigurationException('You need to configure a proxy_client for the logout_handler.');
113 39
                })
114
        ;
115
116 39
        $this->addCacheableResponseSection($rootNode);
117 39
        $this->addCacheControlSection($rootNode);
118 39
        $this->addProxyClientSection($rootNode);
119 39
        $this->addCacheManagerSection($rootNode);
120 39
        $this->addTagSection($rootNode);
121 39
        $this->addInvalidationSection($rootNode);
122 39
        $this->addUserContextListenerSection($rootNode);
123 39
        $this->addFlashMessageSection($rootNode);
124 39
        $this->addTestSection($rootNode);
125 39
        $this->addDebugSection($rootNode);
126
127 39
        return $treeBuilder;
128
    }
129
130 39
    private function addCacheableResponseSection(ArrayNodeDefinition $rootNode)
131
    {
132
        $rootNode
133 39
            ->children()
134 39
                ->arrayNode('cacheable')
135 39
                    ->addDefaultsIfNotSet()
136 39
                    ->children()
137 39
                        ->arrayNode('response')
138 39
                            ->addDefaultsIfNotSet()
139 39
                            ->children()
140 39
                                ->arrayNode('additional_status')
141 39
                                    ->prototype('scalar')->end()
142 39
                                    ->info('Additional response HTTP status codes that will be considered cacheable.')
143 39
                                ->end()
144 39
                                ->scalarNode('expression')
145 39
                                    ->defaultNull()
146 39
                                    ->info('Expression to decide whether response is cacheable. Replaces the default status codes.')
147 39
                            ->end()
148 39
                        ->end()
149
150 39
                        ->validate()
151 39
                            ->ifTrue(function ($v) {
152 3
                                return !empty($v['additional_status']) && !empty($v['expression']);
153 39
                            })
154 39
                            ->thenInvalid('You may not set both additional_status and expression.')
155 39
                        ->end()
156 39
                    ->end()
157 39
                ->end()
158 39
            ->end();
159 39
    }
160
161
    /**
162
     * Cache header control main section.
163
     *
164
     * @param ArrayNodeDefinition $rootNode
165
     */
166 39
    private function addCacheControlSection(ArrayNodeDefinition $rootNode)
167
    {
168
        $rules = $rootNode
169 39
            ->children()
170 39
                ->arrayNode('cache_control')
171 39
                    ->fixXmlConfig('rule')
172 39
                    ->children()
173 39
                        ->arrayNode('defaults')
174 39
                            ->addDefaultsIfNotSet()
175 39
                            ->children()
176 39
                                ->booleanNode('overwrite')
177 39
                                    ->info('Whether to overwrite existing cache headers')
178 39
                                    ->defaultFalse()
179 39
                                ->end()
180 39
                            ->end()
181 39
                        ->end()
182 39
                        ->arrayNode('rules')
183 39
                            ->prototype('array')
184 39
                                ->children();
185
186 39
        $this->addMatch($rules, true);
187
        $rules
188 39
            ->arrayNode('headers')
189 39
                ->isRequired()
190
                // todo validate there is some header defined
191 39
                ->children()
192 39
                    ->enumNode('overwrite')
193 39
                        ->info('Whether to overwrite cache headers for this rule, defaults to the cache_control.defaults.overwrite setting')
194 39
                        ->values(['default', true, false])
195 39
                        ->defaultValue('default')
196 39
                    ->end()
197 39
                    ->arrayNode('cache_control')
198 39
                        ->info('Add the specified cache control directives.')
199 39
                        ->children()
200 39
                            ->scalarNode('max_age')->end()
201 39
                            ->scalarNode('s_maxage')->end()
202 39
                            ->booleanNode('private')->end()
203 39
                            ->booleanNode('public')->end()
204 39
                            ->booleanNode('must_revalidate')->end()
205 39
                            ->booleanNode('proxy_revalidate')->end()
206 39
                            ->booleanNode('no_transform')->end()
207 39
                            ->booleanNode('no_cache')->end()
208 39
                            ->booleanNode('no_store')->end()
209 39
                            ->scalarNode('stale_if_error')->end()
210 39
                            ->scalarNode('stale_while_revalidate')->end()
211 39
                        ->end()
212 39
                    ->end()
213 39
                    ->booleanNode('etag')
214 39
                        ->defaultValue(false)
215 39
                        ->info('Set a simple ETag which is just the md5 hash of the response body')
216 39
                    ->end()
217 39
                    ->scalarNode('last_modified')
218 39
                        ->validate()
219 39
                            ->ifTrue(function ($v) {
220 2
                                if (is_string($v)) {
221 2
                                    new \DateTime($v);
222
                                }
223
224 1
                                return false;
225 39
                            })
226 39
                            ->thenInvalid('') // this will never happen as new DateTime will throw an exception if $v is no date
227 39
                        ->end()
228 39
                        ->info('Set a default last modified timestamp if none is set yet. Value must be parseable by DateTime')
229 39
                    ->end()
230 39
                    ->scalarNode('reverse_proxy_ttl')
231 39
                        ->defaultNull()
232 39
                        ->info('Specify an X-Reverse-Proxy-TTL header with a time in seconds for a caching proxy under your control.')
233 39
                    ->end()
234 39
                    ->arrayNode('vary')
235 39
                        ->beforeNormalization()->ifString()->then(function ($v) {
236 2
                            return preg_split('/\s*,\s*/', $v);
237 39
                        })->end()
238 39
                        ->prototype('scalar')->end()
239 39
                        ->info('Define a list of additional headers on which the response varies.')
240 39
                    ->end()
241 39
                ->end()
242 39
            ->end()
243
        ;
244 39
    }
245
246
    /**
247
     * Shared configuration between cache control, tags and invalidation.
248
     *
249
     * @param NodeBuilder $rules
250
     * @param bool        $matchResponse whether to also add fields to match response
251
     */
252 39
    private function addMatch(NodeBuilder $rules, $matchResponse = false)
253
    {
254
        $match = $rules
255 39
            ->arrayNode('match')
256 39
                ->cannotBeOverwritten()
257 39
                ->isRequired()
258 39
                ->fixXmlConfig('method')
259 39
                ->fixXmlConfig('ip')
260 39
                ->fixXmlConfig('attribute')
261 39
                ->validate()
262 39
                    ->ifTrue(function ($v) {
263 11
                        return !empty($v['additional_response_status']) && !empty($v['match_response']);
264 39
                    })
265 39
                    ->thenInvalid('You may not set both additional_response_status and match_response.')
266 39
                ->end()
267 39
                ->children()
268 39
                    ->scalarNode('path')
269 39
                        ->defaultNull()
270 39
                        ->info('Request path.')
271 39
                    ->end()
272 39
                    ->scalarNode('host')
273 39
                        ->defaultNull()
274 39
                        ->info('Request host name.')
275 39
                    ->end()
276 39
                    ->arrayNode('methods')
277 39
                        ->beforeNormalization()->ifString()->then(function ($v) {
278 3
                            return preg_split('/\s*,\s*/', $v);
279 39
                        })->end()
280 39
                        ->useAttributeAsKey('name')
281 39
                        ->prototype('scalar')->end()
282 39
                        ->info('Request HTTP methods.')
283 39
                    ->end()
284 39
                    ->arrayNode('ips')
285 39
                        ->beforeNormalization()->ifString()->then(function ($v) {
286 3
                            return preg_split('/\s*,\s*/', $v);
287 39
                        })->end()
288 39
                        ->useAttributeAsKey('name')
289 39
                        ->prototype('scalar')->end()
290 39
                        ->info('List of client IPs.')
291 39
                    ->end()
292 39
                    ->arrayNode('attributes')
293 39
                        ->useAttributeAsKey('name')
294 39
                        ->prototype('scalar')->end()
295 39
                        ->info('Regular expressions on request attributes.')
296 39
                    ->end()
297
        ;
298 39
        if ($matchResponse) {
299
            $match
300 39
                ->arrayNode('additional_response_status')
301 39
                    ->prototype('scalar')->end()
302 39
                    ->info('Additional response HTTP status codes that will match. Replaces cacheable configuration.')
303 39
                ->end()
304 39
                ->scalarNode('match_response')
305 39
                    ->defaultNull()
306 39
                    ->info('Expression to decide whether response should be matched. Replaces cacheable configuration.')
307 39
                ->end()
308
            ;
309
        }
310 39
    }
311
312 39
    private function addProxyClientSection(ArrayNodeDefinition $rootNode)
313
    {
314
        $rootNode
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\Config...\Builder\NodeDefinition as the method children() does only exist in the following sub-classes of Symfony\Component\Config...\Builder\NodeDefinition: Symfony\Component\Config...der\ArrayNodeDefinition. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
315 39
            ->children()
316 39
                ->arrayNode('proxy_client')
317 39
                    ->children()
318 39
                        ->enumNode('default')
319 39
                            ->values(['varnish', 'nginx', 'symfony', 'noop'])
320 39
                            ->info('If you configure more than one proxy client, you need to specify which client is the default.')
321 39
                        ->end()
322 39
                        ->arrayNode('varnish')
323 39
                            ->fixXmlConfig('default_ban_header')
324 39
                            ->validate()
325 39
                                ->always(function ($v) {
326 15
                                    if (!count($v['default_ban_headers'])) {
327 14
                                        unset($v['default_ban_headers']);
328
                                    }
329
330 15
                                    return $v;
331 39
                                })
332 39
                            ->end()
333 39
                            ->children()
334 39
                                ->scalarNode('tags_header')
335 39
                                    ->defaultValue(Varnish::DEFAULT_HTTP_HEADER_CACHE_TAGS)
336 39
                                    ->info('HTTP header to use when sending tag invalidation requests to Varnish')
337 39
                                ->end()
338 39
                                ->scalarNode('header_length')
339 39
                                    ->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.')
340 39
                                ->end()
341 39
                                ->arrayNode('default_ban_headers')
342 39
                                    ->useAttributeAsKey('name')
343 39
                                    ->info('Map of additional headers to include in each ban request.')
344 39
                                    ->prototype('scalar')->end()
345 39
                                ->end()
346 39
                                ->append($this->getHttpDispatcherNode())
347 39
                            ->end()
348 39
                        ->end()
349
350 39
                        ->arrayNode('nginx')
351 39
                            ->children()
352 39
                                ->scalarNode('purge_location')
353 39
                                    ->defaultValue(false)
354 39
                                    ->info('Path to trigger the purge on Nginx for different location purge.')
355 39
                                ->end()
356 39
                                ->append($this->getHttpDispatcherNode())
357 39
                            ->end()
358 39
                        ->end()
359
360 39
                        ->arrayNode('symfony')
361 39
                            ->children()
362 39
                                ->append($this->getHttpDispatcherNode())
363 39
                            ->end()
364 39
                        ->end()
365
366 39
                        ->booleanNode('noop')->end()
367
368 39
                    ->end()
369 39
                ->end()
370 39
            ->end();
371 39
    }
372
373
    /**
374
     * Get the configuration node for a HTTP dispatcher in a proxy client.
375
     *
376
     * @return NodeDefinition
377
     */
378 39
    private function getHttpDispatcherNode()
379
    {
380 39
        $treeBuilder = new TreeBuilder();
381 39
        $node = $treeBuilder->root('http');
382
383
        $node
384 39
            ->fixXmlConfig('server')
385 39
            ->isRequired()
386 39
            ->children()
387 39
                ->arrayNode('servers')
388 39
                    ->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.')
389 39
                    ->useAttributeAsKey('name')
390 39
                    ->isRequired()
391 39
                    ->requiresAtLeastOneElement()
392 39
                    ->prototype('scalar')->end()
393 39
                ->end()
394 39
                ->scalarNode('base_url')
395 39
                    ->defaultNull()
396 39
                    ->info('Default host name and optional path for path based invalidation.')
397 39
                ->end()
398 39
                ->scalarNode('http_client')
399 39
                    ->defaultNull()
400 39
                    ->info('Httplug async client service name to use for sending the requests.')
401 39
                ->end()
402 39
            ->end()
403
        ;
404
405 39
        return $node;
406
    }
407
408 39
    private function addTestSection(ArrayNodeDefinition $rootNode)
409
    {
410
        $rootNode
411 39
            ->children()
412 39
                ->arrayNode('test')
413 39
                    ->children()
414 39
                        ->scalarNode('cache_header')
415 39
                            ->defaultValue('X-Cache')
416 39
                            ->info('HTTP cache hit/miss header')
417 39
                        ->end()
418 39
                        ->arrayNode('proxy_server')
419 39
                            ->info('Configure how caching proxy will be run in your tests')
420 39
                            ->children()
421 39
                                ->enumNode('default')
422 39
                                    ->values(['varnish', 'nginx'])
423 39
                                    ->info('If you configure more than one proxy server, specify which client is the default.')
424 39
                                ->end()
425 39
                                ->arrayNode('varnish')
426 39
                                    ->children()
427 39
                                        ->scalarNode('config_file')->isRequired()->end()
428 39
                                        ->scalarNode('binary')->defaultValue('varnishd')->end()
429 39
                                        ->integerNode('port')->defaultValue(6181)->end()
430 39
                                        ->scalarNode('ip')->defaultValue('127.0.0.1')->end()
431 39
                                    ->end()
432 39
                                ->end()
433 39
                                ->arrayNode('nginx')
434 39
                                    ->children()
435 39
                                        ->scalarNode('config_file')->isRequired()->end()
436 39
                                        ->scalarNode('binary')->defaultValue('nginx')->end()
437 39
                                        ->integerNode('port')->defaultValue(8080)->end()
438 39
                                        ->scalarNode('ip')->defaultValue('127.0.0.1')->end()
439 39
                                    ->end()
440 39
                                ->end()
441 39
                            ->end()
442 39
                        ->end()
443 39
                    ->end()
444 39
                ->end()
445 39
            ->end();
446 39
    }
447
448
    /**
449
     * Cache manager main section.
450
     *
451
     * @param ArrayNodeDefinition $rootNode
452
     */
453 39
    private function addCacheManagerSection(ArrayNodeDefinition $rootNode)
454
    {
455
        $rootNode
456 39
            ->children()
457 39
                ->arrayNode('cache_manager')
458 39
                    ->addDefaultsIfNotSet()
459 39
                    ->beforeNormalization()
460 39
                        ->ifArray()
461 39
                        ->then(function ($v) {
462 6
                            $v['enabled'] = isset($v['enabled']) ? $v['enabled'] : true;
463
464 6
                            return $v;
465 39
                        })
466 39
                    ->end()
467 39
                    ->info('Configure the cache manager. Needs a proxy_client to be configured.')
468 39
                    ->children()
469 39
                        ->enumNode('enabled')
470 39
                            ->values([true, false, 'auto'])
471 39
                            ->defaultValue('auto')
472 39
                            ->info('Allows to disable the invalidation manager. Enabled by default if you configure a proxy client.')
473 39
                        ->end()
474 39
                        ->scalarNode('custom_proxy_client')
475 39
                            ->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.')
476 39
                            ->cannotBeEmpty()
477 39
                        ->end()
478 39
                        ->enumNode('generate_url_type')
479 39
                            ->values([
480 39
                                'auto',
481
                                UrlGeneratorInterface::ABSOLUTE_PATH,
482
                                UrlGeneratorInterface::ABSOLUTE_URL,
483
                                UrlGeneratorInterface::NETWORK_PATH,
484
                                UrlGeneratorInterface::RELATIVE_PATH,
485
                            ])
486 39
                            ->defaultValue('auto')
487 39
                            ->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.')
488 39
                        ->end()
489 39
                    ->end()
490
        ;
491 39
    }
492
493 39
    private function addTagSection(ArrayNodeDefinition $rootNode)
494
    {
495
        $rules = $rootNode
496 39
            ->children()
497 39
                ->arrayNode('tags')
498 39
                    ->addDefaultsIfNotSet()
499 39
                    ->fixXmlConfig('rule')
500 39
                    ->children()
501 39
                        ->enumNode('enabled')
502 39
                            ->values([true, false, 'auto'])
503 39
                            ->defaultValue('auto')
504 39
                            ->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.')
505 39
                        ->end()
506 39
                        ->booleanNode('strict')->defaultFalse()->end()
507 39
                        ->scalarNode('expression_language')
508 39
                            ->defaultNull()
509 39
                            ->info('Service name of a custom ExpressionLanugage to use.')
510 39
                        ->end()
511 39
                        ->scalarNode('response_header')
512 39
                            ->defaultValue(TagHeaderFormatter::DEFAULT_HEADER_NAME)
513 39
                            ->info('HTTP header that contains cache tags')
514 39
                        ->end()
515 39
                        ->arrayNode('rules')
516 39
                            ->prototype('array')
517 39
                                ->fixXmlConfig('tag')
518 39
                                ->fixXmlConfig('tag_expression')
519 39
                                ->validate()
520 39
                                    ->ifTrue(function ($v) {
521 3
                                        return !empty($v['tag_expressions']) && !class_exists('Symfony\Component\ExpressionLanguage\ExpressionLanguage');
522 39
                                    })
523 39
                                    ->thenInvalid('Configured a tag_expression but ExpressionLanugage is not available')
524 39
                                ->end()
525 39
                                ->children();
526
527 39
        $this->addMatch($rules);
528
529
        $rules
530 39
            ->arrayNode('tags')
531 39
                ->prototype('scalar')
532 39
                ->info('Tags to add to the response on safe requests, to invalidate on unsafe requests')
533 39
            ->end()->end()
534 39
            ->arrayNode('tag_expressions')
535 39
                ->prototype('scalar')
536 39
                ->info('Tags to add to the response on safe requests, to invalidate on unsafe requests')
537 39
            ->end()
538
        ;
539 39
    }
540
541 39
    private function addInvalidationSection(ArrayNodeDefinition $rootNode)
542
    {
543
        $rules = $rootNode
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Symfony\Component\Config...\Builder\NodeDefinition as the method fixXmlConfig() does only exist in the following sub-classes of Symfony\Component\Config...\Builder\NodeDefinition: Symfony\Component\Config...der\ArrayNodeDefinition. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
544 39
            ->children()
545 39
                ->arrayNode('invalidation')
546 39
                    ->fixXmlConfig('rule')
547 39
                    ->addDefaultsIfNotSet()
548 39
                    ->children()
549 39
                        ->enumNode('enabled')
550 39
                            ->values([true, false, 'auto'])
551 39
                            ->defaultValue('auto')
552 39
                            ->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.')
553 39
                        ->end()
554 39
                        ->scalarNode('expression_language')
555 39
                            ->defaultNull()
556 39
                            ->info('Service name of a custom ExpressionLanugage to use.')
557 39
                        ->end()
558 39
                        ->arrayNode('rules')
559 39
                            ->info('Set what requests should invalidate which target routes.')
560 39
                            ->prototype('array')
561 39
                                ->fixXmlConfig('route')
562 39
                                ->children();
563
564 39
        $this->addMatch($rules);
565
        $rules
566 39
            ->arrayNode('routes')
567 39
                ->isRequired()
568 39
                ->requiresAtLeastOneElement()
569 39
                ->useAttributeAsKey('name')
570 39
                ->info('Target routes to invalidate when request is matched')
571 39
                ->prototype('array')
572 39
                    ->children()
573 39
                        ->booleanNode('ignore_extra_params')->defaultTrue()->end()
574 39
                    ->end()
575 39
                ->end()
576 39
            ->end();
577 39
    }
578
579
    /**
580
     * User context main section.
581
     *
582
     * @param ArrayNodeDefinition $rootNode
583
     */
584 39
    private function addUserContextListenerSection(ArrayNodeDefinition $rootNode)
585
    {
586
        $rootNode
587 39
            ->children()
588 39
                ->arrayNode('user_context')
589 39
                    ->info('Listener that returns the request for the user context hash as early as possible.')
590 39
                    ->addDefaultsIfNotSet()
591 39
                    ->canBeEnabled()
592 39
                    ->fixXmlConfig('user_identifier_header')
593 39
                    ->children()
594 39
                        ->arrayNode('match')
595 39
                            ->addDefaultsIfNotSet()
596 39
                            ->children()
597 39
                                ->scalarNode('matcher_service')
598 39
                                    ->defaultValue('fos_http_cache.user_context.request_matcher')
599 39
                                    ->info('Service id of a request matcher that tells whether the request is a context hash request.')
600 39
                                ->end()
601 39
                                ->scalarNode('accept')
602 39
                                    ->defaultValue('application/vnd.fos.user-context-hash')
603 39
                                    ->info('Specify the accept HTTP header used for context hash requests.')
604 39
                                ->end()
605 39
                                ->scalarNode('method')
606 39
                                    ->defaultNull()
607 39
                                    ->info('Specify the HTTP method used for context hash requests.')
608 39
                                ->end()
609 39
                            ->end()
610 39
                        ->end()
611 39
                        ->scalarNode('hash_cache_ttl')
612 39
                            ->defaultValue(0)
613 39
                            ->info('Cache the response for the hash for the specified number of seconds. Setting this to 0 will not cache those responses at all.')
614 39
                        ->end()
615 39
                        ->booleanNode('always_vary_on_context_hash')
616 39
                            ->defaultTrue()
617 39
                            ->info('Whether to always add the user context hash header name in the response Vary header.')
618 39
                        ->end()
619 39
                        ->arrayNode('user_identifier_headers')
620 39
                            ->prototype('scalar')->end()
621 39
                            ->defaultValue(['Cookie', 'Authorization'])
622 39
                            ->info('List of headers that contains the unique identifier for the user in the hash request.')
623 39
                        ->end()
624 39
                        ->scalarNode('user_hash_header')
625 39
                            ->defaultValue('X-User-Context-Hash')
626 39
                            ->info('Name of the header that contains the hash information for the context.')
627 39
                        ->end()
628 39
                        ->booleanNode('role_provider')
629 39
                            ->defaultFalse()
630 39
                            ->info('Whether to enable a provider that automatically adds all roles of the current user to the context.')
631 39
                        ->end()
632 39
                        ->arrayNode('logout_handler')
633 39
                            ->addDefaultsIfNotSet()
634 39
                            ->canBeEnabled()
635 39
                            ->children()
636 39
                                ->enumNode('enabled')
637 39
                                    ->values([true, false, 'auto'])
638 39
                                    ->defaultValue('auto')
639 39
                                    ->info('Whether to enable the user context logout handler.')
640 39
                                ->end()
641 39
                            ->end()
642 39
                        ->end()
643 39
                    ->end()
644 39
                ->end()
645 39
            ->end()
646
        ;
647 39
    }
648
649 39
    private function addFlashMessageSection(ArrayNodeDefinition $rootNode)
650
    {
651
        $rootNode
652 39
            ->children()
653 39
                ->arrayNode('flash_message')
654 39
                    ->canBeUnset()
655 39
                    ->canBeEnabled()
656 39
                    ->info('Activate the flash message listener that puts flash messages into a cookie.')
657 39
                    ->children()
658 39
                        ->scalarNode('name')
659 39
                            ->defaultValue('flashes')
660 39
                            ->info('Name of the cookie to set for flashes.')
661 39
                        ->end()
662 39
                        ->scalarNode('path')
663 39
                            ->defaultValue('/')
664 39
                            ->info('Cookie path validity.')
665 39
                        ->end()
666 39
                        ->scalarNode('host')
667 39
                            ->defaultNull()
668 39
                            ->info('Cookie host name validity.')
669 39
                        ->end()
670 39
                        ->scalarNode('secure')
671 39
                            ->defaultFalse()
672 39
                            ->info('Whether the cookie should only be transmitted over a secure HTTPS connection from the client.')
673 39
                        ->end()
674 39
                    ->end()
675 39
                ->end()
676 39
            ->end();
677 39
    }
678
679 39
    private function addDebugSection(ArrayNodeDefinition $rootNode)
680
    {
681
        $rootNode
682 39
            ->children()
683 39
                ->arrayNode('debug')
684 39
                ->addDefaultsIfNotSet()
685 39
                ->canBeEnabled()
686 39
                ->children()
687 39
                    ->booleanNode('enabled')
688 39
                        ->defaultValue($this->debug)
689 39
                        ->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.')
690 39
                    ->end()
691 39
                    ->scalarNode('header')
692 39
                        ->defaultValue('X-Cache-Debug')
693 39
                        ->info('The header to send if debug is true.')
694 39
                    ->end()
695 39
                ->end()
696 39
            ->end()
697 39
        ->end();
698 39
    }
699
}
700