Passed
Pull Request — master (#520)
by
unknown
02:47
created

CacheInvalidator   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 287
Duplicated Lines 0 %

Test Coverage

Coverage 88.89%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 70
dl 0
loc 287
ccs 64
cts 72
cp 0.8889
rs 9.84
c 1
b 0
f 0
wmc 32

12 Methods

Rating   Name   Duplication   Size   Complexity  
A refreshPath() 0 9 2
A flush() 0 16 5
B supports() 0 20 8
A invalidate() 0 9 2
A invalidateRegex() 0 9 2
A invalidateTags() 0 8 2
A invalidatePath() 0 9 2
A clearCache() 0 9 2
A setEventDispatcher() 0 8 2
A getEventDispatcher() 0 11 3
A __construct() 0 3 1
A dispatch() 0 3 1
1
<?php
2
3
/*
4
 * This file is part of the FOSHttpCache 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\HttpCache;
13
14
use FOS\HttpCache\Exception\ExceptionCollection;
15
use FOS\HttpCache\Exception\InvalidArgumentException;
16
use FOS\HttpCache\Exception\ProxyResponseException;
17
use FOS\HttpCache\Exception\ProxyUnreachableException;
18
use FOS\HttpCache\Exception\UnsupportedProxyOperationException;
19
use FOS\HttpCache\ProxyClient\Invalidation\BanCapable;
20
use FOS\HttpCache\ProxyClient\Invalidation\ClearCapable;
21
use FOS\HttpCache\ProxyClient\Invalidation\PurgeCapable;
22
use FOS\HttpCache\ProxyClient\Invalidation\RefreshCapable;
23
use FOS\HttpCache\ProxyClient\Invalidation\TagCapable;
24
use FOS\HttpCache\ProxyClient\ProxyClient;
25
use FOS\HttpCache\ProxyClient\Symfony;
26
use Symfony\Component\EventDispatcher\EventDispatcher;
27
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
28
use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy;
29
use Symfony\Component\HttpKernel\Kernel;
30
use Toflar\Psr6HttpCacheStore\Psr6Store;
0 ignored issues
show
Bug introduced by
The type Toflar\Psr6HttpCacheStore\Psr6Store was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
31
32
/**
33
 * Manages HTTP cache invalidation.
34
 *
35
 * @author David de Boer <[email protected]>
36
 * @author David Buchmann <[email protected]>
37
 * @author André Rømcke <[email protected]>
38
 */
39
class CacheInvalidator
40
{
41
    /**
42
     * Value to check support of invalidatePath operation.
43
     */
44
    public const PATH = 'path';
45
46
    /**
47
     * Value to check support of refreshPath operation.
48
     */
49
    public const REFRESH = 'refresh';
50
51
    /**
52
     * Value to check support of invalidate and invalidateRegex operations.
53
     */
54
    public const INVALIDATE = 'invalidate';
55
56
    /**
57
     * Value to check support of invalidateTags operation.
58
     */
59
    public const TAGS = 'tags';
60
61
    /**
62
     * Value to check support of clearCache operation.
63
     */
64
    public const CLEAR = 'clear';
65
66
    /**
67
     * @var ProxyClient
68
     */
69
    private $cache;
70
71
    /**
72
     * @var EventDispatcherInterface
73
     */
74
    private $eventDispatcher;
75
76
    /**
77
     * Constructor.
78
     *
79
     * @param ProxyClient $cache HTTP cache
80
     */
81 17
    public function __construct(ProxyClient $cache)
82
    {
83 17
        $this->cache = $cache;
84 17
    }
85
86
    /**
87
     * Check whether this invalidator instance supports the specified
88
     * operation.
89
     *
90
     * Support for PATH means invalidatePath will work, REFRESH means
91
     * refreshPath works, TAGS means that invalidateTags works and
92
     * INVALIDATE is for the invalidate and invalidateRegex methods.
93
     *
94
     * @param string $operation one of the class constants
95
     *
96
     * @return bool
97
     *
98
     * @throws InvalidArgumentException
99
     */
100 3
    public function supports($operation)
101
    {
102
        switch ($operation) {
103 3
            case self::PATH:
104 2
                return $this->cache instanceof PurgeCapable;
105 3
            case self::REFRESH:
106 2
                return $this->cache instanceof RefreshCapable;
107 3
            case self::INVALIDATE:
108 2
                return $this->cache instanceof BanCapable;
109 3
            case self::TAGS:
110 2
                $supports = $this->cache instanceof TagCapable;
111 2
                if ($supports && $this->cache instanceof Symfony) {
112
                    return class_exists(Psr6Store::class);
113
                }
114
115 2
                return $supports;
116 1
            case self::CLEAR:
117
                return $this->cache instanceof ClearCapable;
118
            default:
119 1
                throw new InvalidArgumentException('Unknown operation '.$operation);
120
        }
121
    }
122
123
    /**
124
     * Set event dispatcher - may only be called once.
125
     *
126
     * @throws \Exception when trying to override the event dispatcher
127
     */
128 2
    public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
129
    {
130 2
        if ($this->eventDispatcher) {
131
            // if you want to set a custom event dispatcher, do so right after instantiating
132
            // the invalidator.
133 1
            throw new \Exception('You may not change the event dispatcher once it is set.');
134
        }
135 2
        $this->eventDispatcher = $eventDispatcher;
136 2
    }
137
138
    /**
139
     * Get the event dispatcher used by the cache invalidator.
140
     *
141
     * @return EventDispatcherInterface
142
     */
143 2
    public function getEventDispatcher()
144
    {
145 2
        if (!$this->eventDispatcher) {
146 1
            if (class_exists(LegacyEventDispatcherProxy::class)) {
147 1
                $this->eventDispatcher = LegacyEventDispatcherProxy::decorate(new EventDispatcher());
148
            } else {
149
                $this->eventDispatcher = new EventDispatcher();
150
            }
151
        }
152
153 2
        return $this->eventDispatcher;
154
    }
155
156
    /**
157
     * Invalidate a path or URL.
158
     *
159
     * @param string $path    Path or URL
160
     * @param array  $headers HTTP headers (optional)
161
     *
162
     * @throws UnsupportedProxyOperationException
163
     *
164
     * @return $this
165
     */
166 2
    public function invalidatePath($path, array $headers = [])
167
    {
168 2
        if (!$this->cache instanceof PurgeCapable) {
169 1
            throw UnsupportedProxyOperationException::cacheDoesNotImplement('PURGE');
170
        }
171
172 1
        $this->cache->purge($path, $headers);
173
174 1
        return $this;
175
    }
176
177
    /**
178
     * Refresh a path or URL.
179
     *
180
     * @param string $path    Path or URL
181
     * @param array  $headers HTTP headers (optional)
182
     *
183
     * @see RefreshCapable::refresh()
184
     *
185
     * @throws UnsupportedProxyOperationException
186
     *
187
     * @return $this
188
     */
189 2
    public function refreshPath($path, array $headers = [])
190
    {
191 2
        if (!$this->cache instanceof RefreshCapable) {
192 1
            throw UnsupportedProxyOperationException::cacheDoesNotImplement('REFRESH');
193
        }
194
195 1
        $this->cache->refresh($path, $headers);
196
197 1
        return $this;
198
    }
199
200
    /**
201
     * Invalidate all cached objects matching the provided HTTP headers.
202
     *
203
     * Each header is a a POSIX regular expression, for example
204
     * ['X-Host' => '^(www\.)?(this|that)\.com$']
205
     *
206
     * @see BanCapable::ban()
207
     *
208
     * @param array $headers HTTP headers that path must match to be banned
209
     *
210
     * @throws UnsupportedProxyOperationException If HTTP cache does not support BAN requests
211
     *
212
     * @return $this
213
     */
214 2
    public function invalidate(array $headers)
215
    {
216 2
        if (!$this->cache instanceof BanCapable) {
217 1
            throw UnsupportedProxyOperationException::cacheDoesNotImplement('BAN');
218
        }
219
220 1
        $this->cache->ban($headers);
221
222 1
        return $this;
223
    }
224
225
    /**
226
     * Remove/Expire cache objects based on cache tags.
227
     *
228
     * @see TagCapable::tags()
229
     *
230
     * @param array $tags Tags that should be removed/expired from the cache
231
     *
232
     * @throws UnsupportedProxyOperationException If HTTP cache does not support Tags invalidation
233
     *
234
     * @return $this
235
     */
236 3
    public function invalidateTags(array $tags)
237
    {
238 3
        if (!$this->cache instanceof TagCapable) {
239 1
            throw UnsupportedProxyOperationException::cacheDoesNotImplement('Tags');
240
        }
241 2
        $this->cache->invalidateTags($tags);
242
243 2
        return $this;
244
    }
245
246
    /**
247
     * Invalidate URLs based on a regular expression for the URI, an optional
248
     * content type and optional limit to certain hosts.
249
     *
250
     * The hosts parameter can either be a regular expression, e.g.
251
     * '^(www\.)?(this|that)\.com$' or an array of exact host names, e.g.
252
     * ['example.com', 'other.net']. If the parameter is empty, all hosts
253
     * are matched.
254
     *
255
     * @see BanCapable::banPath()
256
     *
257
     * @param string       $path        Regular expression pattern for URI to
258
     *                                  invalidate
259
     * @param string       $contentType Regular expression pattern for the content
260
     *                                  type to limit banning, for instance 'text'
261
     * @param array|string $hosts       Regular expression of a host name or list of
262
     *                                  exact host names to limit banning
263
     *
264
     * @throws UnsupportedProxyOperationException If HTTP cache does not support BAN requests
265
     *
266
     * @return $this
267
     */
268 2
    public function invalidateRegex($path, $contentType = null, $hosts = null)
269
    {
270 2
        if (!$this->cache instanceof BanCapable) {
271 1
            throw UnsupportedProxyOperationException::cacheDoesNotImplement('BAN');
272
        }
273
274 1
        $this->cache->banPath($path, $contentType, $hosts);
275
276 1
        return $this;
277
    }
278
279
    /**
280
     * Clear the cache completely.
281
     *
282
     * @throws UnsupportedProxyOperationException if HTTP cache does not support clearing the cache completely
283
     *
284
     * @return $this
285
     */
286
    public function clearCache()
287
    {
288
        if (!$this->cache instanceof ClearCapable) {
289
            throw UnsupportedProxyOperationException::cacheDoesNotImplement('CLEAR');
290
        }
291
292
        $this->cache->clear();
293
294
        return $this;
295
    }
296
297
    /**
298
     * Send all pending invalidation requests.
299
     *
300
     * @return int the number of cache invalidations performed per caching server
301
     *
302
     * @throws ExceptionCollection if any errors occurred during flush
303
     */
304 3
    public function flush()
305
    {
306
        try {
307 3
            return $this->cache->flush();
308 1
        } catch (ExceptionCollection $exceptions) {
309 1
            foreach ($exceptions as $exception) {
310 1
                $event = new Event();
311 1
                $event->setException($exception);
312 1
                if ($exception instanceof ProxyResponseException) {
313 1
                    $this->dispatch($event, Events::PROXY_RESPONSE_ERROR);
314 1
                } elseif ($exception instanceof ProxyUnreachableException) {
315 1
                    $this->dispatch($event, Events::PROXY_UNREACHABLE_ERROR);
316
                }
317
            }
318
319 1
            throw $exceptions;
320
        }
321
    }
322
323 1
    private function dispatch(Event $event, $eventName)
324
    {
325 1
        $this->getEventDispatcher()->dispatch($event, $eventName);
326 1
    }
327
}
328