Completed
Push — master ( ab38a7...a2326e )
by David
10s
created

src/EventListener/InvalidationListener.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\HttpCache\Exception\ExceptionCollection;
15
use FOS\HttpCacheBundle\CacheManager;
16
use FOS\HttpCacheBundle\Configuration\InvalidatePath;
17
use FOS\HttpCacheBundle\Configuration\InvalidateRoute;
18
use FOS\HttpCacheBundle\Http\RuleMatcherInterface;
19
use Symfony\Component\Console\ConsoleEvents;
20
use Symfony\Component\Console\Event\ConsoleEvent;
21
use Symfony\Component\Console\Output\OutputInterface;
22
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
23
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
24
use Symfony\Component\HttpFoundation\Request;
25
use Symfony\Component\HttpFoundation\Response;
26
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
27
use Symfony\Component\HttpKernel\KernelEvents;
28
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
29
30
/**
31
 * On kernel.terminate event, this event handler invalidates routes for the
32
 * current request and flushes the CacheManager.
33
 *
34
 * @author David de Boer <[email protected]>
35
 */
36
class InvalidationListener extends AbstractRuleListener implements EventSubscriberInterface
37
{
38
    /**
39
     * Cache manager.
40
     *
41
     * @var CacheManager
42
     */
43
    private $cacheManager;
44
45
    /**
46
     * Router.
47
     *
48
     * @var UrlGeneratorInterface
49
     */
50
    private $urlGenerator;
51
52
    /**
53
     * Router.
54
     *
55
     * @var ExpressionLanguage|null
56
     */
57
    private $expressionLanguage;
58
59
    /**
60
     * @var RuleMatcherInterface
61
     */
62
    private $mustInvalidateRule;
63
64
    /**
65
     * Constructor.
66
     *
67
     * @param CacheManager            $cacheManager
68
     * @param UrlGeneratorInterface   $urlGenerator
69
     * @param ExpressionLanguage|null $expressionLanguage
70
     */
71 29 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...
72
        CacheManager $cacheManager,
73
        UrlGeneratorInterface $urlGenerator,
74
        RuleMatcherInterface $mustInvalidateRule,
75
        ExpressionLanguage $expressionLanguage = null
76
    ) {
77 29
        $this->cacheManager = $cacheManager;
78 29
        $this->urlGenerator = $urlGenerator;
79 29
        $this->expressionLanguage = $expressionLanguage ?: new ExpressionLanguage();
80 29
        $this->mustInvalidateRule = $mustInvalidateRule;
81 29
    }
82
83
    /**
84
     * Apply invalidators and flush cache manager.
85
     *
86
     * On kernel.terminate:
87
     * - see if any invalidators apply to the current request and, if so, add
88
     *   their routes to the cache manager;
89
     * - flush the cache manager in order to send invalidation requests to the
90
     *   HTTP cache.
91
     *
92
     * @param PostResponseEvent $event
93
     */
94 22
    public function onKernelTerminate(PostResponseEvent $event)
95
    {
96 22
        $request = $event->getRequest();
97 22
        $response = $event->getResponse();
98
99
        // Don't invalidate any caches if the request was unsuccessful
100 22
        if ($this->mustInvalidateRule->matches($request, $response)) {
101 10
            $this->handleInvalidation($request, $response);
102
        }
103
104
        try {
105 22
            $this->cacheManager->flush();
106
        } catch (ExceptionCollection $e) {
107
            // swallow exception
108
            // there is the fos_http_cache.event_listener.log to log them
109
        }
110 22
    }
111
112
    /**
113
     * Flush cache manager when kernel exception occurs.
114
     */
115 1
    public function onKernelException()
116
    {
117
        try {
118 1
            $this->cacheManager->flush();
119
        } catch (ExceptionCollection $e) {
120
            // swallow exception
121
            // there is the fos_http_cache.event_listener.log to log them
122
        }
123 1
    }
124
125
    /**
126
     * Flush cache manager when console terminates or errors.
127
     *
128
     * @throws ExceptionCollection If an exception occurs during flush
129
     */
130 5
    public function onConsoleTerminate(ConsoleEvent $event)
131
    {
132 5
        $num = $this->cacheManager->flush();
133
134 5
        if ($num > 0 && OutputInterface::VERBOSITY_VERBOSE <= $event->getOutput()->getVerbosity()) {
135 5
            $event->getOutput()->writeln(sprintf('Sent %d invalidation request(s)', $num));
136
        }
137 5
    }
138
139
    /**
140
     * {@inheritdoc}
141
     */
142 2
    public static function getSubscribedEvents()
143
    {
144
        return [
145 2
            KernelEvents::TERMINATE => 'onKernelTerminate',
146 2
            KernelEvents::EXCEPTION => 'onKernelException',
147 2
            ConsoleEvents::TERMINATE => 'onConsoleTerminate',
148
        ];
149
    }
150
151
    /**
152
     * Handle the invalidation annotations and configured invalidators.
153
     *
154
     * @param Request  $request
155
     * @param Response $response
156
     */
157 10
    private function handleInvalidation(Request $request, Response $response)
158
    {
159
        // Check controller annotations
160 10
        if ($paths = $request->attributes->get('_invalidate_path')) {
161 4
            $this->invalidatePaths($paths);
162
        }
163
164 10
        if ($routes = $request->attributes->get('_invalidate_route')) {
165 2
            $this->invalidateRoutes($routes, $request);
166
        }
167
168
        // Check configured invalidators
169 10
        if (!$invalidatorConfigs = $this->matchRule($request, $response)) {
170 6
            return;
171
        }
172
173 4
        $requestParams = $request->attributes->get('_route_params');
174 4
        foreach ($invalidatorConfigs as $route => $config) {
175 4
            $path = $this->urlGenerator->generate($route, $requestParams);
176
            // If extra route parameters should be ignored, strip the query
177
            // string generated by the Symfony router from the path
178 4
            if (isset($config['ignore_extra_params'])
179 4
                && $config['ignore_extra_params']
180 4
                && $pos = strpos($path, '?')
181
            ) {
182 1
                $path = substr($path, 0, $pos);
183
            }
184
185 4
            $this->cacheManager->invalidatePath($path);
186
        }
187 4
    }
188
189
    /**
190
     * Invalidate paths from annotations.
191
     *
192
     * @param array|InvalidatePath[] $pathConfigurations
193
     */
194 4
    private function invalidatePaths(array $pathConfigurations)
195
    {
196 4
        foreach ($pathConfigurations as $pathConfiguration) {
197 4
            foreach ($pathConfiguration->getPaths() as $path) {
198 4
                $this->cacheManager->invalidatePath($path);
199
            }
200
        }
201 4
    }
202
203
    /**
204
     * Invalidate routes from annotations.
205
     *
206
     * @param array|InvalidateRoute[] $routes
207
     * @param Request                 $request
208
     */
209 2
    private function invalidateRoutes(array $routes, Request $request)
210
    {
211 2
        $values = $request->attributes->all();
212
        // if there is an attribute called "request", it needs to be accessed through the request.
213 2
        $values['request'] = $request;
214
215 2
        foreach ($routes as $route) {
216 2
            $params = [];
217
218 2
            if (null !== $route->getParams()) {
219
                // Iterate over route params and try to evaluate their values
220 2
                foreach ($route->getParams() as $key => $value) {
221 2
                    if (is_array($value)) {
222 2
                        $value = $this->expressionLanguage->evaluate($value['expression'], $values);
223
                    }
224
225 2
                    $params[$key] = $value;
226
                }
227
            }
228
229 2
            $this->cacheManager->invalidateRoute($route->getName(), $params);
230
        }
231 2
    }
232
}
233