Completed
Push — master ( 0de349...246355 )
by David
13s
created

FOSHttpCacheExtension::loadCacheable()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2.0438

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 2
dl 0
loc 15
ccs 7
cts 9
cp 0.7778
crap 2.0438
rs 9.4285
c 0
b 0
f 0
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\HttpCacheBundle\DependencyInjection\Compiler\HashGeneratorPass;
16
use FOS\HttpCacheBundle\Http\ResponseMatcher\ExpressionResponseMatcher;
17
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
18
use Symfony\Component\Config\FileLocator;
19
use Symfony\Component\DependencyInjection\ContainerBuilder;
20
use Symfony\Component\DependencyInjection\Definition;
21
use Symfony\Component\DependencyInjection\DefinitionDecorator;
22
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
23
use Symfony\Component\DependencyInjection\Reference;
24
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
25
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
26
27
/**
28
 * {@inheritdoc}
29
 */
30
class FOSHttpCacheExtension extends Extension
31
{
32
    /**
33
     * {@inheritdoc}
34
     */
35 23
    public function getConfiguration(array $config, ContainerBuilder $container)
36
    {
37 23
        return new Configuration($container->getParameter('kernel.debug'));
38
    }
39
40
    /**
41
     * {@inheritdoc}
42
     */
43 23
    public function load(array $configs, ContainerBuilder $container)
44
    {
45 23
        $configuration = $this->getConfiguration($configs, $container);
46 23
        $config = $this->processConfiguration($configuration, $configs);
0 ignored issues
show
Bug introduced by
It seems like $configuration defined by $this->getConfiguration($configs, $container) on line 45 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...
47
48 23
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
49 23
        $loader->load('matcher.xml');
50
51 23
        if ($config['debug']['enabled'] || (!empty($config['cache_control']))) {
52 3
            $debugHeader = $config['debug']['enabled'] ? $config['debug']['header'] : false;
53 3
            $container->setParameter($this->getAlias().'.debug_header', $debugHeader);
54 3
            $loader->load('cache_control_listener.xml');
55
        }
56
57 23
        $this->loadCacheable($container, $config['cacheable']);
58
59 23
        if (!empty($config['cache_control'])) {
60 3
            $this->loadCacheControl($container, $config['cache_control']);
61
        }
62
63 23
        if (isset($config['proxy_client'])) {
64 17
            $this->loadProxyClient($container, $loader, $config['proxy_client']);
65
        }
66
67 22
        if (isset($config['test'])) {
68 1
            $this->loadTest($container, $loader, $config['test']);
69
        }
70
71 22
        if ($config['cache_manager']['enabled']) {
72 17
            if (array_key_exists('custom_proxy_client', $config['cache_manager'])) {
73
                // overwrite the previously set alias, if a proxy client was also configured
74 1
                $container->setAlias(
75 1
                    $this->getAlias().'.default_proxy_client',
76 1
                    $config['cache_manager']['custom_proxy_client']
77
                );
78
            }
79 17
            if ('auto' === $config['cache_manager']['generate_url_type']) {
80 17
                if (array_key_exists('custom_proxy_client', $config['cache_manager'])) {
81 1
                    $generateUrlType = UrlGeneratorInterface::ABSOLUTE_URL;
82
                } else {
83 16
                    $defaultClient = $this->getDefaultProxyClient($config['proxy_client']);
84 16
                    if ($defaultClient !== 'noop'
85 16
                        && array_key_exists('base_url', $config['proxy_client'][$defaultClient])) {
86
                        $generateUrlType = UrlGeneratorInterface::ABSOLUTE_PATH;
87
                    } else {
88 17
                        $generateUrlType = UrlGeneratorInterface::ABSOLUTE_URL;
89
                    }
90
                }
91
            } else {
92
                $generateUrlType = $config['cache_manager']['generate_url_type'];
93
            }
94 17
            $container->setParameter($this->getAlias().'.cache_manager.generate_url_type', $generateUrlType);
95 17
            $loader->load('cache_manager.xml');
96
        }
97
98 22
        if ($config['tags']['enabled']) {
99 17
            $this->loadCacheTagging(
100
                $container,
101
                $loader,
102 17
                $config['tags'],
103 17
                array_key_exists('proxy_client', $config)
104 16
                    ? $this->getDefaultProxyClient($config['proxy_client'])
105 17
                    : 'custom'
106
            );
107
        } else {
108 5
            $container->setParameter($this->getAlias().'.compiler_pass.tag_annotations', false);
109
        }
110
111 21
        if ($config['invalidation']['enabled']) {
112 16
            $loader->load('invalidation_listener.xml');
113
114 16
            if (!empty($config['invalidation']['expression_language'])) {
115
                $container->setAlias(
116
                    $this->getAlias().'.invalidation.expression_language',
117
                    $config['invalidation']['expression_language']
118
                );
119
            }
120
121 16
            if (!empty($config['invalidation']['rules'])) {
122 2
                $this->loadInvalidatorRules($container, $config['invalidation']['rules']);
123
            }
124
        }
125
126 21
        if ($config['user_context']['enabled']) {
127 4
            $this->loadUserContext($container, $loader, $config['user_context']);
128
        }
129
130 21
        if (!empty($config['flash_message']) && $config['flash_message']['enabled']) {
131 1
            $container->setParameter($this->getAlias().'.event_listener.flash_message.options', $config['flash_message']);
132
133 1
            $loader->load('flash_message.xml');
134
        }
135 21
    }
136
137 23
    private function loadCacheable(ContainerBuilder $container, array $config)
138
    {
139 23
        $definition = $container->getDefinition($this->getAlias().'.response_matcher.cacheable');
140
141
        // Change CacheableResponseMatcher to ExpressionResponseMatcher
142 23
        if ($config['response']['expression']) {
143
            $definition->setClass(ExpressionResponseMatcher::class)
144
                ->setArguments([$config['response']['expression']]);
145
        } else {
146 23
            $container->setParameter(
147 23
                $this->getAlias().'.cacheable.response.additional_status',
148 23
                $config['response']['additional_status']
149
            );
150
        }
151 23
    }
152
153
    /**
154
     * @param ContainerBuilder $container
155
     * @param array            $config
156
     *
157
     * @throws InvalidConfigurationException
158
     */
159 3
    private function loadCacheControl(ContainerBuilder $container, array $config)
160
    {
161 3
        $controlDefinition = $container->getDefinition($this->getAlias().'.event_listener.cache_control');
162
163 3
        foreach ($config['rules'] as $rule) {
164 3
            $ruleMatcher = $this->parseRuleMatcher($container, $rule['match']);
165
166 3
            if ('default' === $rule['headers']['overwrite']) {
167 3
                $rule['headers']['overwrite'] = $config['defaults']['overwrite'];
168
            }
169
170 3
            $controlDefinition->addMethodCall('addRule', [$ruleMatcher, $rule['headers']]);
171
        }
172 3
    }
173
174 5
    private function parseRuleMatcher(ContainerBuilder $container, array $match)
175
    {
176 5
        $match['ips'] = (empty($match['ips'])) ? null : $match['ips'];
177
178 5
        return $this->createRequestMatcher(
179
            $container,
180 5
            $match['path'],
181 5
            $match['host'],
182 5
            $match['methods'],
183 5
            $match['ips'],
184 5
            $match['attributes']
185
        );
186
    }
187
188 4
    private function loadUserContext(ContainerBuilder $container, XmlFileLoader $loader, array $config)
189
    {
190 4
        $loader->load('user_context.xml');
191
192 4
        $container->getDefinition($this->getAlias().'.user_context.request_matcher')
193 4
            ->replaceArgument(0, $config['match']['accept'])
194 4
            ->replaceArgument(1, $config['match']['method']);
195
196 4
        $container->getDefinition($this->getAlias().'.event_listener.user_context')
197 4
            ->replaceArgument(0, new Reference($config['match']['matcher_service']))
198 4
            ->replaceArgument(2, $config['user_identifier_headers'])
199 4
            ->replaceArgument(3, $config['user_hash_header'])
200 4
            ->replaceArgument(4, $config['hash_cache_ttl'])
201 4
            ->replaceArgument(5, $config['always_vary_on_context_hash']);
202
203 4
        $container->getDefinition($this->getAlias().'.user_context.anonymous_request_matcher')
204 4
            ->replaceArgument(0, $config['user_identifier_headers']);
205
206 4
        if ($config['logout_handler']['enabled']) {
207 4
            $container->getDefinition($this->getAlias().'.user_context.logout_handler')
208 4
                ->replaceArgument(1, $config['user_identifier_headers'])
209 4
                ->replaceArgument(2, $config['match']['accept']);
210
        } else {
211
            $container->removeDefinition($this->getAlias().'.user_context.logout_handler');
212
        }
213
214 4
        if ($config['role_provider']) {
215 2
            $container->getDefinition($this->getAlias().'.user_context.role_provider')
216 2
                ->addTag(HashGeneratorPass::TAG_NAME)
217 2
                ->setAbstract(false);
218
        }
219 4
    }
220
221 5
    private function createRequestMatcher(ContainerBuilder $container, $path = null, $host = null, $methods = null, $ips = null, array $attributes = [])
222
    {
223 5
        $arguments = [$path, $host, $methods, $ips, $attributes];
224 5
        $serialized = serialize($arguments);
225 5
        $id = $this->getAlias().'.request_matcher.'.md5($serialized).sha1($serialized);
226
227 5
        if (!$container->hasDefinition($id)) {
228
            $container
229 5
                ->setDefinition($id, new DefinitionDecorator($this->getAlias().'.request_matcher'))
230 5
                ->setArguments($arguments)
231
            ;
232
        }
233
234 5
        return new Reference($id);
235
    }
236
237 17
    private function loadProxyClient(ContainerBuilder $container, XmlFileLoader $loader, array $config)
238
    {
239 17
        if (isset($config['varnish'])) {
240 13
            $this->loadVarnish($container, $loader, $config['varnish']);
241
        }
242 16
        if (isset($config['nginx'])) {
243 2
            $this->loadNginx($container, $loader, $config['nginx']);
244
        }
245 16
        if (isset($config['symfony'])) {
246 1
            $this->loadSymfony($container, $loader, $config['symfony']);
247
        }
248 16
        if (isset($config['noop'])) {
249 1
            $loader->load('noop.xml');
250
        }
251
252 16
        $container->setAlias(
253 16
            $this->getAlias().'.default_proxy_client',
254 16
            $this->getAlias().'.proxy_client.'.$this->getDefaultProxyClient($config)
255
        );
256 16
    }
257
258
    /**
259
     * Define the http dispatcher service for the proxy client $name.
260
     *
261
     * @param ContainerBuilder $container
262
     * @param array            $config
263
     * @param string           $serviceName
264
     */
265 16
    private function createHttpDispatcherDefinition(ContainerBuilder $container, array $config, $serviceName)
266
    {
267 16
        foreach ($config['servers'] as $url) {
268 16
            $this->validateUrl($url, 'Not a valid Varnish server address: "%s"');
269
        }
270 16
        if (!empty($config['base_url'])) {
271 16
            $baseUrl = $this->prefixSchema($config['base_url']);
272 16
            $this->validateUrl($baseUrl, 'Not a valid base path: "%s"');
273
        } else {
274
            $baseUrl = null;
275
        }
276 15
        $httpClient = null;
277 15
        if ($config['http_client']) {
278 1
            $httpClient = new Reference($config['http_client']);
279
        }
280
281 15
        $definition = new Definition(HttpDispatcher::class, [
282 15
            $config['servers'],
283 15
            $baseUrl,
284 15
            $httpClient,
285
        ]);
286
287 15
        $container->setDefinition($serviceName, $definition);
288 15
    }
289
290 13
    private function loadVarnish(ContainerBuilder $container, XmlFileLoader $loader, array $config)
291
    {
292 13
        $this->createHttpDispatcherDefinition($container, $config['http'], $this->getAlias().'.proxy_client.varnish.http_dispatcher');
293 12
        $loader->load('varnish.xml');
294 12
    }
295
296 2
    private function loadNginx(ContainerBuilder $container, XmlFileLoader $loader, array $config)
297
    {
298 2
        $this->createHttpDispatcherDefinition($container, $config['http'], $this->getAlias().'.proxy_client.nginx.http_dispatcher');
299 2
        $container->setParameter($this->getAlias().'.proxy_client.nginx.options', [
300 2
            'purge_location' => $config['purge_location'],
301
        ]);
302 2
        $loader->load('nginx.xml');
303 2
    }
304
305 1
    private function loadSymfony(ContainerBuilder $container, XmlFileLoader $loader, array $config)
306
    {
307 1
        $this->createHttpDispatcherDefinition($container, $config['http'], $this->getAlias().'.proxy_client.symfony.http_dispatcher');
308 1
        $loader->load('symfony.xml');
309 1
    }
310
311
    /**
312
     * @param ContainerBuilder $container
313
     * @param XmlFileLoader    $loader
314
     * @param array            $config    Configuration section for the tags node
315
     * @param string           $client    Name of the client used with the cache manager,
316
     *                                    "custom" when a custom client is used
317
     */
318 17
    private function loadCacheTagging(ContainerBuilder $container, XmlFileLoader $loader, array $config, $client)
319
    {
320 17
        if ('auto' === $config['enabled'] && 'varnish' !== $client) {
321 4
            $container->setParameter($this->getAlias().'.compiler_pass.tag_annotations', false);
322
323 4
            return;
324
        }
325 13
        if (!in_array($client, ['varnish', 'custom'])) {
326 1
            throw new InvalidConfigurationException(sprintf('You can not enable cache tagging with the %s client', $client));
327
        }
328
329 12
        $container->setParameter($this->getAlias().'.compiler_pass.tag_annotations', true);
330 12
        $container->setParameter($this->getAlias().'.tag_handler.header', $config['header']);
331 12
        $container->setParameter($this->getAlias().'.tag_handler.strict', $config['strict']);
332 12
        $loader->load('cache_tagging.xml');
333
334 12
        if (!empty($config['expression_language'])) {
335
            $container->setAlias(
336
                $this->getAlias().'.tag_handler.expression_language',
337
                $config['expression_language']
338
            );
339
        }
340
341 12
        if (!empty($config['rules'])) {
342 2
            $this->loadTagRules($container, $config['rules']);
343
        }
344 12
    }
345
346 1
    private function loadTest(ContainerBuilder $container, XmlFileLoader $loader, array $config)
347
    {
348 1
        $container->setParameter($this->getAlias().'.test.cache_header', $config['cache_header']);
349
350 1
        if ($config['proxy_server']) {
351 1
            $this->loadProxyServer($container, $loader, $config['proxy_server']);
352
        }
353 1
    }
354
355 1
    private function loadProxyServer(ContainerBuilder $container, XmlFileLoader $loader, array $config)
356
    {
357 1
        if (isset($config['varnish'])) {
358 1
            $this->loadVarnishProxyServer($container, $loader, $config['varnish']);
359
        }
360
361 1
        if (isset($config['nginx'])) {
362
            $this->loadNginxProxyServer($container, $loader, $config['varnish']);
363
        }
364
365 1
        $container->setAlias(
366 1
            $this->getAlias().'.test.default_proxy_server',
367 1
            $this->getAlias().'.test.proxy_server.'.$this->getDefaultProxyClient($config)
368
        );
369 1
    }
370
371 1
    private function loadVarnishProxyServer(ContainerBuilder $container, XmlFileLoader $loader, $config)
372
    {
373 1
        $loader->load('varnish_proxy.xml');
374 1
        foreach ($config as $key => $value) {
375 1
            $container->setParameter(
376 1
                $this->getAlias().'.test.proxy_server.varnish.'.$key,
377
                $value
378
            );
379
        }
380 1
    }
381
382
    private function loadNginxProxyServer(ContainerBuilder $container, XmlFileLoader $loader, $config)
383
    {
384
        $loader->load('nginx_proxy.xml');
385
        foreach ($config as $key => $value) {
386
            $container->setParameter(
387
                $this->getAlias().'.test.proxy_server.nginx.'.$key,
388
                $value
389
            );
390
        }
391
    }
392
393 2
    private function loadTagRules(ContainerBuilder $container, array $config)
394
    {
395 2
        $tagDefinition = $container->getDefinition($this->getAlias().'.event_listener.tag');
396
397 2
        foreach ($config as $rule) {
398 2
            $ruleMatcher = $this->parseRuleMatcher($container, $rule['match']);
399
400
            $tags = [
401 2
                'tags' => $rule['tags'],
402 2
                'expressions' => $rule['tag_expressions'],
403
            ];
404
405 2
            $tagDefinition->addMethodCall('addRule', [$ruleMatcher, $tags]);
406
        }
407 2
    }
408
409 2
    private function loadInvalidatorRules(ContainerBuilder $container, array $config)
410
    {
411 2
        $tagDefinition = $container->getDefinition($this->getAlias().'.event_listener.invalidation');
412
413 2
        foreach ($config as $rule) {
414 2
            $ruleMatcher = $this->parseRuleMatcher($container, $rule['match']);
415 2
            $tagDefinition->addMethodCall('addRule', [$ruleMatcher, $rule['routes']]);
416
        }
417 2
    }
418
419 16
    private function validateUrl($url, $msg)
420
    {
421 16
        $prefixed = $this->prefixSchema($url);
422
423 16
        if (!$parts = parse_url($prefixed)) {
424 1
            throw new InvalidConfigurationException(sprintf($msg, $url));
425
        }
426 16
    }
427
428 16
    private function prefixSchema($url)
429
    {
430 16
        if (false === strpos($url, '://')) {
431 16
            $url = sprintf('%s://%s', 'http', $url);
432
        }
433
434 16
        return $url;
435
    }
436
437 16
    private function getDefaultProxyClient(array $config)
438
    {
439 16
        if (isset($config['default'])) {
440
            return $config['default'];
441
        }
442
443 16
        if (isset($config['varnish'])) {
444 12
            return 'varnish';
445
        }
446
447 4
        if (isset($config['nginx'])) {
448 2
            return 'nginx';
449
        }
450
451 2
        if (isset($config['symfony'])) {
452 1
            return 'symfony';
453
        }
454
455 1
        if (isset($config['noop'])) {
456 1
            return 'noop';
457
        }
458
459
        throw new InvalidConfigurationException('No proxy client configured');
460
    }
461
}
462