FOSHttpCacheExtension   F
last analyzed

Complexity

Total Complexity 97

Size/Duplication

Total Lines 605
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 90.71%

Importance

Changes 0
Metric Value
wmc 97
lcom 1
cbo 9
dl 0
loc 605
ccs 283
cts 312
cp 0.9071
rs 1.995
c 0
b 0
f 0

24 Methods

Rating   Name   Duplication   Size   Complexity  
A getConfiguration() 0 4 1
A loadCacheable() 0 15 2
A loadCacheControl() 0 14 3
A parseRuleMatcher() 0 20 2
A parseRequestMatcher() 0 27 4
A parseResponseMatcher() 0 24 5
F load() 0 97 22
B loadUserContext() 0 59 7
A loadProxyClient() 0 24 5
B createHttpDispatcherDefinition() 0 33 6
A loadVarnish() 0 18 3
A loadNginx() 0 8 1
A loadSymfony() 0 25 3
B loadCacheTagging() 0 39 8
A loadTest() 0 8 2
A loadProxyServer() 0 15 3
A loadVarnishProxyServer() 0 10 2
A loadNginxProxyServer() 0 10 2
A loadTagRules() 0 15 2
A loadInvalidatorRules() 0 9 2
A validateUrl() 0 8 2
A prefixSchema() 0 8 2
B getDefaultProxyClient() 0 24 6
A createChildDefinition() 0 8 2

How to fix   Complexity   

Complex Class

Complex classes like FOSHttpCacheExtension often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FOSHttpCacheExtension, and based on these observations, apply Extract Interface, too.

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\HttpDispatcher;
15
use FOS\HttpCache\ProxyClient\ProxyClient;
16
use FOS\HttpCache\SymfonyCache\KernelDispatcher;
17
use FOS\HttpCache\TagHeaderFormatter\MaxHeaderValueLengthFormatter;
18
use FOS\HttpCacheBundle\DependencyInjection\Compiler\HashGeneratorPass;
19
use FOS\HttpCacheBundle\Http\ResponseMatcher\ExpressionResponseMatcher;
20
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
21
use Symfony\Component\Config\FileLocator;
22
use Symfony\Component\Console\Application;
23
use Symfony\Component\DependencyInjection\ChildDefinition;
24
use Symfony\Component\DependencyInjection\ContainerBuilder;
25
use Symfony\Component\DependencyInjection\Definition;
26
use Symfony\Component\DependencyInjection\DefinitionDecorator;
27
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
28
use Symfony\Component\DependencyInjection\Reference;
29
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
30
use Symfony\Component\HttpKernel\Kernel;
31
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
32
33
/**
34
 * {@inheritdoc}
35
 */
36
class FOSHttpCacheExtension extends Extension
37
{
38
    /**
39
     * {@inheritdoc}
40
     */
41 37
    public function getConfiguration(array $config, ContainerBuilder $container)
42
    {
43 37
        return new Configuration($container->getParameter('kernel.debug'));
44
    }
45
46
    /**
47
     * {@inheritdoc}
48
     */
49 37
    public function load(array $configs, ContainerBuilder $container)
50
    {
51 37
        $configuration = $this->getConfiguration($configs, $container);
52 37
        $config = $this->processConfiguration($configuration, $configs);
0 ignored issues
show
Bug introduced by David de Boer
It seems like $configuration defined by $this->getConfiguration($configs, $container) on line 51 can be null; however, Symfony\Component\Depend...:processConfiguration() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
53
54 37
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
55 37
        $loader->load('matcher.xml');
56
57 37
        if ($config['debug']['enabled'] || (!empty($config['cache_control']))) {
58 7
            $debugHeader = $config['debug']['enabled'] ? $config['debug']['header'] : false;
59 7
            $container->setParameter('fos_http_cache.debug_header', $debugHeader);
60 7
            $loader->load('cache_control_listener.xml');
61
        }
62
63 37
        $this->loadCacheable($container, $config['cacheable']);
64
65 37
        if (!empty($config['cache_control'])) {
66 7
            $this->loadCacheControl($container, $config['cache_control']);
67
        }
68
69 36
        if (isset($config['proxy_client'])) {
70 27
            $this->loadProxyClient($container, $loader, $config['proxy_client']);
71
        }
72
73 35
        if (isset($config['test'])) {
74 2
            $this->loadTest($container, $loader, $config['test']);
75
        }
76
77 35
        if ($config['cache_manager']['enabled']) {
78 27
            if (array_key_exists('custom_proxy_client', $config['cache_manager'])) {
79
                // overwrite the previously set alias, if a proxy client was also configured
80 1
                $container->setAlias(
81 1
                    'fos_http_cache.default_proxy_client',
82 1
                    $config['cache_manager']['custom_proxy_client']
83
                );
84
            }
85 27
            if ('auto' === $config['cache_manager']['generate_url_type']) {
86 27
                if (array_key_exists('custom_proxy_client', $config['cache_manager'])) {
87 1
                    $generateUrlType = UrlGeneratorInterface::ABSOLUTE_URL;
88
                } else {
89 26
                    $defaultClient = $this->getDefaultProxyClient($config['proxy_client']);
90 26
                    if ('noop' !== $defaultClient
91 26
                        && array_key_exists('base_url', $config['proxy_client'][$defaultClient])) {
92
                        $generateUrlType = UrlGeneratorInterface::ABSOLUTE_PATH;
93
                    } else {
94 27
                        $generateUrlType = UrlGeneratorInterface::ABSOLUTE_URL;
95
                    }
96
                }
97
            } else {
98
                $generateUrlType = $config['cache_manager']['generate_url_type'];
99
            }
100 27
            $container->setParameter('fos_http_cache.cache_manager.generate_url_type', $generateUrlType);
101 27
            $loader->load('cache_manager.xml');
102 27
            if (class_exists(Application::class)) {
103 27
                $loader->load('cache_manager_commands.xml');
104
            }
105
        }
106
107 35
        if ($config['tags']['enabled']) {
108 27
            $this->loadCacheTagging(
109 27
                $container,
110
                $loader,
111 27
                $config['tags'],
112 27
                array_key_exists('proxy_client', $config)
113 26
                    ? $this->getDefaultProxyClient($config['proxy_client'])
114 27
                    : 'custom'
115
            );
116
        } else {
117 8
            $container->setParameter('fos_http_cache.compiler_pass.tag_annotations', false);
118
        }
119
120 34
        if ($config['invalidation']['enabled']) {
121 26
            $loader->load('invalidation_listener.xml');
122
123 26
            if (!empty($config['invalidation']['expression_language'])) {
124
                $container->setAlias(
125
                    'fos_http_cache.invalidation.expression_language',
126
                    $config['invalidation']['expression_language']
127
                );
128
            }
129
130 26
            if (!empty($config['invalidation']['rules'])) {
131 3
                $this->loadInvalidatorRules($container, $config['invalidation']['rules']);
132
            }
133
        }
134
135 34
        if ($config['user_context']['enabled']) {
136 6
            $this->loadUserContext($container, $loader, $config['user_context']);
137
        }
138
139 34
        if (!empty($config['flash_message']) && $config['flash_message']['enabled']) {
140 3
            unset($config['flash_message']['enabled']);
141 3
            $container->setParameter('fos_http_cache.event_listener.flash_message.options', $config['flash_message']);
142
143 3
            $loader->load('flash_message.xml');
144
        }
145 34
    }
146
147 37
    private function loadCacheable(ContainerBuilder $container, array $config)
148
    {
149 37
        $definition = $container->getDefinition('fos_http_cache.response_matcher.cacheable');
150
151
        // Change CacheableResponseMatcher to ExpressionResponseMatcher
152 37
        if ($config['response']['expression']) {
153
            $definition->setClass(ExpressionResponseMatcher::class)
154
                ->setArguments([$config['response']['expression']]);
155
        } else {
156 37
            $container->setParameter(
157 37
                'fos_http_cache.cacheable.response.additional_status',
158 37
                $config['response']['additional_status']
159
            );
160
        }
161 37
    }
162
163
    /**
164
     * @param ContainerBuilder $container
165
     * @param array            $config
166
     *
167
     * @throws InvalidConfigurationException
168
     */
169 7
    private function loadCacheControl(ContainerBuilder $container, array $config)
170
    {
171 7
        $controlDefinition = $container->getDefinition('fos_http_cache.event_listener.cache_control');
172
173 7
        foreach ($config['rules'] as $rule) {
174 7
            $ruleMatcher = $this->parseRuleMatcher($container, $rule['match']);
175
176 7
            if ('default' === $rule['headers']['overwrite']) {
177 7
                $rule['headers']['overwrite'] = $config['defaults']['overwrite'];
178
            }
179
180 7
            $controlDefinition->addMethodCall('addRule', [$ruleMatcher, $rule['headers']]);
181
        }
182 6
    }
183
184
    /**
185
     * Parse one cache control rule match configuration.
186
     *
187
     * @param ContainerBuilder $container
188
     * @param array            $match     Request and response match criteria
189
     *
190
     * @return Reference pointing to a rule matcher service
191
     */
192 7
    private function parseRuleMatcher(ContainerBuilder $container, array $match)
193
    {
194 7
        $requestMatcher = $this->parseRequestMatcher($container, $match);
195 7
        $responseMatcher = $this->parseResponseMatcher($container, $match);
196
197 7
        $signature = serialize([(string) $requestMatcher, (string) $responseMatcher]);
198 7
        $id = 'fos_http_cache.cache_control.rule_matcher.'.md5($signature);
199
200 7
        if ($container->hasDefinition($id)) {
201 1
            throw new InvalidConfigurationException('Duplicate match criteria. Would be hidden by a previous rule. match: '.json_encode($match));
202
        }
203
204
        $container
205 7
            ->setDefinition($id, $this->createChildDefinition('fos_http_cache.rule_matcher'))
206 7
            ->replaceArgument(0, $requestMatcher)
207 7
            ->replaceArgument(1, $responseMatcher)
208
        ;
209
210 7
        return new Reference($id);
211
    }
212
213
    /**
214
     * Used for cache control, tag and invalidation rules.
215
     *
216
     * @param ContainerBuilder $container
217
     * @param array            $match
218
     *
219
     * @return Reference to the request matcher
220
     */
221 9
    private function parseRequestMatcher(ContainerBuilder $container, array $match)
222
    {
223 9
        $match['ips'] = (empty($match['ips'])) ? null : $match['ips'];
224
225
        $arguments = [
226 9
            $match['path'],
227 9
            $match['host'],
228 9
            $match['methods'],
229 9
            $match['ips'],
230 9
            $match['attributes'],
231
        ];
232 9
        $serialized = serialize($arguments);
233 9
        $id = 'fos_http_cache.request_matcher.'.md5($serialized).sha1($serialized);
234
235 9
        if (!$container->hasDefinition($id)) {
236
            $container
237 9
                ->setDefinition($id, $this->createChildDefinition('fos_http_cache.request_matcher'))
238 9
                ->setArguments($arguments)
239
            ;
240
241 9
            if (!empty($match['query_string'])) {
242
                $container->getDefinition($id)->addMethodCall('setQueryString', [$match['query_string']]);
243
            }
244
        }
245
246 9
        return new Reference($id);
247
    }
248
249
    /**
250
     * Used only for cache control rules.
251
     *
252
     * @param ContainerBuilder $container
253
     * @param array            $config
254
     *
255
     * @return Reference to the correct response matcher service
256
     */
257 7
    private function parseResponseMatcher(ContainerBuilder $container, array $config)
258
    {
259 7
        if (!empty($config['additional_response_status'])) {
260 1
            $id = 'fos_http_cache.cache_control.expression.'.md5(serialize($config['additional_response_status']));
261 1
            if (!$container->hasDefinition($id)) {
262
                $container
263 1
                    ->setDefinition($id, $this->createChildDefinition('fos_http_cache.response_matcher.cache_control.cacheable_response'))
264 1
                    ->setArguments([$config['additional_response_status']])
265
                ;
266
            }
267 6
        } elseif (!empty($config['match_response'])) {
268 2
            $id = 'fos_http_cache.cache_control.match_response.'.md5($config['match_response']);
269 2
            if (!$container->hasDefinition($id)) {
270
                $container
271 2
                    ->setDefinition($id, $this->createChildDefinition('fos_http_cache.response_matcher.cache_control.expression'))
272 2
                    ->replaceArgument(0, $config['match_response'])
273
                ;
274
            }
275
        } else {
276 4
            $id = 'fos_http_cache.response_matcher.cacheable';
277
        }
278
279 7
        return new Reference($id);
280
    }
281
282 6
    private function loadUserContext(ContainerBuilder $container, XmlFileLoader $loader, array $config)
283
    {
284 6
        $configuredUserIdentifierHeaders = array_map('strtolower', $config['user_identifier_headers']);
285 6
        $completeUserIdentifierHeaders = $configuredUserIdentifierHeaders;
286 6
        if (false !== $config['session_name_prefix'] && !in_array('cookie', $completeUserIdentifierHeaders)) {
287
            $completeUserIdentifierHeaders[] = 'cookie';
288
        }
289
290 6
        $loader->load('user_context.xml');
291
292 6
        $container->getDefinition('fos_http_cache.user_context.request_matcher')
293 6
            ->replaceArgument(0, $config['match']['accept'])
294 6
            ->replaceArgument(1, $config['match']['method']);
295
296 6
        $container->setParameter('fos_http_cache.event_listener.user_context.options', [
297 6
            'user_identifier_headers' => $completeUserIdentifierHeaders,
298 6
            'user_hash_header' => $config['user_hash_header'],
299 6
            'ttl' => $config['hash_cache_ttl'],
300 6
            'add_vary_on_hash' => $config['always_vary_on_context_hash'],
301
        ]);
302
303 6
        $container->getDefinition('fos_http_cache.event_listener.user_context')
304 6
            ->replaceArgument(0, new Reference($config['match']['matcher_service']))
305
        ;
306
307
        $options = [
308 6
            'user_identifier_headers' => $configuredUserIdentifierHeaders,
309 6
            'session_name_prefix' => $config['session_name_prefix'],
310
        ];
311 6
        $container->getDefinition('fos_http_cache.user_context.anonymous_request_matcher')
312 6
            ->replaceArgument(0, $options);
313
314 6
        if ($config['logout_handler']['enabled']) {
315 5
            $container->setAlias('security.logout.handler.session', 'fos_http_cache.user_context.session_logout_handler');
316
        } else {
317 1
            $container->removeDefinition('fos_http_cache.user_context.logout_handler');
318 1
            $container->removeDefinition('fos_http_cache.user_context.session_logout_handler');
319 1
            $container->removeDefinition('fos_http_cache.user_context_invalidator');
320
        }
321
322 6
        if ($config['role_provider']) {
323 4
            $container->getDefinition('fos_http_cache.user_context.role_provider')
324 4
                ->addTag(HashGeneratorPass::TAG_NAME)
325 4
                ->setAbstract(false);
326
        }
327
328
        // Only decorate default SessionListener for Symfony 3.4 - 4.0
329
        // For Symfony 4.1+, the UserContextListener sets the header that tells
330
        // the SessionListener to leave the cache-control header unchanged.
331 6
        if (version_compare(Kernel::VERSION, '3.4', '>=')
332 6
            && version_compare(Kernel::VERSION, '4.1', '<')
333
        ) {
334
            $container->getDefinition('fos_http_cache.user_context.session_listener')
335
                ->setArgument(1, strtolower($config['user_hash_header']))
336
                ->setArgument(2, $completeUserIdentifierHeaders);
337
        } else {
338 6
            $container->removeDefinition('fos_http_cache.user_context.session_listener');
339
        }
340 6
    }
341
342 27
    private function loadProxyClient(ContainerBuilder $container, XmlFileLoader $loader, array $config)
343
    {
344 27
        if (isset($config['varnish'])) {
345 22
            $this->loadVarnish($container, $loader, $config['varnish']);
346
        }
347 26
        if (isset($config['nginx'])) {
348 2
            $this->loadNginx($container, $loader, $config['nginx']);
349
        }
350 26
        if (isset($config['symfony'])) {
351 2
            $this->loadSymfony($container, $loader, $config['symfony']);
352
        }
353 26
        if (isset($config['noop'])) {
354 1
            $loader->load('noop.xml');
355
        }
356
357 26
        $container->setAlias(
358 26
            'fos_http_cache.default_proxy_client',
359 26
            'fos_http_cache.proxy_client.'.$this->getDefaultProxyClient($config)
360
        );
361 26
        $container->setAlias(
362 26
            ProxyClient::class,
363 26
            'fos_http_cache.default_proxy_client'
364
        );
365 26
    }
366
367
    /**
368
     * Define the http dispatcher service for the proxy client $name.
369
     *
370
     * @param ContainerBuilder $container
371
     * @param array            $config
372
     * @param string           $serviceName
373
     */
374 25
    private function createHttpDispatcherDefinition(ContainerBuilder $container, array $config, $serviceName)
375
    {
376 25
        foreach ($config['servers'] as $url) {
377 25
            $usedEnvs = [];
378 25
            $container->resolveEnvPlaceholders($url, null, $usedEnvs);
379 25
            if (0 === \count($usedEnvs)) {
380 25
                $this->validateUrl($url, 'Not a valid Varnish server address: "%s"');
381
            }
382
        }
383 25
        if (!empty($config['base_url'])) {
384 25
            $baseUrl = $config['base_url'];
385 25
            $usedEnvs = [];
386 25
            $container->resolveEnvPlaceholders($baseUrl, null, $usedEnvs);
387 25
            if (0 === \count($usedEnvs)) {
388 25
                $baseUrl = $this->prefixSchema($baseUrl);
389 25
                $this->validateUrl($baseUrl, 'Not a valid base path: "%s"');
390
            }
391
        } else {
392
            $baseUrl = null;
393
        }
394 24
        $httpClient = null;
395 24
        if ($config['http_client']) {
396 1
            $httpClient = new Reference($config['http_client']);
397
        }
398
399 24
        $definition = new Definition(HttpDispatcher::class, [
400 24
            $config['servers'],
401 24
            $baseUrl,
402 24
            $httpClient,
403
        ]);
404
405 24
        $container->setDefinition($serviceName, $definition);
406 24
    }
407
408 22
    private function loadVarnish(ContainerBuilder $container, XmlFileLoader $loader, array $config)
409
    {
410 22
        $this->createHttpDispatcherDefinition($container, $config['http'], 'fos_http_cache.proxy_client.varnish.http_dispatcher');
411
        $options = [
412 21
            'tag_mode' => $config['tag_mode'],
413 21
            'tags_header' => $config['tags_header'],
414
        ];
415
416 21
        if (!empty($config['header_length'])) {
417
            $options['header_length'] = $config['header_length'];
418
        }
419 21
        if (!empty($config['default_ban_headers'])) {
420
            $options['default_ban_headers'] = $config['default_ban_headers'];
421
        }
422 21
        $container->setParameter('fos_http_cache.proxy_client.varnish.options', $options);
423
424 21
        $loader->load('varnish.xml');
425 21
    }
426
427 2
    private function loadNginx(ContainerBuilder $container, XmlFileLoader $loader, array $config)
428
    {
429 2
        $this->createHttpDispatcherDefinition($container, $config['http'], 'fos_http_cache.proxy_client.nginx.http_dispatcher');
430 2
        $container->setParameter('fos_http_cache.proxy_client.nginx.options', [
431 2
            'purge_location' => $config['purge_location'],
432
        ]);
433 2
        $loader->load('nginx.xml');
434 2
    }
435
436 2
    private function loadSymfony(ContainerBuilder $container, XmlFileLoader $loader, array $config)
437
    {
438 2
        $serviceName = 'fos_http_cache.proxy_client.symfony.http_dispatcher';
439
440 2
        if ($config['use_kernel_dispatcher']) {
441 1
            $definition = new Definition(KernelDispatcher::class, [
442 1
                new Reference('kernel'),
443
            ]);
444 1
            $container->setDefinition($serviceName, $definition);
445
        } else {
446 1
            $this->createHttpDispatcherDefinition($container, $config['http'], $serviceName);
447
        }
448
449
        $options = [
450 2
            'tags_header' => $config['tags_header'],
451 2
            'tags_method' => $config['tags_method'],
452 2
            'purge_method' => $config['purge_method'],
453
        ];
454 2
        if (!empty($config['header_length'])) {
455
            $options['header_length'] = $config['header_length'];
456
        }
457 2
        $container->setParameter('fos_http_cache.proxy_client.symfony.options', $options);
458
459 2
        $loader->load('symfony.xml');
460 2
    }
461
462
    /**
463
     * @param ContainerBuilder $container
464
     * @param XmlFileLoader    $loader
465
     * @param array            $config    Configuration section for the tags node
466
     * @param string           $client    Name of the client used with the cache manager,
467
     *                                    "custom" when a custom client is used
468
     */
469 27
    private function loadCacheTagging(ContainerBuilder $container, XmlFileLoader $loader, array $config, $client)
470
    {
471 27
        if ('auto' === $config['enabled'] && !in_array($client, ['varnish', 'symfony'])) {
472 3
            $container->setParameter('fos_http_cache.compiler_pass.tag_annotations', false);
473
474 3
            return;
475
        }
476 24
        if (!in_array($client, ['varnish', 'symfony', 'custom', 'noop'])) {
477 1
            throw new InvalidConfigurationException(sprintf('You can not enable cache tagging with the %s client', $client));
478
        }
479
480 23
        $container->setParameter('fos_http_cache.compiler_pass.tag_annotations', $config['annotations']['enabled']);
481 23
        $container->setParameter('fos_http_cache.tag_handler.response_header', $config['response_header']);
482 23
        $container->setParameter('fos_http_cache.tag_handler.separator', $config['separator']);
483 23
        $container->setParameter('fos_http_cache.tag_handler.strict', $config['strict']);
484
485 23
        $loader->load('cache_tagging.xml');
486 23
        if (class_exists(Application::class)) {
487 23
            $loader->load('cache_tagging_commands.xml');
488
        }
489
490 23
        if (!empty($config['expression_language'])) {
491
            $container->setAlias(
492
                'fos_http_cache.tag_handler.expression_language',
493
                $config['expression_language']
494
            );
495
        }
496
497 23
        if (!empty($config['rules'])) {
498 3
            $this->loadTagRules($container, $config['rules']);
499
        }
500
501 23
        if (null !== $config['max_header_value_length']) {
502 1
            $container->register('fos_http_cache.tag_handler.max_header_value_length_header_formatter', MaxHeaderValueLengthFormatter::class)
503 1
                ->setDecoratedService('fos_http_cache.tag_handler.header_formatter')
504 1
                ->addArgument(new Reference('fos_http_cache.tag_handler.max_header_value_length_header_formatter.inner'))
505 1
                ->addArgument((int) $config['max_header_value_length']);
506
        }
507 23
    }
508
509 2
    private function loadTest(ContainerBuilder $container, XmlFileLoader $loader, array $config)
510
    {
511 2
        $container->setParameter('fos_http_cache.test.cache_header', $config['cache_header']);
512
513 2
        if ($config['proxy_server']) {
514 2
            $this->loadProxyServer($container, $loader, $config['proxy_server']);
515
        }
516 2
    }
517
518 2
    private function loadProxyServer(ContainerBuilder $container, XmlFileLoader $loader, array $config)
519
    {
520 2
        if (isset($config['varnish'])) {
521 2
            $this->loadVarnishProxyServer($container, $loader, $config['varnish']);
522
        }
523
524 2
        if (isset($config['nginx'])) {
525
            $this->loadNginxProxyServer($container, $loader, $config['nginx']);
526
        }
527
528 2
        $container->setAlias(
529 2
            'fos_http_cache.test.default_proxy_server',
530 2
            'fos_http_cache.test.proxy_server.'.$this->getDefaultProxyClient($config)
531
        );
532 2
    }
533
534 2
    private function loadVarnishProxyServer(ContainerBuilder $container, XmlFileLoader $loader, $config)
535
    {
536 2
        $loader->load('varnish_proxy.xml');
537 2
        foreach ($config as $key => $value) {
538 2
            $container->setParameter(
539 2
                'fos_http_cache.test.proxy_server.varnish.'.$key,
540
                $value
541
            );
542
        }
543 2
    }
544
545
    private function loadNginxProxyServer(ContainerBuilder $container, XmlFileLoader $loader, $config)
546
    {
547
        $loader->load('nginx_proxy.xml');
548
        foreach ($config as $key => $value) {
549
            $container->setParameter(
550
                'fos_http_cache.test.proxy_server.nginx.'.$key,
551
                $value
552
            );
553
        }
554
    }
555
556 3
    private function loadTagRules(ContainerBuilder $container, array $config)
557
    {
558 3
        $tagDefinition = $container->getDefinition('fos_http_cache.event_listener.tag');
559
560 3
        foreach ($config as $rule) {
561 3
            $ruleMatcher = $this->parseRequestMatcher($container, $rule['match']);
562
563
            $tags = [
564 3
                'tags' => $rule['tags'],
565 3
                'expressions' => $rule['tag_expressions'],
566
            ];
567
568 3
            $tagDefinition->addMethodCall('addRule', [$ruleMatcher, $tags]);
569
        }
570 3
    }
571
572 3
    private function loadInvalidatorRules(ContainerBuilder $container, array $config)
573
    {
574 3
        $tagDefinition = $container->getDefinition('fos_http_cache.event_listener.invalidation');
575
576 3
        foreach ($config as $rule) {
577 3
            $ruleMatcher = $this->parseRequestMatcher($container, $rule['match']);
578 3
            $tagDefinition->addMethodCall('addRule', [$ruleMatcher, $rule['routes']]);
579
        }
580 3
    }
581
582 25
    private function validateUrl($url, $msg)
583
    {
584 25
        $prefixed = $this->prefixSchema($url);
585
586 25
        if (!$parts = parse_url($prefixed)) {
587 1
            throw new InvalidConfigurationException(sprintf($msg, $url));
588
        }
589 25
    }
590
591 25
    private function prefixSchema($url)
592
    {
593 25
        if (false === strpos($url, '://')) {
594 25
            $url = sprintf('%s://%s', 'http', $url);
595
        }
596
597 25
        return $url;
598
    }
599
600 26
    private function getDefaultProxyClient(array $config)
601
    {
602 26
        if (isset($config['default'])) {
603
            return $config['default'];
604
        }
605
606 26
        if (isset($config['varnish'])) {
607 21
            return 'varnish';
608
        }
609
610 5
        if (isset($config['nginx'])) {
611 2
            return 'nginx';
612
        }
613
614 3
        if (isset($config['symfony'])) {
615 2
            return 'symfony';
616
        }
617
618 1
        if (isset($config['noop'])) {
619 1
            return 'noop';
620
        }
621
622
        throw new InvalidConfigurationException('No proxy client configured');
623
    }
624
625
    /**
626
     * Build the child definition with fallback for Symfony versions < 3.3.
627
     *
628
     * @param string $id Id of the service to extend
629
     *
630
     * @return ChildDefinition|DefinitionDecorator
631
     */
632 9
    private function createChildDefinition($id)
633
    {
634 9
        if (class_exists(ChildDefinition::class)) {
635 9
            return new ChildDefinition($id);
636
        }
637
638
        return new DefinitionDecorator($id);
639
    }
640
}
641