Completed
Push — master ( af674c...ca6270 )
by David
22:00
created

FOSHttpCacheExtension::loadVarnishProxyServer()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 10
ccs 8
cts 8
cp 1
rs 9.4286
cc 2
eloc 6
nc 2
nop 3
crap 2
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\HttpCacheBundle\DependencyInjection\Compiler\HashGeneratorPass;
15
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
16
use Symfony\Component\DependencyInjection\ContainerBuilder;
17
use Symfony\Component\Config\FileLocator;
18
use Symfony\Component\DependencyInjection\DefinitionDecorator;
19
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
20
use Symfony\Component\DependencyInjection\Reference;
21
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
22
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
23
24
/**
25
 * {@inheritdoc}
26
 */
27
class FOSHttpCacheExtension extends Extension
28
{
29
    /**
30
     * {@inheritDoc}
31
     */
32 21
    public function getConfiguration(array $config, ContainerBuilder $container)
33
    {
34 21
        return new Configuration($container->getParameter('kernel.debug'));
35
    }
36
37
    /**
38
     * {@inheritDoc}
39
     */
40 21
    public function load(array $configs, ContainerBuilder $container)
41
    {
42 21
        $configuration = $this->getConfiguration($configs, $container);
43 21
        $config = $this->processConfiguration($configuration, $configs);
44
45 21
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
46 21
        $loader->load('matcher.xml');
47
48 21
        if ($config['debug']['enabled'] || (!empty($config['cache_control']))) {
49 3
            $debugHeader = $config['debug']['enabled'] ? $config['debug']['header'] : false;
50 3
            $container->setParameter($this->getAlias().'.debug_header', $debugHeader);
51 3
            $loader->load('cache_control_listener.xml');
52 3
        }
53
54 21
        if (!empty($config['cache_control'])) {
55 3
            $this->loadCacheControl($container, $config['cache_control']);
56 3
        }
57
58 21
        if (isset($config['proxy_client'])) {
59 16
            $this->loadProxyClient($container, $loader, $config['proxy_client']);
60 15
        }
61
62 20
        if (isset($config['test'])) {
63 1
            $this->loadTest($container, $loader, $config['test']);
64 1
        }
65
66 20
        if ($config['cache_manager']['enabled']) {
67 15
            if (!empty($config['cache_manager']['custom_proxy_client'])) {
68
                // overwrite the previously set alias, if a proxy client was also configured
69
                $container->setAlias(
70
                    $this->getAlias().'.default_proxy_client',
71
                    $config['cache_manager']['custom_proxy_client']
72
                );
73
            }
74 15
            if ('auto' === $config['cache_manager']['generate_url_type']) {
75 15
                $defaultClient = $this->getDefaultProxyClient($config['proxy_client']);
76 15
                $generateUrlType = empty($config['cache_manager']['custom_proxy_client']) && isset($config['proxy_client'][$defaultClient]['base_url'])
77 15
                    ? UrlGeneratorInterface::ABSOLUTE_PATH
78 15
                    : UrlGeneratorInterface::ABSOLUTE_URL
79 15
                ;
80 15
            } else {
81
                $generateUrlType = $config['cache_manager']['generate_url_type'];
82
            }
83 15
            $container->setParameter($this->getAlias().'.cache_manager.generate_url_type', $generateUrlType);
84 15
            $loader->load('cache_manager.xml');
85 15
        }
86
87 20
        if ($config['tags']['enabled']) {
88 15
            $this->loadCacheTagging(
89 15
                $container,
90 15
                $loader,
91 15
                $config['tags'],
92 15
                $this->getDefaultProxyClient($config['proxy_client'])
93 15
            );
94 14
        } else {
95 5
            $container->setParameter($this->getAlias().'.compiler_pass.tag_annotations', false);
96
        }
97
98 19
        if ($config['invalidation']['enabled']) {
99 14
            $loader->load('invalidation_listener.xml');
100
101 14
            if (!empty($config['invalidation']['expression_language'])) {
102
                $container->setAlias(
103 1
                    $this->getAlias().'.invalidation.expression_language',
104
                    $config['invalidation']['expression_language']
105
                );
106
            }
107
108 14
            if (!empty($config['invalidation']['rules'])) {
109 2
                $this->loadInvalidatorRules($container, $config['invalidation']['rules']);
110 2
            }
111 14
        }
112
113 19
        if ($config['user_context']['enabled']) {
114 4
            $this->loadUserContext($container, $loader, $config['user_context']);
115 4
        }
116
117 19
        if (!empty($config['flash_message']) && $config['flash_message']['enabled']) {
118 1
            $container->setParameter($this->getAlias().'.event_listener.flash_message.options', $config['flash_message']);
119
120 1
            $loader->load('flash_message.xml');
121 1
        }
122 19
    }
123
124
    /**
125
     * @param ContainerBuilder $container
126
     * @param array            $config
127
     *
128
     * @throws InvalidConfigurationException
129
     */
130 3
    private function loadCacheControl(ContainerBuilder $container, array $config)
131
    {
132 3
        $controlDefinition = $container->getDefinition($this->getAlias().'.event_listener.cache_control');
133
134 3
        foreach ($config['rules'] as $rule) {
135 3
            $ruleMatcher = $this->parseRuleMatcher($container, $rule['match']);
136
137 3
            if ('default' === $rule['headers']['overwrite']) {
138 3
                $rule['headers']['overwrite'] = $config['defaults']['overwrite'];
139 3
            }
140
141 3
            $controlDefinition->addMethodCall('addRule', array($ruleMatcher, $rule['headers']));
142 3
        }
143 3
    }
144
145 5
    private function parseRuleMatcher(ContainerBuilder $container, array $match)
146
    {
147 5
        $match['ips'] = (empty($match['ips'])) ? null : $match['ips'];
148
149 5
        $requestMatcher = $this->createRequestMatcher(
150 5
            $container,
151 5
            $match['path'],
152 5
            $match['host'],
153 5
            $match['methods'],
154 5
            $match['ips'],
155 5
            $match['attributes']
156 5
        );
157
158 5
        $extraCriteria = array();
159 5
        foreach (array('additional_cacheable_status', 'match_response') as $extra) {
160 5
            if (isset($match[$extra])) {
161 5
                $extraCriteria[$extra] = $match[$extra];
162 5
            }
163 5
        }
164
165 5
        return $this->createRuleMatcher(
166 5
            $container,
167 5
            $requestMatcher,
168
            $extraCriteria
169 5
        );
170
    }
171
172 5
    private function createRuleMatcher(ContainerBuilder $container, Reference $requestMatcher, array $extraCriteria)
173
    {
174 5
        $arguments = array((string) $requestMatcher, $extraCriteria);
175 5
        $serialized = serialize($arguments);
176 5
        $id = $this->getAlias().'.rule_matcher.'.md5($serialized).sha1($serialized);
177
178 5 View Code Duplication
        if (!$container->hasDefinition($id)) {
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...
179
            $container
180 5
                ->setDefinition($id, new DefinitionDecorator($this->getAlias().'.rule_matcher'))
181 5
                ->replaceArgument(0, $requestMatcher)
182 5
                ->replaceArgument(1, $extraCriteria)
183
            ;
184 5
        }
185
186 5
        return new Reference($id);
187
    }
188
189 4
    private function loadUserContext(ContainerBuilder $container, XmlFileLoader $loader, array $config)
190
    {
191 4
        $loader->load('user_context.xml');
192
193 4
        $container->getDefinition($this->getAlias().'.user_context.request_matcher')
194 4
            ->replaceArgument(0, $config['match']['accept'])
195 4
            ->replaceArgument(1, $config['match']['method']);
196
197 4
        $container->getDefinition($this->getAlias().'.event_listener.user_context')
198 4
            ->replaceArgument(0, new Reference($config['match']['matcher_service']))
199 4
            ->replaceArgument(2, $config['user_identifier_headers'])
200 4
            ->replaceArgument(3, $config['user_hash_header'])
201 4
            ->replaceArgument(4, $config['hash_cache_ttl']);
202
203 4
        if ($config['logout_handler']['enabled']) {
204 4
            $container->getDefinition($this->getAlias().'.user_context.logout_handler')
205 4
                ->replaceArgument(1, $config['user_identifier_headers'])
206 4
                ->replaceArgument(2, $config['match']['accept']);
207 4
        } else {
208
            $container->removeDefinition($this->getAlias().'.user_context.logout_handler');
209
        }
210
211 4
        if ($config['role_provider']) {
212 2
            $container->getDefinition($this->getAlias().'.user_context.role_provider')
213 2
                ->addTag(HashGeneratorPass::TAG_NAME)
214 2
                ->setAbstract(false);
215 2
        }
216 4
    }
217
218 5
    private function createRequestMatcher(ContainerBuilder $container, $path = null, $host = null, $methods = null, $ips = null, array $attributes = array())
219
    {
220 5
        $arguments = array($path, $host, $methods, $ips, $attributes);
221 5
        $serialized = serialize($arguments);
222 5
        $id = $this->getAlias().'.request_matcher.'.md5($serialized).sha1($serialized);
223
224 5 View Code Duplication
        if (!$container->hasDefinition($id)) {
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...
225
            $container
226 5
                ->setDefinition($id, new DefinitionDecorator($this->getAlias().'.request_matcher'))
227 5
                ->setArguments($arguments)
228
            ;
229 5
        }
230
231 5
        return new Reference($id);
232
    }
233
234 16
    private function loadProxyClient(ContainerBuilder $container, XmlFileLoader $loader, array $config)
235
    {
236 16
        if (isset($config['varnish'])) {
237 13
            $this->loadVarnish($container, $loader, $config['varnish']);
238 12
        }
239 15
        if (isset($config['nginx'])) {
240 2
            $this->loadNginx($container, $loader, $config['nginx']);
241 2
        }
242 15
        if (isset($config['symfony'])) {
243 1
            $this->loadSymfony($container, $loader, $config['symfony']);
244 1
        }
245
246 15
        $container->setAlias(
247 15
            $this->getAlias().'.default_proxy_client',
248 15
            $this->getAlias().'.proxy_client.'.$this->getDefaultProxyClient($config)
249 15
        );
250 15
    }
251
252 13 View Code Duplication
    private function loadVarnish(ContainerBuilder $container, XmlFileLoader $loader, array $config)
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...
253
    {
254 13
        $loader->load('varnish.xml');
255 13
        foreach ($config['servers'] as $url) {
256 13
            $this->validateUrl($url, 'Not a valid Varnish server address: "%s"');
257 13
        }
258 13
        if (!empty($config['base_url'])) {
259 13
            $baseUrl = $this->prefixSchema($config['base_url'], 'Not a valid base path: "%s"');
0 ignored issues
show
Unused Code introduced by
The call to FOSHttpCacheExtension::prefixSchema() has too many arguments starting with 'Not a valid base path: "%s"'.

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...
260 13
            $this->validateUrl($baseUrl, 'Not a valid base path: "%s"');
261 12
        } else {
262
            $baseUrl = null;
263
        }
264 12
        $container->setParameter($this->getAlias().'.proxy_client.varnish.servers', $config['servers']);
265 12
        $container->setParameter($this->getAlias().'.proxy_client.varnish.base_url', $baseUrl);
266
267 12
        if (!empty($config['guzzle_client'])) {
268 1
            $container->setAlias(
269 1
                $this->getAlias().'.proxy_client.varnish.guzzle_client',
270 1
                $config['guzzle_client']
271 1
            );
272 1
        }
273 12
    }
274
275 2 View Code Duplication
    private function loadNginx(ContainerBuilder $container, XmlFileLoader $loader, array $config)
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...
276
    {
277 2
        $loader->load('nginx.xml');
278 2
        foreach ($config['servers'] as $url) {
279 2
            $this->validateUrl($url, 'Not a valid Nginx server address: "%s"');
280 2
        }
281 2
        if (!empty($config['base_url'])) {
282 2
            $baseUrl = $this->prefixSchema($config['base_url'], 'Not a valid base path: "%s"');
0 ignored issues
show
Unused Code introduced by
The call to FOSHttpCacheExtension::prefixSchema() has too many arguments starting with 'Not a valid base path: "%s"'.

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...
283 2
        } else {
284
            $baseUrl = null;
285
        }
286 2
        $container->setParameter($this->getAlias().'.proxy_client.nginx.servers', $config['servers']);
287 2
        $container->setParameter($this->getAlias().'.proxy_client.nginx.base_url', $baseUrl);
288 2
        $container->setParameter($this->getAlias().'.proxy_client.nginx.purge_location', $config['purge_location']);
289
290 2
        if (!empty($config['guzzle_client'])) {
291
            $container->setAlias(
292
                $this->getAlias().'.proxy_client.nginx.guzzle_client',
293
                $config['guzzle_client']
294
            );
295
        }
296 2
    }
297
298 1 View Code Duplication
    private function loadSymfony(ContainerBuilder $container, XmlFileLoader $loader, array $config)
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...
299
    {
300 1
        $loader->load('symfony.xml');
301 1
        foreach ($config['servers'] as $url) {
302 1
            $this->validateUrl($url, 'Not a valid web server address: "%s"');
303 1
        }
304 1
        if (!empty($config['base_url'])) {
305 1
            $baseUrl = $this->prefixSchema($config['base_url'], 'Not a valid base path: "%s"');
0 ignored issues
show
Unused Code introduced by
The call to FOSHttpCacheExtension::prefixSchema() has too many arguments starting with 'Not a valid base path: "%s"'.

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...
306 1
            $this->validateUrl($baseUrl, 'Not a valid base path: "%s"');
307 1
        } else {
308
            $baseUrl = null;
309
        }
310 1
        $container->setParameter($this->getAlias().'.proxy_client.symfony.servers', $config['servers']);
311 1
        $container->setParameter($this->getAlias().'.proxy_client.symfony.base_url', $baseUrl);
312
313 1
        if (!empty($config['guzzle_client'])) {
314
            $container->setAlias(
315
                $this->getAlias().'.proxy_client.symfony.guzzle_client',
316
                $config['guzzle_client']
317
            );
318
        }
319 1
    }
320
321 15
    private function loadCacheTagging(ContainerBuilder $container, XmlFileLoader $loader, array $config, $client)
322
    {
323 15
        if ('auto' === $config['enabled'] && 'varnish' !== $client) {
324 2
            $container->setParameter($this->getAlias().'.compiler_pass.tag_annotations', false);
325
326 2
            return;
327
        }
328 13
        if ('varnish' !== $client) {
329 1
            throw new InvalidConfigurationException(sprintf('You can not enable cache tagging with %s', $client));
330
        }
331
332 12
        $container->setParameter($this->getAlias().'.compiler_pass.tag_annotations', true);
333 12
        $container->setParameter($this->getAlias().'.tag_handler.header', $config['header']);
334 12
        $loader->load('cache_tagging.xml');
335
336 12
        if (!empty($config['expression_language'])) {
337
            $container->setAlias(
338
                $this->getAlias().'.tag_handler.expression_language',
339
                $config['expression_language']
340
            );
341
        }
342
343 12
        if (!empty($config['rules'])) {
344 2
            $this->loadTagRules($container, $config['rules']);
345 2
        }
346
347 12
        $tagsHeader = $config['header'];
348 12
        $container->getDefinition($this->getAlias().'.cache_manager')
349 12
            ->addMethodCall('setTagsHeader', array($tagsHeader))
350
        ;
351 12
    }
352
353 1
    private function loadTest(ContainerBuilder $container, XmlFileLoader $loader, array $config)
354
    {
355 1
        $container->setParameter($this->getAlias().'.test.cache_header', $config['cache_header']);
356
357 1
        if ($config['proxy_server']) {
358 1
            $this->loadProxyServer($container, $loader, $config['proxy_server']);
359 1
        }
360
361 1
        if (isset($config['client']['varnish']['enabled'])
362 1
            || isset($config['client']['nginx']['enabled'])) {
363 1
            $loader->load('test_client.xml');
364
365 1
            if ($config['client']['varnish']['enabled']) {
366 1
                $loader->load('varnish_test_client.xml');
367 1
            }
368
369 1
            if ($config['client']['nginx']['enabled']) {
370
                $loader->load('nginx_test_client.xml');
371
            }
372
373 1
            $container->setAlias(
374 1
                $this->getAlias().'.test.default_client',
375 1
                $this->getAlias().'.test.client.'.$this->getDefaultProxyClient($config['client'])
376 1
            );
377 1
        }
378 1
    }
379
380 1
    private function loadProxyServer(ContainerBuilder $container, XmlFileLoader $loader, array $config)
381
    {
382 1
        if (isset($config['varnish'])) {
383 1
            $this->loadVarnishProxyServer($container, $loader, $config['varnish']);
384 1
        }
385
386 1
        if (isset($config['nginx'])) {
387
            $this->loadNginxProxyServer($container, $loader, $config['varnish']);
388
        }
389
390 1
        $container->setAlias(
391 1
            $this->getAlias().'.test.default_proxy_server',
392 1
            $this->getAlias().'.test.proxy_server.'.$this->getDefaultProxyClient($config)
393 1
        );
394 1
    }
395
396 1
    private function loadVarnishProxyServer(ContainerBuilder $container, XmlFileLoader $loader, $config)
397
    {
398 1
        $loader->load('varnish_proxy.xml');
399 1
        foreach ($config as $key => $value) {
400 1
            $container->setParameter(
401 1
                $this->getAlias().'.test.proxy_server.varnish.'.$key,
402
                $value
403 1
            );
404 1
        }
405 1
    }
406
407
    private function loadNginxProxyServer(ContainerBuilder $container, XmlFileLoader $loader, $config)
408
    {
409
        $loader->load('nginx_proxy.xml');
410
        foreach ($config as $key => $value) {
411
            $container->setParameter(
412
                $this->getAlias().'.test.proxy_server.nginx.'.$key,
413
                $value
414
            );
415
        }
416
    }
417
418 2
    private function loadTagRules(ContainerBuilder $container, array $config)
419
    {
420 2
        $tagDefinition = $container->getDefinition($this->getAlias().'.event_listener.tag');
421
422 2
        foreach ($config as $rule) {
423 2
            $ruleMatcher = $this->parseRuleMatcher($container, $rule['match']);
424
425
            $tags = array(
426 2
                'tags' => $rule['tags'],
427 2
                'expressions' => $rule['tag_expressions'],
428 2
            );
429
430 2
            $tagDefinition->addMethodCall('addRule', array($ruleMatcher, $tags));
431 2
        }
432 2
    }
433
434 2
    private function loadInvalidatorRules(ContainerBuilder $container, array $config)
435
    {
436 2
        $tagDefinition = $container->getDefinition($this->getAlias().'.event_listener.invalidation');
437
438 2
        foreach ($config as $rule) {
439 2
            $ruleMatcher = $this->parseRuleMatcher($container, $rule['match']);
440 2
            $tagDefinition->addMethodCall('addRule', array($ruleMatcher, $rule['routes']));
441 2
        }
442 2
    }
443
444 16
    private function validateUrl($url, $msg)
445
    {
446 16
        $prefixed = $this->prefixSchema($url);
447
448 16
        if (!$parts = parse_url($prefixed)) {
449 1
            throw new InvalidConfigurationException(sprintf($msg, $url));
450
        }
451 16
    }
452
453 16
    private function prefixSchema($url)
454
    {
455 16
        if (false === strpos($url, '://')) {
456 16
            $url = sprintf('%s://%s', 'http', $url);
457 16
        }
458
459 16
        return $url;
460
    }
461
462 15
    private function getDefaultProxyClient(array $config)
463
    {
464 15
        if (isset($config['default'])) {
465
            return $config['default'];
466
        }
467
468 15
        if (isset($config['varnish'])) {
469 12
            return 'varnish';
470
        }
471
472 3
        if (isset($config['nginx'])) {
473 2
            return 'nginx';
474
        }
475
476 1
        if (isset($config['symfony'])) {
477 1
            return 'symfony';
478
        }
479
480
        throw new InvalidConfigurationException('No proxy client configured');
481
    }
482
}
483