PluginProbe
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance / 3.2.7
WP-Optimize – Cache, Compress images, Minify & Clean database to boost page speed & performance v3.2.7
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.7, at vendor/rosell-dk/webp-convert/src/Convert/Converters/AbstractConverter.php

388 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 $sourceSize = filesize($source);
216 $destSize = filesize($destination);
217 $this->log(round(($sourceSize - $destSize) / $sourceSize * 100) . '% ');
218 if ($sourceSize < 10000) {
219 $this->logLn('(went from ' . strval($sourceSize) . ' bytes to ' . strval($destSize) . ' bytes)');
220 } else {
221 $this->logLn('(went from ' . round($sourceSize / 1024) . ' kb to ' . round($destSize / 1024) . ' kb)');
222 }
223 }
224
225 /**
226 * Run conversion.
227 *
228 * @return void
229 */
230 private function doConvertImplementation()
231 {
232 $beginTime = microtime(true);
233
234 $this->activateWarningLogger();
235
236 $this->checkOptions();
237
238 // Prepare destination folder
239 $this->createWritableDestinationFolder();
240 $this->removeExistingDestinationIfExists();
241
242 if (!isset($this->options['_skip_input_check'])) {
243 // Check that a file can be written to destination
244 $this->checkDestinationWritable();
245 }
246
247 $this->checkOperationality();
248 $this->checkConvertability();
249
250 if ($this->options['log-call-arguments']) {
251 $this->logOptions();
252 $this->logLn('');
253 }
254
255 $this->runActualConvert();
256
257 $source = $this->source;
258 $destination = $this->destination;
259
260 if (!@file_exists($destination)) {
261 throw new ConversionFailedException('Destination file is not there: ' . $destination);
262 } elseif (@filesize($destination) === 0) {
263 unlink($destination);
264 throw new ConversionFailedException('Destination file was completely empty');
265 } else {
266 if (!isset($this->options['_suppress_success_message'])) {
267 $this->ln();
268 $this->log('Converted image in ' . round((microtime(true) - $beginTime) * 1000) . ' ms');
269
270 $sourceSize = @filesize($source);
271 if ($sourceSize !== false) {
272 $this->log(', reducing file size with ');
273 $this->logReduction($source, $destination);
274 }
275 }
276 }
277
278 $this->deactivateWarningLogger();
279 }
280
281 //private function logEx
282 /**
283 * Start conversion.
284 *
285 * Usually it would be more convenience to call the static convert method, but alternatively you can call
286 * call ::createInstance to get an instance and then ::doConvert().
287 *
288 * @return void
289 */
290 public function doConvert()
291 {
292 try {
293 //trigger_error('hello', E_USER_ERROR);
294 $this->doConvertImplementation();
295 } catch (WebPConvertException $e) {
296 $this->logLn('');
297 /*
298 if (isset($e->description) && ($e->description != '')) {
299 $this->log('Error: ' . $e->description . '. ', 'bold');
300 } else {
301 $this->log('Error: ', 'bold');
302 }
303 */
304 $this->log('Error: ', 'bold');
305 $this->logLn($e->getMessage(), 'bold');
306 throw $e;
307 } catch (\Exception $e) {
308 $className = get_class($e);
309 $classNameParts = explode("\\", $className);
310 $shortClassName = array_pop($classNameParts);
311
312 $this->logLn('');
313 $this->logLn($shortClassName . ' thrown in ' . $e->getFile() . ':' . $e->getLine(), 'bold');
314 $this->logLn('Message: "' . $e->getMessage() . '"', 'bold');
315
316 $this->logLn('Trace:');
317 foreach ($e->getTrace() as $trace) {
318 //$this->logLn(print_r($trace, true));
319 if (isset($trace['file']) && isset($trace['line'])) {
320 $this->logLn(
321 $trace['file'] . ':' . $trace['line']
322 );
323 }
324 }
325 throw $e;
326 } catch (\Throwable $e) {
327 $className = get_class($e);
328 $classNameParts = explode("\\", $className);
329 $shortClassName = array_pop($classNameParts);
330
331 $this->logLn('');
332 $this->logLn($shortClassName . ' thrown in ' . $e->getFile() . ':' . $e->getLine(), 'bold');
333 $this->logLn('Message: "' . $e->getMessage() . '"', 'bold');
334 throw $e;
335 }
336 }
337
338 /**
339 * Runs the actual conversion (after setup and checks)
340 * Simply calls the doActualConvert() of the actual converter.
341 * However, in the EncodingAutoTrait, this method is overridden to make two conversions
342 * and select the smallest.
343 *
344 * @return void
345 */
346 protected function runActualConvert()
347 {
348 $this->doActualConvert();
349 }
350
351 /**
352 * Convert an image to webp.
353 *
354 * @param string $source path to source file
355 * @param string $destination path to destination
356 * @param array $options (optional) options for conversion
357 * @param BaseLogger $logger (optional)
358 *
359 * @throws ConversionFailedException in case conversion fails in an antipiciated way
360 * @throws \Exception in case conversion fails in an unantipiciated way
361 * @return void
362 */
363 public static function convert($source, $destination, $options = [], $logger = null)
364 {
365 $c = self::createInstance($source, $destination, $options, $logger);
366 $c->doConvert();
367 //echo $instance->id;
368 }
369
370 /**
371 * Get mime type for image (best guess).
372 *
373 * It falls back to using file extension. If that fails too, false is returned
374 *
375 * PS: Is it a security risk to fall back on file extension?
376 * - By setting file extension to "jpg", one can lure our library into trying to convert a file, which isn't a jpg.
377 * hmm, seems very unlikely, though not unthinkable that one of the converters could be exploited
378 *
379 * @return string|false|null mimetype (if it is an image, and type could be determined / guessed),
380 * false (if it is not an image type that the server knowns about)
381 * or null (if nothing can be determined)
382 */
383 public function getMimeTypeOfSource()
384 {
385 return MimeType::getMimeTypeDetectionResult($this->source);
386 }
387 }
388