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

TagListener::onKernelResponse()   D

Complexity

Conditions 9
Paths 9

Size

Total Lines 33
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 9

Importance

Changes 0
Metric Value
dl 0
loc 33
ccs 20
cts 20
cp 1
rs 4.909
c 0
b 0
f 0
cc 9
eloc 19
nc 9
nop 1
crap 9
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\EventListener;
13
14
use FOS\HttpCacheBundle\CacheManager;
15
use FOS\HttpCacheBundle\Configuration\Tag;
16
use FOS\HttpCacheBundle\Http\RuleMatcherInterface;
17
use FOS\HttpCacheBundle\Http\SymfonyResponseTagger;
18
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
19
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
20
use Symfony\Component\HttpFoundation\Request;
21
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
22
use Symfony\Component\HttpKernel\HttpKernelInterface;
23
use Symfony\Component\HttpKernel\KernelEvents;
24
25
/**
26
 * Event handler for the cache tagging tags.
27
 *
28
 * @author David de Boer <[email protected]>
29
 */
30
class TagListener extends AbstractRuleListener implements EventSubscriberInterface
31
{
32
    /**
33
     * @var CacheManager
34
     */
35
    private $cacheManager;
36
37
    /**
38
     * @var SymfonyResponseTagger
39
     */
40
    private $symfonyResponseTagger;
41
42
    /**
43
     * @var ExpressionLanguage
44
     */
45
    private $expressionLanguage;
46
47
    /**
48
     * @var RuleMatcherInterface
49
     */
50
    private $mustInvalidateRule;
51
52
    /**
53
     * @var RuleMatcherInterface
54
     */
55
    private $cacheableRule;
56
57
    /**
58
     * Constructor.
59
     *
60
     * @param CacheManager            $cacheManager
61
     * @param SymfonyResponseTagger   $tagHandler
62
     * @param ExpressionLanguage|null $expressionLanguage
63
     */
64 24 View Code Duplication
    public function __construct(
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...
65
        CacheManager $cacheManager,
66
        SymfonyResponseTagger $tagHandler,
67
        RuleMatcherInterface $cacheableRule,
68
        RuleMatcherInterface $mustInvalidateRule,
69
        ExpressionLanguage $expressionLanguage = null
70
    ) {
71 24
        $this->cacheManager = $cacheManager;
72 24
        $this->symfonyResponseTagger = $tagHandler;
73 24
        $this->cacheableRule = $cacheableRule;
74 24
        $this->mustInvalidateRule = $mustInvalidateRule;
75 24
        $this->expressionLanguage = $expressionLanguage ?: new ExpressionLanguage();
76 24
    }
77
78
    /**
79
     * Process the _tags request attribute, which is set when using the Tag
80
     * annotation.
81
     *
82
     * - For a safe (GET or HEAD) request, the tags are set on the response.
83
     * - For a non-safe request, the tags will be invalidated.
84
     *
85
     * @param FilterResponseEvent $event
86
     */
87 24
    public function onKernelResponse(FilterResponseEvent $event)
88
    {
89 24
        $request = $event->getRequest();
90 24
        $response = $event->getResponse();
91
92 24
        if (!$this->cacheableRule->matches($request, $response)
93 24
            && !$this->mustInvalidateRule->matches($request, $response)
94
        ) {
95 4
            return;
96
        }
97
98 20
        $tags = $this->getAnnotationTags($request);
99
100 20
        $configuredTags = $this->matchRule($request, $response);
0 ignored issues
show
Unused Code introduced by
The call to TagListener::matchRule() has too many arguments starting with $response.

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...
101 20
        if ($configuredTags) {
102 5
            $tags = array_merge($tags, $configuredTags['tags']);
103 5
            foreach ($configuredTags['expressions'] as $expression) {
104 5
                $tags[] = $this->evaluateTag($expression, $request);
105
            }
106
        }
107
108 20
        if ($this->cacheableRule->matches($request, $response)) {
109
            // For safe requests (GET and HEAD), set cache tags on response
110 12
            $this->symfonyResponseTagger->addTags($tags);
111 12
            if (HttpKernelInterface::MASTER_REQUEST === $event->getRequestType()) {
112 12
                $this->symfonyResponseTagger->tagSymfonyResponse($response);
113
            }
114 8
        } elseif (count($tags)
115 8
            && $this->mustInvalidateRule->matches($request, $response)
116
        ) {
117 4
            $this->cacheManager->invalidateTags($tags);
118
        }
119 20
    }
120
121
    /**
122
     * {@inheritdoc}
123
     */
124 23
    public static function getSubscribedEvents()
125
    {
126
        return [
127 23
            KernelEvents::RESPONSE => 'onKernelResponse',
128
        ];
129
    }
130
131
    /**
132
     * Get the tags from the annotations on the controller that was used in the
133
     * request.
134
     *
135
     * @param Request $request
136
     *
137
     * @return array List of tags affected by the request
138
     */
139 20
    private function getAnnotationTags(Request $request)
140
    {
141
        // Check for _tag request attribute that is set when using @Tag
142
        // annotation
143
        /** @var $tagConfigurations Tag[] */
144 20
        if (!$tagConfigurations = $request->attributes->get('_tag')) {
145 9
            return [];
146
        }
147
148 11
        $tags = [];
149 11
        foreach ($tagConfigurations as $tagConfiguration) {
150 11
            if (null !== $tagConfiguration->getExpression()) {
151 3
                $tags[] = $this->evaluateTag(
152 3
                    $tagConfiguration->getExpression(),
153
                    $request
154
                );
155
            } else {
156 11
                $tags = array_merge($tags, $tagConfiguration->getTags());
157
            }
158
        }
159
160 11
        return $tags;
161
    }
162
163
    /**
164
     * Evaluate a tag that contains expressions.
165
     *
166
     * @param string  $expression
167
     * @param Request $request
168
     *
169
     * @return string Evaluated tag
0 ignored issues
show
Documentation introduced by
Should the return type not be array?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
170
     */
171 8
    private function evaluateTag($expression, Request $request)
172
    {
173 8
        $values = $request->attributes->all();
174
        // if there is an attribute called "request", it needs to be accessed through the request.
175 8
        $values['request'] = $request;
176
177 8
        return $this->expressionLanguage->evaluate($expression, $values);
178
    }
179
}
180