Completed
Push — master ( 849954...8f1795 )
by David
9s
created

src/EventListener/TagListener.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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 28 View Code Duplication
    public function __construct(
65
        CacheManager $cacheManager,
66
        SymfonyResponseTagger $tagHandler,
67
        RuleMatcherInterface $cacheableRule,
68
        RuleMatcherInterface $mustInvalidateRule,
69
        ExpressionLanguage $expressionLanguage = null
70
    ) {
71 28
        $this->cacheManager = $cacheManager;
72 28
        $this->symfonyResponseTagger = $tagHandler;
73 28
        $this->cacheableRule = $cacheableRule;
74 28
        $this->mustInvalidateRule = $mustInvalidateRule;
75 28
        $this->expressionLanguage = $expressionLanguage ?: new ExpressionLanguage();
76 28
    }
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 27
    public function onKernelResponse(FilterResponseEvent $event)
88
    {
89 27
        $request = $event->getRequest();
90 27
        $response = $event->getResponse();
91
92 27
        if (!$this->cacheableRule->matches($request, $response)
93 27
            && !$this->mustInvalidateRule->matches($request, $response)
94
        ) {
95 4
            return;
96
        }
97
98 23
        $tags = $this->getAnnotationTags($request);
99
100 23
        $configuredTags = $this->matchRule($request);
101 23
        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 23
        if ($this->cacheableRule->matches($request, $response)) {
109
            // For safe requests (GET and HEAD), set cache tags on response
110 15
            $this->symfonyResponseTagger->addTags($tags);
111 15
            if (HttpKernelInterface::MASTER_REQUEST === $event->getRequestType()) {
112 15
                $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 23
    }
120
121
    /**
122
     * {@inheritdoc}
123
     */
124 2
    public static function getSubscribedEvents()
125
    {
126
        return [
127 2
            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 23
    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 23
        if (!$tagConfigurations = $request->attributes->get('_tag')) {
145 12
            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 3
                    $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
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