Passed
Push — master ( 74ce7c...3af96c )
by Bjørn
03:41
created

Ewww   A

Complexity

Total Complexity 36

Size/Duplication

Total Lines 305
Duplicated Lines 0 %

Test Coverage

Coverage 55%

Importance

Changes 0
Metric Value
eloc 141
dl 0
loc 305
ccs 77
cts 140
cp 0.55
rs 9.52
c 0
b 0
f 0
wmc 36

10 Methods

Rating   Name   Duplication   Size   Complexity  
A checkOperationality() 0 27 6
B getKeyStatus() 0 53 8
A isValidKey() 0 3 1
B doActualConvert() 0 73 7
B keepSubscriptionAlive() 0 55 7
A getKey() 0 9 3
A getOptionDefinitionsExtra() 0 4 1
A getQuota() 0 21 1
A isWorkingKey() 0 3 1
A supportsLossless() 0 3 1
1
<?php
2
3
namespace WebPConvert\Convert\Converters;
4
5
use WebPConvert\Convert\BaseConverters\AbstractCloudCurlConverter;
6
use WebPConvert\Convert\Exceptions\ConversionFailedException;
7
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperationalException;
8
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\InvalidApiKeyException;
9
use WebPConvert\Convert\Exceptions\ConversionFailed\ConverterNotOperational\SystemRequirementsNotMetException;
10
11
/**
12
 * Convert images to webp using ewww cloud service.
13
 *
14
 * @package    WebPConvert
15
 * @author     Bjørn Rosell <[email protected]>
16
 * @since      Class available since Release 2.0.0
17
 */
18
class Ewww extends AbstractCloudCurlConverter
19
{
20
    public function supportsLossless()
21
    {
22
        return false;
23
    }
24
25 4
    protected function getOptionDefinitionsExtra()
26
    {
27
        return [
28 4
            ['key', 'string', '', true, true]
29
        ];
30
    }
31
32
    /**
33
     * Get api key from options or environment variable
34
     *
35
     * @return string|false  api key or false if none is set
36
     */
37 4
    private function getKey()
38
    {
39 4
        if (!empty($this->options['key'])) {
40 3
            return $this->options['key'];
41
        }
42 1
        if (!empty(getenv('EWWW_API_KEY'))) {
43 1
            return getenv('EWWW_API_KEY');
44
        }
45
        return false;
46
    }
47
48
49
    /**
50
     * Check operationality of Ewww converter.
51
     *
52
     * @throws SystemRequirementsNotMetException  if system requirements are not met (curl)
53
     * @throws ConverterNotOperationalException   if key is missing or invalid, or quota has exceeded
54
     */
55 3
    public function checkOperationality()
56
    {
57
        // First check for curl requirements
58 3
        parent::checkOperationality();
59
60 3
        $apiKey = $this->getKey();
61
62 3
        if ($apiKey === false) {
63
            throw new InvalidApiKeyException('Missing API key.');
64
        }
65
66 3
        if (strlen($apiKey) < 20) {
67 1
            throw new InvalidApiKeyException(
68 1
                'Key is invalid. Keys are supposed to be 32 characters long - your key is much shorter'
69
            );
70
        }
71
72 2
        $keyStatus = self::getKeyStatus($apiKey);
73
        switch ($keyStatus) {
74 2
            case 'great':
75 1
                break;
76 1
            case 'exceeded':
77
                throw new ConverterNotOperationalException('quota has exceeded');
78
                break;
79 1
            case 'invalid':
80 1
                throw new InvalidApiKeyException('key is invalid');
81
                break;
82
        }
83 1
    }
84
85
    // Although this method is public, do not call directly.
86
    // You should rather call the static convert() function, defined in AbstractConverter, which
87
    // takes care of preparing stuff before calling doConvert, and validating after.
88 2
    protected function doActualConvert()
89
    {
90
91 2
        $options = $this->options;
92
93 2
        $ch = self::initCurl();
94
95
        //$this->logLn('api key:' . $this->getKey());
96
97
        $postData = [
98 2
            'api_key' => $this->getKey(),
99 2
            'webp' => '1',
100 2
            'file' => curl_file_create($this->source),
101 2
            'quality' => $this->getCalculatedQuality(),
102 2
            'metadata' => ($options['metadata'] == 'none' ? '0' : '1')
103
        ];
104
105 2
        curl_setopt_array(
106 2
            $ch,
107
            [
108 2
            CURLOPT_URL => "https://optimize.exactlywww.com/v2/",
109 2
            CURLOPT_HTTPHEADER => [
110
                'User-Agent: WebPConvert',
111
                'Accept: image/*'
112
            ],
113 2
            CURLOPT_POSTFIELDS => $postData,
114 2
            CURLOPT_BINARYTRANSFER => true,
115 2
            CURLOPT_RETURNTRANSFER => true,
116 2
            CURLOPT_HEADER => false,
117 2
            CURLOPT_SSL_VERIFYPEER => false
118
            ]
119
        );
120
121 2
        $response = curl_exec($ch);
122
123 2
        if (curl_errno($ch)) {
124
            throw new ConversionFailedException(curl_error($ch));
125
        }
126
127
        // The API does not always return images.
128
        // For example, it may return a message such as '{"error":"invalid","t":"exceeded"}
129
        // Messages has a http content type of ie 'text/html; charset=UTF-8
130
        // Images has application/octet-stream.
131
        // So verify that we got an image back.
132 2
        if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) != 'application/octet-stream') {
133
            //echo curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
134 1
            curl_close($ch);
135
136
            /* May return this: {"error":"invalid","t":"exceeded"} */
137 1
            $responseObj = json_decode($response);
138 1
            if (isset($responseObj->error)) {
139
                //echo 'error:' . $responseObj->error . '<br>';
140
                //echo $response;
141
                //self::blacklistKey($key);
142
                //throw new SystemRequirementsNotMetException('The key is invalid. Blacklisted it!');
143 1
                throw new InvalidApiKeyException('The key is invalid');
144
            }
145
146
            throw new ConversionFailedException(
147
                'ewww api did not return an image. It could be that the key is invalid. Response: '
148
                . $response
149
            );
150
        }
151
152
        // Not sure this can happen. So just in case
153 1
        if ($response == '') {
154
            throw new ConversionFailedException('ewww api did not return anything');
155
        }
156
157 1
        $success = file_put_contents($this->destination, $response);
158
159 1
        if (!$success) {
160
            throw new ConversionFailedException('Error saving file');
161
        }
162 1
    }
163
164
    /**
165
     *  Keep subscription alive by optimizing a jpeg
166
     *  (ewww closes accounts after 6 months of inactivity - and webp conversions seems not to be counted? )
167
     */
168
    public static function keepSubscriptionAlive($source, $key)
169
    {
170
        try {
171
            $ch = curl_init();
172
        } catch (\Exception $e) {
173
            return 'curl is not installed';
174
        }
175
        if ($ch === false) {
176
            return 'curl could not be initialized';
177
        }
178
        curl_setopt_array(
179
            $ch,
180
            [
181
            CURLOPT_URL => "https://optimize.exactlywww.com/v2/",
182
            CURLOPT_HTTPHEADER => [
183
                'User-Agent: WebPConvert',
184
                'Accept: image/*'
185
            ],
186
            CURLOPT_POSTFIELDS => [
187
                'api_key' => $key,
188
                'webp' => '0',
189
                'file' => curl_file_create($source),
190
                'domain' => $_SERVER['HTTP_HOST'],
191
                'quality' => 60,
192
                'metadata' => 0
193
            ],
194
            CURLOPT_BINARYTRANSFER => true,
195
            CURLOPT_RETURNTRANSFER => true,
196
            CURLOPT_HEADER => false,
197
            CURLOPT_SSL_VERIFYPEER => false
198
            ]
199
        );
200
201
        $response = curl_exec($ch);
202
        if (curl_errno($ch)) {
203
            return 'curl error' . curl_error($ch);
204
        }
205
        if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) != 'application/octet-stream') {
206
            curl_close($ch);
207
208
            /* May return this: {"error":"invalid","t":"exceeded"} */
209
            $responseObj = json_decode($response);
210
            if (isset($responseObj->error)) {
211
                return 'The key is invalid';
212
            }
213
214
            return 'ewww api did not return an image. It could be that the key is invalid. Response: ' . $response;
215
        }
216
217
        // Not sure this can happen. So just in case
218
        if ($response == '') {
219
            return 'ewww api did not return anything';
220
        }
221
222
        return true;
223
    }
224
225
    /*
226
        public static function blacklistKey($key)
227
        {
228
        }
229
230
        public static function isKeyBlacklisted($key)
231
        {
232
        }*/
233
234
    /**
235
     *  Return "great", "exceeded" or "invalid"
236
     */
237 4
    public static function getKeyStatus($key)
238
    {
239 4
        $ch = self::initCurl();
240
241 4
        curl_setopt($ch, CURLOPT_URL, "https://optimize.exactlywww.com/verify/");
242 4
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
243 4
        curl_setopt(
244 4
            $ch,
245 4
            CURLOPT_POSTFIELDS,
246
            [
247 4
            'api_key' => $key
248
            ]
249
        );
250
251
        // The 403 forbidden is avoided with this line.
252 4
        curl_setopt(
253 4
            $ch,
254 4
            CURLOPT_USERAGENT,
255 4
            'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)'
256
        );
257
258 4
        $response = curl_exec($ch);
259
        // echo $response;
260 4
        if (curl_errno($ch)) {
261
            throw new \Exception(curl_error($ch));
262
        }
263 4
        curl_close($ch);
264
265
        // Possible responses:
266
        // “great” = verification successful
267
        // “exceeded” = indicates a valid key with no remaining image credits.
268
        // an empty response indicates that the key is not valid
269
270 4
        if ($response == '') {
271
            return 'invalid';
272
        }
273 4
        $responseObj = json_decode($response);
274 4
        if (isset($responseObj->error)) {
275 3
            if ($responseObj->error == 'invalid') {
276 3
                return 'invalid';
277
            } else {
278
                throw new \Exception('Ewww returned unexpected error: ' . $response);
279
            }
280
        }
281 3
        if (!isset($responseObj->status)) {
282
            throw new \Exception('Ewww returned unexpected response to verify request: ' . $response);
283
        }
284 3
        switch ($responseObj->status) {
285 3
            case 'great':
286
            case 'exceeded':
287 3
                return $responseObj->status;
288
        }
289
        throw new \Exception('Ewww returned unexpected status to verify request: "' . $responseObj->status . '"');
290
    }
291
292 1
    public static function isWorkingKey($key)
293
    {
294 1
        return (self::getKeyStatus($key) == 'great');
295
    }
296
297 1
    public static function isValidKey($key)
298
    {
299 1
        return (self::getKeyStatus($key) != 'invalid');
300
    }
301
302
    public static function getQuota($key)
303
    {
304
        $ch = self::initCurl();
305
306
        curl_setopt($ch, CURLOPT_URL, "https://optimize.exactlywww.com/quota/");
307
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
308
        curl_setopt(
309
            $ch,
310
            CURLOPT_POSTFIELDS,
311
            [
312
            'api_key' => $key
313
            ]
314
        );
315
        curl_setopt(
316
            $ch,
317
            CURLOPT_USERAGENT,
318
            'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.0.3705; .NET CLR 1.1.4322)'
319
        );
320
321
        $response = curl_exec($ch);
322
        return $response; // ie -830 23. Seems to return empty for invalid keys
323
        // or empty
324
        //echo $response;
325
    }
326
}
327