Passed
Push — master ( 47232b...207a99 )
by Bjørn
01:36 queued 12s
created

Ewww::doActualConvert()   B

Complexity

Conditions 10
Paths 26

Size

Total Lines 87
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 11.139

Importance

Changes 5
Bugs 0 Features 0
Metric Value
cc 10
eloc 44
c 5
b 0
f 0
nc 26
nop 0
dl 0
loc 87
ccs 31
cts 40
cp 0.775
crap 11.139
rs 7.6666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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