Completed
Push — master ( 41c0c2...358625 )
by Bjørn
04:46 queued 11s
created

Ewww::doActualConvert()   B

Complexity

Conditions 8
Paths 14

Size

Total Lines 77
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 8.1715

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 8
eloc 39
c 3
b 0
f 0
nc 14
nop 0
dl 0
loc 77
ccs 31
cts 36
cp 0.8611
crap 8.1715
rs 8.0515

How to fix   Long Method   

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 api keys discovered during conversions (only stored during the request)  */
28
    public static $invalidApiKeysDiscoveredDuringConversion = [];
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 1
            return getenv('WEBPCONVERT_EWWW_API_KEY');
66
        }
67
        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
            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
            throw new InvalidApiKeyException('Missing API key.');
91
        }
92
93 3
        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 2
        $this->checkOperationalityForCurlTrait();
102
103 2
        if ($this->options['check-key-status-before-converting']) {
104 2
            $keyStatus = self::getKeyStatus($apiKey);
105
            switch ($keyStatus) {
106 2
                case 'great':
107 1
                    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 1
    }
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 2
    protected function doActualConvert()
130
    {
131
132 2
        $options = $this->options;
133
134 2
        $ch = self::initCurl();
135
136
        //$this->logLn('api key:' . $this->getKey());
137
138
        $postData = [
139 2
            'api_key' => $this->getKey(),
140 2
            'webp' => '1',
141 2
            'file' => curl_file_create($this->source),
142 2
            'quality' => $this->getCalculatedQuality(),
143 2
            'metadata' => ($options['metadata'] == 'none' ? '0' : '1')
144
        ];
145
146 2
        curl_setopt_array(
147 2
            $ch,
148
            [
149 2
            CURLOPT_URL => "https://optimize.exactlywww.com/v2/",
150 2
            CURLOPT_HTTPHEADER => [
151
                'User-Agent: WebPConvert',
152
                'Accept: image/*'
153
            ],
154 2
            CURLOPT_POSTFIELDS => $postData,
155 2
            CURLOPT_BINARYTRANSFER => true,
156 2
            CURLOPT_RETURNTRANSFER => true,
157 2
            CURLOPT_HEADER => false,
158 2
            CURLOPT_SSL_VERIFYPEER => false
159
            ]
160
        );
161
162 2
        $response = curl_exec($ch);
163
164 2
        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 2
        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
            /* May ie return this: {"error":"invalid","t":"exceeded"} */
178 1
            $responseObj = json_decode($response);
179 1
            if (isset($responseObj->error)) {
180 1
                $this->logLn('We received the following error response: ' . $responseObj->error);
181 1
                $this->logLn('Complete response: ' . json_encode($responseObj));
182
183
                // Store the invalid key in array so it can be received once the Stack is completed
184
                // (even when stack succeeds)
185 1
                if (!in_array($options['api-key'], self::$invalidApiKeysDiscoveredDuringConversion)) {
186 1
                    self::$invalidApiKeysDiscoveredDuringConversion[] = $options['api-key'];
187
                }
188 1
                throw new InvalidApiKeyException('The api key is invalid!');
189
            }
190
191
            throw new ConversionFailedException(
192
                'ewww api did not return an image. It could be that the key is invalid. Response: '
193
                . $response
194
            );
195
        }
196
197
        // Not sure this can happen. So just in case
198 1
        if ($response == '') {
199
            throw new ConversionFailedException('ewww api did not return anything');
200
        }
201
202 1
        $success = file_put_contents($this->destination, $response);
203
204 1
        if (!$success) {
205
            throw new ConversionFailedException('Error saving file');
206
        }
207 1
    }
208
209
    /**
210
     *  Keep subscription alive by optimizing a jpeg
211
     *  (ewww closes accounts after 6 months of inactivity - and webp conversions seems not to be counted? )
212
     */
213
    public static function keepSubscriptionAlive($source, $key)
214
    {
215
        try {
216
            $ch = curl_init();
217
        } catch (\Exception $e) {
218
            return 'curl is not installed';
219
        }
220
        if ($ch === false) {
221
            return 'curl could not be initialized';
222
        }
223
        curl_setopt_array(
224
            $ch,
225
            [
226
            CURLOPT_URL => "https://optimize.exactlywww.com/v2/",
227
            CURLOPT_HTTPHEADER => [
228
                'User-Agent: WebPConvert',
229
                'Accept: image/*'
230
            ],
231
            CURLOPT_POSTFIELDS => [
232
                'api_key' => $key,
233
                'webp' => '0',
234
                'file' => curl_file_create($source),
235
                'domain' => $_SERVER['HTTP_HOST'],
236
                'quality' => 60,
237
                'metadata' => 0
238
            ],
239
            CURLOPT_BINARYTRANSFER => true,
240
            CURLOPT_RETURNTRANSFER => true,
241
            CURLOPT_HEADER => false,
242
            CURLOPT_SSL_VERIFYPEER => false
243
            ]
244
        );
245
246
        $response = curl_exec($ch);
247
        if (curl_errno($ch)) {
248
            return 'curl error' . curl_error($ch);
249
        }
250
        if (curl_getinfo($ch, CURLINFO_CONTENT_TYPE) != 'application/octet-stream') {
251
            curl_close($ch);
252
253
            /* May return this: {"error":"invalid","t":"exceeded"} */
254
            $responseObj = json_decode($response);
255
            if (isset($responseObj->error)) {
256
                return 'The key is invalid';
257
            }
258
259
            return 'ewww api did not return an image. It could be that the key is invalid. Response: ' . $response;
260
        }
261
262
        // Not sure this can happen. So just in case
263
        if ($response == '') {
264
            return 'ewww api did not return anything';
265
        }
266
267
        return true;
268
    }
269
270
    /*
271
        public static function blacklistKey($key)
272
        {
273
        }
274
275
        public static function isKeyBlacklisted($key)
276
        {
277
        }*/
278
279
    /**
280
     *  Return "great", "exceeded" or "invalid"
281
     */
282 4
    public static function getKeyStatus($key)
283
    {
284 4
        $ch = self::initCurl();
285
286 4
        curl_setopt($ch, CURLOPT_URL, "https://optimize.exactlywww.com/verify/");
287 4
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
288 4
        curl_setopt($ch, CURLOPT_POSTFIELDS, [
289 4
            'api_key' => $key
290
        ]);
291
292 4
        curl_setopt($ch, CURLOPT_USERAGENT, 'WebPConvert');
293
294 4
        $response = curl_exec($ch);
295
        // echo $response;
296 4
        if (curl_errno($ch)) {
297
            throw new \Exception(curl_error($ch));
298
        }
299 4
        curl_close($ch);
300
301
        // Possible responses:
302
        // “great” = verification successful
303
        // “exceeded” = indicates a valid key with no remaining image credits.
304
        // an empty response indicates that the key is not valid
305
306 4
        if ($response == '') {
307
            return 'invalid';
308
        }
309 4
        $responseObj = json_decode($response);
310 4
        if (isset($responseObj->error)) {
311 3
            if ($responseObj->error == 'invalid') {
312 3
                return 'invalid';
313
            } else {
314
                throw new \Exception('Ewww returned unexpected error: ' . $response);
315
            }
316
        }
317 3
        if (!isset($responseObj->status)) {
318
            throw new \Exception('Ewww returned unexpected response to verify request: ' . $response);
319
        }
320 3
        switch ($responseObj->status) {
321 3
            case 'great':
322
            case 'exceeded':
323 3
                return $responseObj->status;
324
        }
325
        throw new \Exception('Ewww returned unexpected status to verify request: "' . $responseObj->status . '"');
326
    }
327
328 1
    public static function isWorkingKey($key)
329
    {
330 1
        return (self::getKeyStatus($key) == 'great');
331
    }
332
333 1
    public static function isValidKey($key)
334
    {
335 1
        return (self::getKeyStatus($key) != 'invalid');
336
    }
337
338
    public static function getQuota($key)
339
    {
340
        $ch = self::initCurl();
341
342
        curl_setopt($ch, CURLOPT_URL, "https://optimize.exactlywww.com/quota/");
343
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
344
        curl_setopt($ch, CURLOPT_POSTFIELDS, [
345
            'api_key' => $key
346
        ]);
347
        curl_setopt($ch, CURLOPT_USERAGENT, 'WebPConvert');
348
349
        $response = curl_exec($ch);
350
        return $response; // ie -830 23. Seems to return empty for invalid keys
351
        // or empty
352
        //echo $response;
353
    }
354
}
355