Completed
Push — master ( a8a5cf...b42ed1 )
by David
14s
created

FOSHttpCacheExtension::loadNginx()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 3
crap 1
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\ChildDefinition;
20
use Symfony\Component\DependencyInjection\ContainerBuilder;
21
use Symfony\Component\DependencyInjection\Definition;
22
use Symfony\Component\DependencyInjection\DefinitionDecorator;
23
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
24
use Symfony\Component\DependencyInjection\Reference;
25
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
26
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
27
28
/**
29
 * {@inheritdoc}
30
 */
31
class FOSHttpCacheExtension extends Extension
32
{
33
    /**
34
     * {@inheritdoc}
35
     */
36 23
    public function getConfiguration(array $config, ContainerBuilder $container)
37
    {
38 23
        return new Configuration($container->getParameter('kernel.debug'));
39
    }
40
41
    /**
42
     * {@inheritdoc}
43
     */
44 23
    public function load(array $configs, ContainerBuilder $container)
45
    {
46 23
        $configuration = $this->getConfiguration($configs, $container);
47 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 46 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...
48
49 23
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
50 23
        $loader->load('matcher.xml');
51
52 23
        if ($config['debug']['enabled'] || (!empty($config['cache_control']))) {
53 3
            $debugHeader = $config['debug']['enabled'] ? $config['debug']['header'] : false;
54 3
            $container->setParameter($this->getAlias().'.debug_header', $debugHeader);
55 3
            $loader->load('cache_control_listener.xml');
56
        }
57
58 23
        $this->loadCacheable($container, $config['cacheable']);
59
60 23
        if (!empty($config['cache_control'])) {
61 3
            $this->loadCacheControl($container, $config['cache_control']);
62
        }
63
64 23
        if (isset($config['proxy_client'])) {
65 17
            $this->loadProxyClient($container, $loader, $config['proxy_client']);
66
        }
67
68 22
        if (isset($config['test'])) {
69 1
            $this->loadTest($container, $loader, $config['test']);
70
        }
71
72 22
        if ($config['cache_manager']['enabled']) {
73 17
            if (array_key_exists('custom_proxy_client', $config['cache_manager'])) {
74
                // overwrite the previously set alias, if a proxy client was also configured
75 1
                $container->setAlias(
76 1
                    $this->getAlias().'.default_proxy_client',
77 1
                    $config['cache_manager']['custom_proxy_client']
78
                );
79
            }
80 17
            if ('auto' === $config['cache_manager']['generate_url_type']) {
81 17
                if (array_key_exists('custom_proxy_client', $config['cache_manager'])) {
82 1
                    $generateUrlType = UrlGeneratorInterface::ABSOLUTE_URL;
83
                } else {
84 16
                    $defaultClient = $this->getDefaultProxyClient($config['proxy_client']);
85 16
                    if ($defaultClient !== 'noop'
86 15
                        && array_key_exists('base_url', $config['proxy_client'][$defaultClient])) {
87
                        $generateUrlType = UrlGeneratorInterface::ABSOLUTE_PATH;
88
                    } else {
89 16
                        $generateUrlType = UrlGeneratorInterface::ABSOLUTE_URL;
90
                    }
91
                }
92
            } else {
93
                $generateUrlType = $config['cache_manager']['generate_url_type'];
94
            }
95 17
            $container->setParameter($this->getAlias().'.cache_manager.generate_url_type', $generateUrlType);
96 17
            $loader->load('cache_manager.xml');
97
        }
98
99 22
        if ($config['tags']['enabled']) {
100 17
            $this->loadCacheTagging(
101 17
                $container,
102 17
                $loader,
103 17
                $config['tags'],
104 17
                array_key_exists('proxy_client', $config)
105 16
                    ? $this->getDefaultProxyClient($config['proxy_client'])
106 17
                    : 'custom'
107
            );
108
        } else {
109 5
            $container->setParameter($this->getAlias().'.compiler_pass.tag_annotations', false);
110
        }
111
112 21
        if ($config['invalidation']['enabled']) {
113 16
            $loader->load('invalidation_listener.xml');
114
115 16
            if (!empty($config['invalidation']['expression_language'])) {
116
                $container->setAlias(
117
                    $this->getAlias().'.invalidation.expression_language',
118
                    $config['invalidation']['expression_language']
119
                );
120
            }
121
122 16
            if (!empty($config['invalidation']['rules'])) {
123 2
                $this->loadInvalidatorRules($container, $config['invalidation']['rules']);
124
            }
125
        }
126
127 21
        if ($config['user_context']['enabled']) {
128 4
            $this->loadUserContext($container, $loader, $config['user_context']);
129
        }
130
131 21
        if (!empty($config['flash_message']) && $config['flash_message']['enabled']) {
132 1
            $container->setParameter($this->getAlias().'.event_listener.flash_message.options', $config['flash_message']);
133
134 1
            $loader->load('flash_message.xml');
135
        }
136 21
    }
137
138 23
    private function loadCacheable(ContainerBuilder $container, array $config)
139
    {
140 23
        $definition = $container->getDefinition($this->getAlias().'.response_matcher.cacheable');
141
142
        // Change CacheableResponseMatcher to ExpressionResponseMatcher
143 23
        if ($config['response']['expression']) {
144
            $definition->setClass(ExpressionResponseMatcher::class)
145
                ->setArguments([$config['response']['expression']]);
146
        } else {
147 23
            $container->setParameter(
148 23
                $this->getAlias().'.cacheable.response.additional_status',
149 23
                $config['response']['additional_status']
150
            );
151
        }
152 23
    }
153
154
    /**
155
     * @param ContainerBuilder $container
156
     * @param array            $config
157
     *
158
     * @throws InvalidConfigurationException
159
     */
160 3
    private function loadCacheControl(ContainerBuilder $container, array $config)
161
    {
162 3
        $controlDefinition = $container->getDefinition($this->getAlias().'.event_listener.cache_control');
163
164 3
        foreach ($config['rules'] as $rule) {
165 3
            $ruleMatcher = $this->parseRuleMatcher($container, $rule['match']);
166
167 3
            if ('default' === $rule['headers']['overwrite']) {
168 3
                $rule['headers']['overwrite'] = $config['defaults']['overwrite'];
169
            }
170
171 3
            $controlDefinition->addMethodCall('addRule', [$ruleMatcher, $rule['headers']]);
172
        }
173 3
    }
174
175 5
    private function parseRuleMatcher(ContainerBuilder $container, array $match)
176
    {
177 5
        $match['ips'] = (empty($match['ips'])) ? null : $match['ips'];
178
179 5
        return $this->createRequestMatcher(
180 5
            $container,
181 5
            $match['path'],
182 5
            $match['host'],
183 5
            $match['methods'],
184 5
            $match['ips'],
185 5
            $match['attributes']
186
        );
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->setParameter($this->getAlias().'.event_listener.user_context.options', [
198 4
            'user_identifier_headers' => $config['user_identifier_headers'],
199 4
            'user_hash_header' => $config['user_hash_header'],
200 4
            'ttl' => $config['hash_cache_ttl'],
201 4
            'add_vary_on_hash' => $config['always_vary_on_context_hash'],
202
        ]);
203 4
        $container->getDefinition($this->getAlias().'.event_listener.user_context')
204 4
            ->replaceArgument(0, new Reference($config['match']['matcher_service']));
205
206 4
        $container->getDefinition($this->getAlias().'.user_context.anonymous_request_matcher')
207 4
            ->replaceArgument(0, $config['user_identifier_headers']);
208
209 4
        if ($config['logout_handler']['enabled']) {
210 4
            $container->getDefinition($this->getAlias().'.user_context.logout_handler')
211 4
                ->replaceArgument(1, $config['user_identifier_headers'])
212 4
                ->replaceArgument(2, $config['match']['accept']);
213
        } else {
214
            $container->removeDefinition($this->getAlias().'.user_context.logout_handler');
215
        }
216
217 4
        if ($config['role_provider']) {
218 2
            $container->getDefinition($this->getAlias().'.user_context.role_provider')
219 2
                ->addTag(HashGeneratorPass::TAG_NAME)
220 2
                ->setAbstract(false);
221
        }
222 4
    }
223
224 5
    private function createRequestMatcher(ContainerBuilder $container, $path = null, $host = null, $methods = null, $ips = null, array $attributes = [])
225
    {
226 5
        $arguments = [$path, $host, $methods, $ips, $attributes];
227 5
        $serialized = serialize($arguments);
228 5
        $id = $this->getAlias().'.request_matcher.'.md5($serialized).sha1($serialized);
229
230 5
        if (!$container->hasDefinition($id)) {
231 5
            if (class_exists(ChildDefinition::class)) {
232
                $container
233
                    ->setDefinition($id, new ChildDefinition($this->getAlias().'.request_matcher'))
234
                    ->setArguments($arguments)
235
                ;
236
            } else {
237
                $container
238 5
                    ->setDefinition($id, new DefinitionDecorator($this->getAlias().'.request_matcher'))
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Depend...ion\DefinitionDecorator has been deprecated with message: The DefinitionDecorator class is deprecated since version 3.3 and will be removed in 4.0. Use the Symfony\Component\DependencyInjection\ChildDefinition class instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
239 5
                    ->setArguments($arguments)
240
                ;
241
            }
242
        }
243
244 5
        return new Reference($id);
245
    }
246
247 17
    private function loadProxyClient(ContainerBuilder $container, XmlFileLoader $loader, array $config)
248
    {
249 17
        if (isset($config['varnish'])) {
250 13
            $this->loadVarnish($container, $loader, $config['varnish']);
251
        }
252 16
        if (isset($config['nginx'])) {
253 2
            $this->loadNginx($container, $loader, $config['nginx']);
254
        }
255 16
        if (isset($config['symfony'])) {
256 1
            $this->loadSymfony($container, $loader, $config['symfony']);
257
        }
258 16
        if (isset($config['noop'])) {
259 1
            $loader->load('noop.xml');
260
        }
261
262 16
        $container->setAlias(
263 16
            $this->getAlias().'.default_proxy_client',
264 16
            $this->getAlias().'.proxy_client.'.$this->getDefaultProxyClient($config)
265
        );
266 16
    }
267
268
    /**
269
     * Define the http dispatcher service for the proxy client $name.
270
     *
271
     * @param ContainerBuilder $container
272
     * @param array            $config
273
     * @param string           $serviceName
274
     */
275 16
    private function createHttpDispatcherDefinition(ContainerBuilder $container, array $config, $serviceName)
276
    {
277 16
        foreach ($config['servers'] as $url) {
278 16
            $this->validateUrl($url, 'Not a valid Varnish server address: "%s"');
279
        }
280 16
        if (!empty($config['base_url'])) {
281 16
            $baseUrl = $this->prefixSchema($config['base_url']);
282 16
            $this->validateUrl($baseUrl, 'Not a valid base path: "%s"');
283
        } else {
284
            $baseUrl = null;
285
        }
286 15
        $httpClient = null;
287 15
        if ($config['http_client']) {
288 1
            $httpClient = new Reference($config['http_client']);
289
        }
290
291 15
        $definition = new Definition(HttpDispatcher::class, [
292 15
            $config['servers'],
293 15
            $baseUrl,
294 15
            $httpClient,
295
        ]);
296
297 15
        $container->setDefinition($serviceName, $definition);
298 15
    }
299
300 13
    private function loadVarnish(ContainerBuilder $container, XmlFileLoader $loader, array $config)
301
    {
302 13
        $this->createHttpDispatcherDefinition($container, $config['http'], $this->getAlias().'.proxy_client.varnish.http_dispatcher');
303
        $options = [
304 12
            'tags_header' => $config['tags_header'],
305
        ];
306 12
        if (!empty($config['header_length'])) {
307
            $options['header_length'] = $config['header_length'];
308
        }
309 12
        if (!empty($config['default_ban_headers'])) {
310
            $options['default_ban_headers'] = $config['default_ban_headers'];
311
        }
312 12
        $container->setParameter($this->getAlias().'.proxy_client.varnish.options', $options);
313
314 12
        $loader->load('varnish.xml');
315 12
    }
316
317 2
    private function loadNginx(ContainerBuilder $container, XmlFileLoader $loader, array $config)
318
    {
319 2
        $this->createHttpDispatcherDefinition($container, $config['http'], $this->getAlias().'.proxy_client.nginx.http_dispatcher');
320 2
        $container->setParameter($this->getAlias().'.proxy_client.nginx.options', [
321 2
            'purge_location' => $config['purge_location'],
322
        ]);
323 2
        $loader->load('nginx.xml');
324 2
    }
325
326 1
    private function loadSymfony(ContainerBuilder $container, XmlFileLoader $loader, array $config)
327
    {
328 1
        $this->createHttpDispatcherDefinition($container, $config['http'], $this->getAlias().'.proxy_client.symfony.http_dispatcher');
329 1
        $loader->load('symfony.xml');
330 1
    }
331
332
    /**
333
     * @param ContainerBuilder $container
334
     * @param XmlFileLoader    $loader
335
     * @param array            $config    Configuration section for the tags node
336
     * @param string           $client    Name of the client used with the cache manager,
337
     *                                    "custom" when a custom client is used
338
     */
339 17
    private function loadCacheTagging(ContainerBuilder $container, XmlFileLoader $loader, array $config, $client)
340
    {
341 17
        if ('auto' === $config['enabled'] && 'varnish' !== $client) {
342 4
            $container->setParameter($this->getAlias().'.compiler_pass.tag_annotations', false);
343
344 4
            return;
345
        }
346 13
        if (!in_array($client, ['varnish', 'custom'])) {
347 1
            throw new InvalidConfigurationException(sprintf('You can not enable cache tagging with the %s client', $client));
348
        }
349
350 12
        $container->setParameter($this->getAlias().'.compiler_pass.tag_annotations', true);
351 12
        $container->setParameter($this->getAlias().'.tag_handler.response_header', $config['response_header']);
352 12
        $container->setParameter($this->getAlias().'.tag_handler.strict', $config['strict']);
353 12
        $loader->load('cache_tagging.xml');
354
355 12
        if (!empty($config['expression_language'])) {
356
            $container->setAlias(
357
                $this->getAlias().'.tag_handler.expression_language',
358
                $config['expression_language']
359
            );
360
        }
361
362 12
        if (!empty($config['rules'])) {
363 2
            $this->loadTagRules($container, $config['rules']);
364
        }
365 12
    }
366
367 1
    private function loadTest(ContainerBuilder $container, XmlFileLoader $loader, array $config)
368
    {
369 1
        $container->setParameter($this->getAlias().'.test.cache_header', $config['cache_header']);
370
371 1
        if ($config['proxy_server']) {
372 1
            $this->loadProxyServer($container, $loader, $config['proxy_server']);
373
        }
374 1
    }
375
376 1
    private function loadProxyServer(ContainerBuilder $container, XmlFileLoader $loader, array $config)
377
    {
378 1
        if (isset($config['varnish'])) {
379 1
            $this->loadVarnishProxyServer($container, $loader, $config['varnish']);
380
        }
381
382 1
        if (isset($config['nginx'])) {
383
            $this->loadNginxProxyServer($container, $loader, $config['varnish']);
384
        }
385
386 1
        $container->setAlias(
387 1
            $this->getAlias().'.test.default_proxy_server',
388 1
            $this->getAlias().'.test.proxy_server.'.$this->getDefaultProxyClient($config)
389
        );
390 1
    }
391
392 1
    private function loadVarnishProxyServer(ContainerBuilder $container, XmlFileLoader $loader, $config)
393
    {
394 1
        $loader->load('varnish_proxy.xml');
395 1
        foreach ($config as $key => $value) {
396 1
            $container->setParameter(
397 1
                $this->getAlias().'.test.proxy_server.varnish.'.$key,
398 1
                $value
399
            );
400
        }
401 1
    }
402
403
    private function loadNginxProxyServer(ContainerBuilder $container, XmlFileLoader $loader, $config)
404
    {
405
        $loader->load('nginx_proxy.xml');
406
        foreach ($config as $key => $value) {
407
            $container->setParameter(
408
                $this->getAlias().'.test.proxy_server.nginx.'.$key,
409
                $value
410
            );
411
        }
412
    }
413
414 2
    private function loadTagRules(ContainerBuilder $container, array $config)
415
    {
416 2
        $tagDefinition = $container->getDefinition($this->getAlias().'.event_listener.tag');
417
418 2
        foreach ($config as $rule) {
419 2
            $ruleMatcher = $this->parseRuleMatcher($container, $rule['match']);
420
421
            $tags = [
422 2
                'tags' => $rule['tags'],
423 2
                'expressions' => $rule['tag_expressions'],
424
            ];
425
426 2
            $tagDefinition->addMethodCall('addRule', [$ruleMatcher, $tags]);
427
        }
428 2
    }
429
430 2
    private function loadInvalidatorRules(ContainerBuilder $container, array $config)
431
    {
432 2
        $tagDefinition = $container->getDefinition($this->getAlias().'.event_listener.invalidation');
433
434 2
        foreach ($config as $rule) {
435 2
            $ruleMatcher = $this->parseRuleMatcher($container, $rule['match']);
436 2
            $tagDefinition->addMethodCall('addRule', [$ruleMatcher, $rule['routes']]);
437
        }
438 2
    }
439
440 16
    private function validateUrl($url, $msg)
441
    {
442 16
        $prefixed = $this->prefixSchema($url);
443
444 16
        if (!$parts = parse_url($prefixed)) {
445 1
            throw new InvalidConfigurationException(sprintf($msg, $url));
446
        }
447 16
    }
448
449 16
    private function prefixSchema($url)
450
    {
451 16
        if (false === strpos($url, '://')) {
452 16
            $url = sprintf('%s://%s', 'http', $url);
453
        }
454
455 16
        return $url;
456
    }
457
458 16
    private function getDefaultProxyClient(array $config)
459
    {
460 16
        if (isset($config['default'])) {
461
            return $config['default'];
462
        }
463
464 16
        if (isset($config['varnish'])) {
465 12
            return 'varnish';
466
        }
467
468 4
        if (isset($config['nginx'])) {
469 2
            return 'nginx';
470
        }
471
472 2
        if (isset($config['symfony'])) {
473 1
            return 'symfony';
474
        }
475
476 1
        if (isset($config['noop'])) {
477 1
            return 'noop';
478
        }
479
480
        throw new InvalidConfigurationException('No proxy client configured');
481
    }
482
}
483