Completed
Push — master ( 843a10...48cec1 )
by David
17s queued 12s
created

Cloudflare::invalidateTags()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 6
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 11
ccs 8
cts 8
cp 1
crap 1
rs 10
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\ProxyClient;
13
14
use FOS\HttpCache\ProxyClient\Invalidation\ClearCapable;
15
use FOS\HttpCache\ProxyClient\Invalidation\PurgeCapable;
16
use FOS\HttpCache\ProxyClient\Invalidation\TagCapable;
17
use Http\Message\RequestFactory;
18
19
/**
20
 * Cloudflare HTTP cache invalidator.
21
 *
22
 * Additional constructor options:
23
 * - zone_identifier       Identifier for your Cloudflare zone you want to purge the cache for
24
 * - authentication_token  API authorization token, requires Zone.Cache Purge permissions
25
 *
26
 * @author Simon Jones <[email protected]>
27
 */
28
class Cloudflare extends HttpProxyClient implements ClearCapable, PurgeCapable, TagCapable
29
{
30
    /**
31
     * @see https://api.cloudflare.com/#getting-started-endpoints
32
     */
33
    private const API_ENDPOINT = '/client/v4';
34
35
    /**
36
     * Batch URL purge limit.
37
     *
38
     * @see https://api.cloudflare.com/#zone-purge-files-by-url
39
     */
40
    private const URL_BATCH_PURGE_LIMIT = 30;
41
42
    /**
43
     * Array of data to send to Cloudflare for purge by URLs request.
44
     *
45
     * To reduce the number of requests to cloudflare, we buffer the URLs.
46
     * During flush, we build requests with batches of URL_BATCH_PURGE_LIMIT.
47
     *
48
     * @var array<string|array{url: string, headers: string[]}>
49
     */
50
    private $purgeByUrlsData = [];
51
52 3
    public function __construct(
53
        Dispatcher $httpDispatcher,
54
        array $options = [],
55
        RequestFactory $messageFactory = null
56
    ) {
57 3
        if (!function_exists('json_encode')) {
58
            throw new \Exception('ext-json is required for cloudflare invalidation');
59
        }
60
61 3
        parent::__construct($httpDispatcher, $options, $messageFactory);
62 3
    }
63
64
    /**
65
     * {@inheritdoc}
66
     *
67
     * Tag invalidation only available with Cloudflare enterprise account
68
     *
69
     * @see https://api.cloudflare.com/#zone-purge-files-by-cache-tags,-host-or-prefix
70
     */
71 1
    public function invalidateTags(array $tags)
72
    {
73 1
        $this->queueRequest(
74 1
            'POST',
75 1
            sprintf(self::API_ENDPOINT.'/zones/%s/purge_cache', $this->options['zone_identifier']),
76 1
            [],
77 1
            false,
78 1
            $this->json_encode(['tags' => $tags])
79
        );
80
81 1
        return $this;
82
    }
83
84
    /**
85
     * {@inheritdoc}
86
     *
87
     * @see https://api.cloudflare.com/#zone-purge-files-by-url
88
     * @see https://developers.cloudflare.com/cache/how-to/purge-cache#purge-by-single-file-by-url For details on headers you can pass to clear the cache correctly
89
     */
90 1
    public function purge($url, array $headers = [])
91
    {
92 1
        if (!empty($headers)) {
93 1
            $this->purgeByUrlsData[] = [
94 1
                'url' => $url,
95 1
                'headers' => $headers,
96
            ];
97
        } else {
98 1
            $this->purgeByUrlsData[] = $url;
99
        }
100
101 1
        return $this;
102
    }
103
104
    /**
105
     * {@inheritdoc}
106
     *
107
     * @see https://api.cloudflare.com/#zone-purge-all-files
108
     */
109 1
    public function clear()
110
    {
111 1
        $this->queueRequest(
112 1
            'POST',
113 1
            sprintf(self::API_ENDPOINT.'/zones/%s/purge_cache', $this->options['zone_identifier']),
114 1
            ['Accept' => 'application/json'],
115 1
            false,
116 1
            $this->json_encode(['purge_everything' => true])
117
        );
118
119 1
        return $this;
120
    }
121
122
    /**
123
     * {@inheritdoc} Queue requests for purge by URLs
124
     */
125 1
    public function flush()
126
    {
127
        // Queue requests for purge by URL
128 1
        foreach (\array_chunk($this->purgeByUrlsData, self::URL_BATCH_PURGE_LIMIT) as $urlChunk) {
129 1
            $this->queueRequest(
130 1
                'POST',
131 1
                sprintf(self::API_ENDPOINT.'/zones/%s/purge_cache', $this->options['zone_identifier']),
132 1
                [],
133 1
                false,
134 1
                $this->json_encode(['files' => $urlChunk])
135
            );
136
        }
137 1
        $this->purgeByUrlsData = [];
138
139 1
        return parent::flush();
140
    }
141
142
    /**
143
     * {@inheritdoc} Always provides authentication token
144
     */
145 3
    protected function queueRequest($method, $url, array $headers, $validateHost = true, $body = null)
146
    {
147 3
        parent::queueRequest(
148 3
            $method,
149
            $url,
150 3
            $headers + ['Authorization' => 'Bearer '.$this->options['authentication_token']],
151
            $validateHost,
152
            $body
153
        );
154 3
    }
155
156
    /**
157
     * {@inheritdoc}
158
     */
159 3
    protected function configureOptions()
160
    {
161 3
        $resolver = parent::configureOptions();
162
163 3
        $resolver->setRequired([
164 3
            'authentication_token',
165
            'zone_identifier',
166
        ]);
167
168 3
        return $resolver;
169
    }
170
171 3
    private function json_encode(array $data): string
172
    {
173 3
        $json = json_encode($data, JSON_UNESCAPED_SLASHES);
174 3
        if (false === $json) {
175
            throw new \InvalidArgumentException(sprintf('Cannot encode "$data": %s', json_last_error_msg()));
176
        }
177
178 3
        return $json;
179
    }
180
}
181