Passed
Push — master ( bce216...5b6588 )
by David
02:23
created

CacheInvalidator::flush()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 5

Importance

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