PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 4.7.0
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v4.7.0
4.7.0 4.6.1 4.6.0 4.5.5 4.5.4 4.5.3 4.5.2 3.2.20 3.2.21 3.2.22 3.2.3 3.2.5 3.2.6 3.2.7 3.2.9 3.3.0 3.3.1 3.3.2 3.4.0 3.4.1 3.4.2 3.5.0 3.6.0 3.7.0 3.7.1 All 111 releases
wp-optimize / vendor / rosell-dk / webp-convert / src / Convert / Converters / Ewww.php

Ewww.php in WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance 4.7.0, at vendor/rosell-dk/webp-convert/src/Convert/Converters/Ewww.php

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