Completed
Push — master ( 9a04b6...20ed72 )
by David
13s
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 24 View Code Duplication
    public function __construct(
0 ignored issues
show
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);
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 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
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