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

Fastly::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 3
dl 0
loc 10
ccs 3
cts 4
cp 0.75
crap 2.0625
rs 10
c 0
b 0
f 0
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\RefreshCapable;
17
use FOS\HttpCache\ProxyClient\Invalidation\TagCapable;
18
use Http\Message\RequestFactory;
19
20
/**
21
 * Fastly HTTP cache invalidator.
22
 *
23
 * Additional constructor options:
24
 * - service_identifier    Identifier for your Fastly service account.
25
 * - authentication_token  Token for authentication against Fastly APIs.
26
 *                         For full capabilities (incl ClearCapable) you'll need one with Fastly Engineer permissions.
27
 * - soft_purge            Boolean for doing soft purges or not on tag invalidation and url purging, default true.
28
 *                         Soft purges expires cache instead of hard purge, and allow grace/stale handling.
29
 *
30
 * @see https://docs.fastly.com/api/purge Fastly Purge API documentation.
31
 *
32
 * @author Simone Fumagalli <[email protected]>
33
 */
34
class Fastly extends HttpProxyClient implements ClearCapable, PurgeCapable, RefreshCapable, TagCapable
35
{
36
    private const HTTP_METHOD_PURGE = 'PURGE';
37
38
    /**
39
     * @see https://docs.fastly.com/api/purge#purge_db35b293f8a724717fcf25628d713583 Fastly's limit on batch tag purges.
40
     */
41
    private const TAG_BATCH_PURGE_LIMIT = 256;
42
43
    /**
44
     * @see https://docs.fastly.com/api/purge Base url endpoint used on anything but url PURGE/GET/HEAD.
45
     */
46
    private const API_ENDPOINT = 'https://api.fastly.com';
47
48 6
    public function __construct(
49
        Dispatcher $httpDispatcher,
50
        array $options = [],
51
        RequestFactory $messageFactory = null
52
    ) {
53 6
        if (!function_exists('json_encode')) {
54
            throw new \Exception('ext-json is required for fastly invalidation');
55
        }
56
57 6
        parent::__construct($httpDispatcher, $options, $messageFactory);
58 6
    }
59
60
    /**
61
     * {@inheritdoc}
62
     *
63
     * @see https://docs.fastly.com/api/purge#purge_db35b293f8a724717fcf25628d713583
64
     */
65 3
    public function invalidateTags(array $tags)
66
    {
67 3
        $url = sprintf(self::API_ENDPOINT.'/service/%s/purge', $this->options['service_identifier']);
68 3
        $headers = ['Accept' => 'application/json'];
69 3
        if (true === $this->options['soft_purge']) {
70 2
            $headers['Fastly-Soft-Purge'] = 1;
71
        }
72
73
        // Split tag invalidations across several requests within Fastly's tag batch invalidations limits.
74 3
        foreach (\array_chunk($tags, self::TAG_BATCH_PURGE_LIMIT) as $tagChunk) {
75 3
            $this->queueRequest(
76 3
                'POST',
77
                $url,
78
                $headers,
79 3
                false,
80 3
                json_encode(['surrogate_keys' => $tagChunk])
81
            );
82
        }
83
84 3
        return $this;
85
    }
86
87
    /**
88
     * {@inheritdoc}
89
     *
90
     * @see https://docs.fastly.com/api/purge#soft_purge_0c4f56f3d68e9bed44fb8b638b78ea36
91
     * @see https://docs.fastly.com/guides/purging/authenticating-api-purge-requests#purging-urls-with-an-api-token
92
     */
93 1
    public function purge($url, array $headers = [])
94
    {
95 1
        if (true === $this->options['soft_purge']) {
96 1
            $headers['Fastly-Soft-Purge'] = 1;
97
        }
98
99 1
        $this->queueRequest(
100 1
            self::HTTP_METHOD_PURGE,
101
            $url,
102
            $headers,
103 1
            false
104
        );
105
106 1
        return $this;
107
    }
108
109
    /**
110
     * {@inheritdoc}
111
     */
112 1
    public function refresh($url, array $headers = [])
113
    {
114
        // First soft purge url
115 1
        $this->queueRequest(
116 1
            self::HTTP_METHOD_PURGE,
117
            $url,
118 1
            ['Fastly-Soft-Purge' => 1] + $headers,
119 1
            false
120
        );
121
122
        // Secondly make sure refresh is triggered with a HEAD request
123 1
        $this->queueRequest(
124 1
            'HEAD',
125
            $url,
126
            $headers,
127 1
            false
128
        );
129
130 1
        return $this;
131
    }
132
133
    /**
134
     * {@inheritdoc}
135
     *
136
     * @see https://docs.fastly.com/api/purge#purge_bee5ed1a0cfd541e8b9f970a44718546
137
     *
138
     * Warning:
139
     * - Does not support soft purge, for that use an "all" key.
140
     * - Requires a API token of a user with at least Engineer permissions.
141
     */
142 1
    public function clear()
143
    {
144 1
        $this->queueRequest(
145 1
            'POST',
146 1
            sprintf(self::API_ENDPOINT.'/service/%s/purge_all', $this->options['service_identifier']),
147 1
            ['Accept' => 'application/json'],
148 1
            false
149
        );
150
151 1
        return $this;
152
    }
153
154
    /**
155
     * {@inheritdoc} Always provides default authentication token on "Fastly-Key" header.
156
     */
157 6
    protected function queueRequest($method, $url, array $headers, $validateHost = true, $body = null)
158
    {
159 6
        parent::queueRequest(
160 6
            $method,
161
            $url,
162 6
            $headers + ['Fastly-Key' => $this->options['authentication_token']],
163
            $validateHost,
164
            $body
165
        );
166 6
    }
167
168
    /**
169
     * {@inheritdoc}
170
     */
171 6
    protected function configureOptions()
172
    {
173 6
        $resolver = parent::configureOptions();
174
175 6
        $resolver->setRequired([
176 6
            'authentication_token',
177
            'service_identifier',
178
            'soft_purge',
179
        ]);
180
181 6
        $resolver->setDefaults([
182 6
            'soft_purge' => true,
183
        ]);
184
185 6
        return $resolver;
186
    }
187
}
188