PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.2.22
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.2.22
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 / AbstractConverter.php

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

389 lines 13.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // TODO:
4 // Read this: https://sourcemaking.com/design_patterns/strategy
5
6 namespace WebPConvert\Convert\Converters;
7
8 use WebPConvert\Helpers\InputValidator;
9 use WebPConvert\Helpers\MimeType;
10 use WebPConvert\Convert\Exceptions\ConversionFailedException;
11 use WebPConvert\Convert\Converters\BaseTraits\AutoQualityTrait;
12 use WebPConvert\Convert\Converters\BaseTraits\DestinationPreparationTrait;
13 use WebPConvert\Convert\Converters\BaseTraits\LoggerTrait;
14 use WebPConvert\Convert\Converters\BaseTraits\OptionsTrait;
15 use WebPConvert\Convert\Converters\BaseTraits\WarningLoggerTrait;
16 use WebPConvert\Exceptions\WebPConvertException;
17 use WebPConvert\Loggers\BaseLogger;
18
19 /**
20 * Base for all converter classes.
21 *
22 * @package WebPConvert
23 * @author Bjørn Rosell <[email protected]>
24 * @since Class available since Release 2.0.0
25 */
26 abstract class AbstractConverter
27 {
28 use AutoQualityTrait;
29 use OptionsTrait;
30 use WarningLoggerTrait;
31 use DestinationPreparationTrait;
32 use LoggerTrait;
33
34 /**
35 * The actual conversion is be done by a concrete converter extending this class.
36 *
37 * At the stage this method is called, the abstract converter has taken preparational steps.
38 * - It has created the destination folder (if neccesary)
39 * - It has checked the input (valid mime type)
40 * - It has set up an error handler, mostly in order to catch and log warnings during the doConvert fase
41 *
42 * Note: This method is not meant to be called from the outside. Use the static *convert* method for converting
43 * or, if you wish, create an instance with ::createInstance() and then call ::doConvert()
44 *
45 * @throws ConversionFailedException in case conversion failed in an antipiciated way (or subclass)
46 * @throws \Exception in case conversion failed in an unantipiciated way
47 */
48 abstract protected function doActualConvert();
49
50 /**
51 * Whether or not the converter supports lossless encoding (even for jpegs)
52 *
53 * PS: Converters that supports lossless encoding all use the EncodingAutoTrait, which
54 * overrides this function.
55 *
56 * @return boolean Whether the converter supports lossless encoding (even for jpegs).
57 */
58 public function supportsLossless()
59 {
60 return false;
61 }
62
63 /** @var string The filename of the image to convert (complete path) */
64 protected $source;
65
66 /** @var string Where to save the webp (complete path) */
67 protected $destination;
68
69 /**
70 * Check basis operationality
71 *
72 * Converters may override this method for the purpose of performing basic operationaly checks. It is for
73 * running general operation checks for a conversion method.
74 * If some requirement is not met, it should throw a ConverterNotOperationalException (or subtype)
75 *
76 * The method is called internally right before calling doActualConvert() method.
77 * - It SHOULD take options into account when relevant. For example, a missing api key for a
78 * cloud converter should be detected here
79 * - It should NOT take the actual filename into consideration, as the purpose is *general*
80 * For that pupose, converters should override checkConvertability
81 * Also note that doConvert method is allowed to throw ConverterNotOperationalException too.
82 *
83 * @return void
84 */
85 public function checkOperationality()
86 {
87 }
88
89 /**
90 * Converters may override this for the purpose of performing checks on the concrete file.
91 *
92 * This can for example be used for rejecting big uploads in cloud converters or rejecting unsupported
93 * image types.
94 *
95 * @return void
96 */
97 public function checkConvertability()
98 {
99 }
100
101 /**
102 * Constructor.
103 *
104 * @param string $source path to source file
105 * @param string $destination path to destination
106 * @param array $options (optional) options for conversion
107 * @param BaseLogger $logger (optional)
108 */
109 final public function __construct($source = '', $destination = '', $options = [], $logger = null)
110 {
111 if ($source == '') {
112 return;
113 }
114 InputValidator::checkSourceAndDestination($source, $destination);
115
116 $this->source = $source;
117 $this->destination = $destination;
118
119 $this->setLogger($logger);
120 $this->setProvidedOptions($options);
121
122 if (!isset($this->options['_skip_input_check'])) {
123 $this->logLn('WebP Convert 2.9.0 ignited', 'bold');
124 $this->logLn('PHP version: ' . phpversion());
125 if (isset($_SERVER['SERVER_SOFTWARE'])) {
126 $this->logLn('Server software: ' . $_SERVER['SERVER_SOFTWARE']);
127 }
128 $this->logLn('');
129 if (isset($this->options['log-call-arguments']) && $this->options['log-call-arguments']) {
130 $this->logLn('source: ' . $this->source);
131 $this->logLn('destination: ' . $this->destination);
132 $this->logLn('');
133 }
134
135 $this->logLn(self::getConverterDisplayName() . ' converter ignited', 'bold');
136 }
137 }
138
139 /**
140 * Get source.
141 *
142 * @return string The source.
143 */
144 public function getSource()
145 {
146 return $this->source;
147 }
148
149 /**
150 * Get destination.
151 *
152 * @return string The destination.
153 */
154 public function getDestination()
155 {
156 return $this->destination;
157 }
158
159 /**
160 * Set destination.
161 *
162 * @param string $destination path to destination
163 * @return void
164 */
165 public function setDestination($destination)
166 {
167 $this->destination = $destination;
168 }
169
170
171 /**
172 * Get converter name for display (defaults to the class name (short)).
173 *
174 * Converters can override this.
175 *
176 * @return string A display name, ie "Gd"
177 */
178 protected static function getConverterDisplayName()
179 {
180 // https://stackoverflow.com/questions/19901850/how-do-i-get-an-objects-unqualified-short-class-name/25308464
181 return substr(strrchr('\\' . static::class, '\\'), 1);
182 }
183
184
185 /**
186 * Get converter id (defaults to the class name lowercased)
187 *
188 * Converters can override this.
189 *
190 * @return string A display name, ie "Gd"
191 */
192 protected static function getConverterId()
193 {
194 return strtolower(self::getConverterDisplayName());
195 }
196
197
198 /**
199 * Create an instance of this class
200 *
201 * @param string $source The path to the file to convert
202 * @param string $destination The path to save the converted file to
203 * @param array $options (optional)
204 * @param \WebPConvert\Loggers\BaseLogger $logger (optional)
205 *
206 * @return static
207 */
208 public static function createInstance($source, $destination, $options = [], $logger = null)
209 {
210 return new static($source, $destination, $options, $logger);
211 }
212
213 protected function logReduction($source, $destination)
214 {
215 if (!file_exists($source) || !file_exists($destination)) return;
216 $sourceSize = filesize($source);
217 $destSize = filesize($destination);
218 $this->log(round(($sourceSize - $destSize) / $sourceSize * 100) . '% ');
219 if ($sourceSize < 10000) {
220 $this->logLn('(went from ' . strval($sourceSize) . ' bytes to ' . strval($destSize) . ' bytes)');
221 } else {
222 $this->logLn('(went from ' . round($sourceSize / 1024) . ' kb to ' . round($destSize / 1024) . ' kb)');
223 }
224 }
225
226 /**
227 * Run conversion.
228 *
229 * @return void
230 */
231 private function doConvertImplementation()
232 {
233 $beginTime = microtime(true);
234
235 $this->activateWarningLogger();
236
237 $this->checkOptions();
238
239 // Prepare destination folder
240 $this->createWritableDestinationFolder();
241 $this->removeExistingDestinationIfExists();
242
243 if (!isset($this->options['_skip_input_check'])) {
244 // Check that a file can be written to destination
245 $this->checkDestinationWritable();
246 }
247
248 $this->checkOperationality();
249 $this->checkConvertability();
250
251 if ($this->options['log-call-arguments']) {
252 $this->logOptions();
253 $this->logLn('');
254 }
255
256 $this->runActualConvert();
257
258 $source = $this->source;
259 $destination = $this->destination;
260
261 if (!@file_exists($destination)) {
262 throw new ConversionFailedException('Destination file is not there: ' . $destination);
263 } elseif (@filesize($destination) === 0) {
264 unlink($destination);
265 throw new ConversionFailedException('Destination file was completely empty');
266 } else {
267 if (!isset($this->options['_suppress_success_message'])) {
268 $this->ln();
269 $this->log('Converted image in ' . round((microtime(true) - $beginTime) * 1000) . ' ms');
270
271 $sourceSize = @filesize($source);
272 if ($sourceSize !== false) {
273 $this->log(', reducing file size with ');
274 $this->logReduction($source, $destination);
275 }
276 }
277 }
278
279 $this->deactivateWarningLogger();
280 }
281
282 //private function logEx
283 /**
284 * Start conversion.
285 *
286 * Usually it would be more convenience to call the static convert method, but alternatively you can call
287 * call ::createInstance to get an instance and then ::doConvert().
288 *
289 * @return void
290 */
291 public function doConvert()
292 {
293 try {
294 //trigger_error('hello', E_USER_ERROR);
295 $this->doConvertImplementation();
296 } catch (WebPConvertException $e) {
297 $this->logLn('');
298 /*
299 if (isset($e->description) && ($e->description != '')) {
300 $this->log('Error: ' . $e->description . '. ', 'bold');
301 } else {
302 $this->log('Error: ', 'bold');
303 }
304 */
305 $this->log('Error: ', 'bold');
306 $this->logLn($e->getMessage(), 'bold');
307 throw $e;
308 } catch (\Exception $e) {
309 $className = get_class($e);
310 $classNameParts = explode("\\", $className);
311 $shortClassName = array_pop($classNameParts);
312
313 $this->logLn('');
314 $this->logLn($shortClassName . ' thrown in ' . $e->getFile() . ':' . $e->getLine(), 'bold');
315 $this->logLn('Message: "' . $e->getMessage() . '"', 'bold');
316
317 $this->logLn('Trace:');
318 foreach ($e->getTrace() as $trace) {
319 //$this->logLn(print_r($trace, true));
320 if (isset($trace['file']) && isset($trace['line'])) {
321 $this->logLn(
322 $trace['file'] . ':' . $trace['line']
323 );
324 }
325 }
326 throw $e;
327 } catch (\Throwable $e) {
328 $className = get_class($e);
329 $classNameParts = explode("\\", $className);
330 $shortClassName = array_pop($classNameParts);
331
332 $this->logLn('');
333 $this->logLn($shortClassName . ' thrown in ' . $e->getFile() . ':' . $e->getLine(), 'bold');
334 $this->logLn('Message: "' . $e->getMessage() . '"', 'bold');
335 throw $e;
336 }
337 }
338
339 /**
340 * Runs the actual conversion (after setup and checks)
341 * Simply calls the doActualConvert() of the actual converter.
342 * However, in the EncodingAutoTrait, this method is overridden to make two conversions
343 * and select the smallest.
344 *
345 * @return void
346 */
347 protected function runActualConvert()
348 {
349 $this->doActualConvert();
350 }
351
352 /**
353 * Convert an image to webp.
354 *
355 * @param string $source path to source file
356 * @param string $destination path to destination
357 * @param array $options (optional) options for conversion
358 * @param BaseLogger $logger (optional)
359 *
360 * @throws ConversionFailedException in case conversion fails in an antipiciated way
361 * @throws \Exception in case conversion fails in an unantipiciated way
362 * @return void
363 */
364 public static function convert($source, $destination, $options = [], $logger = null)
365 {
366 $c = self::createInstance($source, $destination, $options, $logger);
367 $c->doConvert();
368 //echo $instance->id;
369 }
370
371 /**
372 * Get mime type for image (best guess).
373 *
374 * It falls back to using file extension. If that fails too, false is returned
375 *
376 * PS: Is it a security risk to fall back on file extension?
377 * - By setting file extension to "jpg", one can lure our library into trying to convert a file, which isn't a jpg.
378 * hmm, seems very unlikely, though not unthinkable that one of the converters could be exploited
379 *
380 * @return string|false|null mimetype (if it is an image, and type could be determined / guessed),
381 * false (if it is not an image type that the server knowns about)
382 * or null (if nothing can be determined)
383 */
384 public function getMimeTypeOfSource()
385 {
386 return MimeType::getMimeTypeDetectionResult($this->source);
387 }
388 }
389