Completed
Push — master ( 3bec43...a2fce4 )
by David
15s queued 10s
created

CacheInvalidator::clearCache()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 2

Importance

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