Passed
Push — master ( 5cb744...41c0c2 )
by Bjørn
01:36 queued 11s
created

Ewww::doActualConvert()   B

Complexity

Conditions 7
Paths 12

Size

Total Lines 73
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 7.7656

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 7
eloc 35
c 2
b 0
f 0
nc 12
nop 0
dl 0
loc 73
ccs 24
cts 32
cp 0.75
crap 7.7656
rs 8.4266

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