PluginProbe ʕ •ᴥ•ʔ
Gallery : FooGallery / 3.3.2
Gallery : FooGallery v3.3.2
3.3.2 3.3.3 3.3.0 3.1.31 3.1.32 3.1.34 3.1.36 3.2.0 3.2.6 trunk 1.10.3 2.0.24 2.1.34 2.2.44 2.3.3 2.4.32 3.0.6 3.1.11 3.1.12 3.1.13 3.1.20 3.1.25 3.1.26 3.1.26.1 3.1.26.2
foogallery / includes / thumbs / default / class-foogallery-thumb-generator.php
foogallery / includes / thumbs / default Last commit date
class-foogallery-thumb-engine-default.php 3 days ago class-foogallery-thumb-generator-background-fill.php 3 days ago class-foogallery-thumb-generator.php 3 days ago
class-foogallery-thumb-generator.php
637 lines
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) {
4 exit;
5 }
6
7 /**
8 * Class that performs all thumbnail generation within FooGallery
9 */
10 if ( ! class_exists( 'FooGallery_Thumb_Generator' ) ) {
11 class FooGallery_Thumb_Generator {
12
13 /**
14 * Array of resize arguments
15 *
16 * @var array
17 * @access private
18 */
19 private $args;
20
21 /**
22 * The file path to the original image
23 *
24 * @var string
25 * @access private
26 */
27 private $file_path;
28
29 /**
30 * The original image URL
31 *
32 * @var string
33 * @access private
34 */
35 private $image_url;
36
37 /**
38 * Any errors we encountered
39 *
40 * @var WP_Error
41 * @access private
42 */
43 private $error;
44
45 /**
46 * Is there an error
47 *
48 * @access public
49 * @return null
50 */
51 public function errored() {
52 return ! empty( $this->error );
53 }
54
55 /**
56 * Returns the error
57 *
58 * @access public
59 * @return null
60 */
61 public function error() {
62 return empty( $this->error ) ? null : $this->error;
63 }
64
65 /**
66 * Constructor to parse the args and determine cache folder
67 *
68 * @access public
69 *
70 * @param string $image_url
71 * @param array $args . (default: array())
72 * @param bool $override_args
73 */
74 public function __construct( $image_url, $args = array(), $override_args = false ) {
75 $this->image_url = $image_url;
76
77 //converts URL's to paths if needed
78 $this->set_file_path( $image_url );
79
80 if ( $args ) {
81 $this->set_args( $args, $override_args );
82 }
83 }
84
85 /**
86 * Returns the base WordPress path
87 * @return string
88 */
89 private static function get_home_path() {
90 $url = str_replace( home_url(), '', site_url() );
91 $url = str_replace( $url, '', ABSPATH );
92 return $url;
93 }
94
95 /**
96 * Returns the scheme that should be used for frontend image URLs.
97 *
98 * @return string|null
99 */
100 private static function get_frontend_url_scheme() {
101 if ( 'on' === foogallery_get_setting( 'force_https' ) ) {
102 return 'https';
103 }
104
105 $scheme = wp_parse_url( get_option( 'home' ), PHP_URL_SCHEME );
106
107 if ( 'https' === $scheme || is_ssl() ) {
108 return 'https';
109 }
110
111 if ( 'http' === $scheme ) {
112 return 'http';
113 }
114
115 return null;
116 }
117
118 /**
119 * Returns the frontend home URL using the configured home URL scheme.
120 *
121 * @return string
122 */
123 private static function get_frontend_home_url() {
124 return home_url( '', self::get_frontend_url_scheme() );
125 }
126
127 /**
128 * Returns scheme variants for a URL.
129 *
130 * @param string $url URL to vary.
131 *
132 * @return array
133 */
134 private static function get_url_scheme_variants( $url ) {
135 $url = trim( $url );
136 $urls = array( $url );
137
138 if ( preg_match( '#^(?:https?:)?//#i', $url ) ) {
139 $without_scheme = preg_replace( '#^(?:https?:)?//#i', '', $url );
140
141 $urls[] = 'http://' . $without_scheme;
142 $urls[] = 'https://' . $without_scheme;
143 $urls[] = '//' . $without_scheme;
144 }
145
146 return array_values( array_unique( $urls ) );
147 }
148
149 /**
150 * Maps an image URL under a local URL prefix back to a filesystem path.
151 *
152 * @param string $image_url URL to map.
153 * @param string $base_url Local URL prefix.
154 * @param string $base_path Local filesystem prefix.
155 *
156 * @return string|null
157 */
158 private static function map_url_to_path( $image_url, $base_url, $base_path ) {
159 $base_path = untrailingslashit( $base_path );
160
161 foreach ( self::get_url_scheme_variants( $base_url ) as $url ) {
162 $url = untrailingslashit( $url );
163
164 if ( $image_url === $url ) {
165 return $base_path;
166 }
167
168 $url = trailingslashit( $url );
169
170 if ( strpos( $image_url, $url ) === 0 ) {
171 return trailingslashit( $base_path ) . substr( $image_url, strlen( $url ) );
172 }
173 }
174
175 return null;
176 }
177
178 /**
179 * Returns the path of an image URL
180 *
181 * @param $image_url
182 *
183 * @return string|false
184 */
185 public static function get_file_path( $image_url ) {
186
187 //check if the $file_path is already a path within the site
188 if ( strpos( $image_url, self::get_home_path() ) === 0 ) {
189 return $image_url;
190 } else {
191 //we are dealing with a URL
192
193 $upload_dir = wp_upload_dir();
194
195 $image_path = self::map_url_to_path( $image_url, $upload_dir['baseurl'], $upload_dir['basedir'] );
196
197 if ( null !== $image_path ) {
198 return strtok( $image_path, '?#' );
199 }
200
201 $image_path = self::map_url_to_path( $image_url, self::get_frontend_home_url(), self::get_home_path() );
202
203 if ( null !== $image_path ) {
204 //strip all querystring params
205 $image_path = strtok( $image_path, '?#' );
206
207 //check it exists
208 if ( !file_exists( $image_path ) ) {
209 return false;
210 }
211 } else {
212 $image_path = $image_url;
213 }
214 }
215
216 return $image_path;
217 }
218
219 /**
220 * Set the correct file path of the original image from the image URL
221 *
222 * @param string $image_url
223 */
224 public function set_file_path( $image_url ) {
225 $file_path = self::get_file_path( $image_url );
226 if ( false === $file_path ) {
227 $this->error = new WP_Error( 'file-not-found' );
228 } else {
229 $this->file_path = $file_path;
230 }
231 }
232
233 /**
234 * Parse the args and merge with defaults
235 *
236 * @param array $args
237 * @param bool $override_args
238 */
239 public function set_args( $args, $override_args = false ) {
240 if ( $override_args ) {
241 // Just set the args and do no more.
242 $this->args = $args;
243 } else {
244
245 //these are the default arguments
246 $arg_defaults = apply_filters( 'foogallery_thumb_default_args', array(
247 'width' => 0,
248 'height' => 0,
249 'crop' => false,
250 'crop_from_position' => foogallery_get_setting( 'default_crop_position', FooGallery_Crop_Position::CROP_POSITION_DEFAULT ),
251 'resize' => true,
252 'watermark_options' => array(),
253 'cache' => true,
254 'skip_remote_check' => false,
255 //not used. Only kept in to preserve the generated cache file name
256 'default' => null,
257 'jpeg_quality' => 90,
258 'resize_animations' => true,
259 'return' => 'url',
260 //not used. Only kept in to preserve the generated cache file name
261 'custom' => false,
262 //not used. Only kept in to preserve the generated cache file name
263 'background_fill' => null,
264 'output_file' => false,
265 'cache_with_query_params' => false
266 //not used. Only kept in to preserve the generated cache file name
267 ) );
268
269 $args = wp_parse_args( $args, $arg_defaults );
270
271 // Cast some args
272 $args['crop'] = (bool) $args['crop'];
273 $args['resize'] = (bool) $args['resize'];
274 $args['cache'] = (bool) $args['cache'];
275 $args['width'] = (int) $args['width'];
276 $args['height'] = (int) $args['height'];
277
278 // Format the crop from position arg
279 if ( is_string( $args['crop_from_position'] ) && $args['crop_from_position'] ) {
280 $args['crop_from_position'] = explode( ',', $args['crop_from_position'] );
281 }
282
283 $this->args = apply_filters( 'foogallery_thumb_args', $args );
284 }
285 }
286
287 /**
288 * Get a specific arg
289 *
290 * @access public
291 *
292 * @param string $arg
293 * @return mixed
294 */
295 public function get_arg( $arg, $default = false ) {
296
297 if ( isset( $this->args[ $arg ] ) ) {
298 return $this->args[ $arg ];
299 }
300
301 return $default;
302 }
303
304 /**
305 * Get the full path to the cache file
306 *
307 * @access public
308 * @return string
309 */
310 public function get_cache_file_path() {
311 //check we have a path first
312 if ( ! $this->file_path ) {
313 return '';
314 }
315
316 $path = trailingslashit( $this->get_cache_file_directory() ) . $this->get_cache_filename();
317
318 return apply_filters( 'foogallery_thumb_cache_file_path', $path, $this );
319 }
320
321 /**
322 * Get the directory that the cache file should be saved too
323 *
324 * @return string
325 */
326 public function get_cache_file_directory() {
327 if ( $this->get_arg( 'output_file' ) ) {
328 return dirname( $this->get_arg( 'output_file' ) );
329 }
330
331 // check we have a path first.
332 if ( ! $this->file_path ) {
333 return '';
334 }
335
336 // get a safe filename.
337 $original_filename = basename( $this->file_path );
338 $parts = explode( '.', $original_filename );
339 array_pop( $parts );
340 $filename_nice = implode( '_', $parts );
341 if ( $this->get_arg( 'override_directory' ) ) {
342 $filename_nice = $this->get_arg( 'override_directory' );
343 }
344
345 $upload_dir = wp_upload_dir();
346
347 if ( strpos( $this->file_path, $upload_dir['basedir'] ) === 0 ) {
348
349 $sub_dir = dirname( str_replace( $upload_dir['basedir'], '', $this->file_path ) );
350 $new_dir = $upload_dir['basedir'] . '/cache' . trailingslashit( $sub_dir ) . $filename_nice;
351
352 } elseif ( strpos( $this->file_path, WP_CONTENT_DIR ) === 0 ) {
353
354 $sub_dir = dirname( str_replace( WP_CONTENT_DIR, '', $this->file_path ) );
355 $new_dir = $upload_dir['basedir'] . '/cache' . trailingslashit( $sub_dir ) . $filename_nice;
356
357 } elseif ( strpos( $this->file_path, self::get_home_path() ) === 0 ) {
358
359 $new_dir = $upload_dir['basedir'] . '/cache/local';
360
361 } else {
362
363 $parts = wp_parse_url( $this->file_path );
364
365 if ( ! empty( $parts['host'] ) ) {
366 $new_dir = $upload_dir['basedir'] . '/cache/remote/' . sanitize_title( $parts['host'] );
367 } else {
368 $new_dir = $upload_dir['basedir'] . '/cache/remote';
369 }
370 }
371
372 return str_replace( '/cache/cache', '/cache', $new_dir );
373 }
374
375 /**
376 * Get the filename of the cache file
377 *
378 * @return string
379 */
380 public function get_cache_filename() {
381 if ( $this->get_arg( 'output_file' ) ) {
382 return basename( $this->get_arg( 'output_file' ) );
383 }
384
385 //check we have a path first
386 if ( ! $this->file_path ) {
387 return '';
388 }
389
390 // Generate a short unique filename
391 $serialize = crc32( serialize( array_merge( $this->args, array( $this->file_path ) ) ) );
392
393 // Get the image extension
394 $ext = $this->get_image_extension();
395
396 // Gifs are converted to pngs
397 if ( $ext === 'gif' ) {
398 $ext = 'png';
399 }
400
401 return $serialize . '.' . $ext;
402 }
403
404 /**
405 * Get the extension of the original image
406 *
407 * @return string
408 */
409 public function get_image_extension() {
410
411 $filename = wp_parse_url( $this->image_url, PHP_URL_PATH );
412
413 $ext = pathinfo( $filename, PATHINFO_EXTENSION );
414
415 if ( ! $ext ) {
416 // Seems like we dont have an ext, lets guess at JPG
417 $ext = 'jpg';
418 }
419
420 return strtolower( $ext );
421 }
422
423 /**
424 * Returns the URL of the generated file.
425 *
426 * @return string
427 */
428 public function get_cache_file_url() {
429 return $this->convert_path_to_url( $this->get_cache_file_path() );
430 }
431
432 /**
433 * Generates the thumbnail based on the args and returns the thumbnail URL
434 */
435 public function generate() {
436 //if we have any errors to begin with, then the file does not exist, so get out early.
437 if ( $this->errored() ) {
438 return $this->image_url;
439 }
440
441 $must_generate = !$this->get_arg( 'cache' );
442
443 if ( $must_generate || !file_exists( $this->get_cache_file_path() )) {
444 $this->generate_cache_file();
445 }
446
447 //if we had any errors then return the original
448 if ( $this->errored() ) {
449 return $this->image_url;
450 }
451
452 return $this->convert_path_to_url( $this->get_cache_file_path() );
453 }
454
455 /**
456 * Convert a path into a url
457 *
458 * @param string $path
459 * @return string url
460 */
461 private function convert_path_to_url( $path ) {
462
463 $upload_dir = wp_upload_dir();
464
465 if ( strpos( $path, $upload_dir['basedir'] ) !== false ) {
466 return str_replace(
467 $upload_dir['basedir'],
468 set_url_scheme( $upload_dir['baseurl'], self::get_frontend_url_scheme() ),
469 $path
470 );
471 } else {
472 return str_replace(
473 self::get_home_path(),
474 trailingslashit( self::get_frontend_home_url() ),
475 $path
476 );
477 }
478 }
479
480 /**
481 * Generate the new cache file using the original image and args
482 *
483 * @return string new filepath
484 */
485 public function generate_cache_file() {
486
487 $new_filepath = $this->get_cache_file_path();
488 $file_path = $this->file_path;
489
490 if ( is_null( $file_path ) ) {
491 return;
492 }
493
494 // Up the php memory limit
495 @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', '256M' ) );
496
497 // Create the image
498 $editor = wp_get_image_editor( $file_path, array( 'methods' => array( 'get_image' ) ) );
499
500 if ( is_wp_error( $editor ) ) {
501 $this->error = $editor;
502 return;
503 }
504
505 wp_mkdir_p( $this->get_cache_file_directory() );
506
507 // Convert gif images to png before resizing
508 if ( $this->get_image_extension() === 'gif' ) {
509
510 // Save the converted image
511 $editor->save( $new_filepath, 'image/png' );
512
513 // Pass the new file back through the function so they are resized
514 $gif = new self( $new_filepath, array_merge( $this->args, array(
515 'output_file' => $new_filepath,
516 'cache' => false
517 ) ) );
518
519 $gif->generate();
520 return;
521 }
522
523 // Apply JPEG quality settings args
524 $editor->set_quality( $this->args['jpeg_quality'] );
525
526 apply_filters( 'foogallery_thumb_image_pre', $editor, $this->args );
527
528 //extract the values from args
529 $crop = $this->get_arg( 'crop', true );
530 $crop_from_position = $this->get_arg( 'crop_from_position', array( 'center', 'center' ) );
531 $width = $this->get_arg( 'width', 150 );
532 $height = $this->get_arg( 'height', 150 );
533 $resize = $this->get_arg( 'resize', true );
534
535 // Cropping and resizing are optional so post-processing effects can run at the original dimensions.
536 if ( $width > 0 || $height > 0 ) {
537 if ( $crop && $crop_from_position && $crop_from_position !== array( 'center', 'center' ) ) {
538 $this->crop_from_position( $editor, $width, $height, $crop_from_position, $resize );
539 } elseif ( $crop === true && $resize === true ) {
540 $editor->resize( $width, $height, true );
541 } elseif ( $crop === true && $resize === false ) {
542 $this->crop_from_center( $editor, $width, $height );
543 } else {
544 $editor->resize( $width, $height );
545 }
546 }
547
548 $editor = apply_filters( 'foogallery_thumb_image_post', $editor, $this->args );
549 if ( is_wp_error( $editor ) ) {
550 $this->error = $editor;
551 return;
552 }
553
554 $editor->save( $new_filepath );
555
556 do_action( 'foogallery_thumb_saved_cache_image', $this, $new_filepath );
557 }
558
559 /**
560 * Crop the image to the specified width and height from the centre of the image, no resize
561 *
562 * @param $editor
563 * @param $width
564 * @param $height
565 *
566 * @return mixed
567 */
568 private function crop_from_center( $editor, $width, $height ) {
569
570 $size = $editor->get_size();
571
572 $crop = array( 'x' => 0, 'y' => 0, 'width' => $size['width'], 'height' => $size['height'] );
573
574 if ( $width < $size['width'] ) {
575 $crop['x'] = intval( ( $size['width'] - $width ) / 2 );
576 $crop['width'] = $width;
577 }
578
579 if ( $height < $size['height'] ) {
580 $crop['y'] = intval( ( $size['height'] - $height ) / 2 );
581 $crop['height'] = $height;
582 }
583
584 return $editor->crop( $crop['x'], $crop['y'], $crop['width'], $crop['height'] );
585 }
586
587 /**
588 * Crop an image to the specified width and height from specific coordinates in the image
589 *
590 * @param $editor
591 * @param $width
592 * @param $height
593 * @param $position
594 * @param bool $resize
595 *
596 * @return mixed
597 */
598 private function crop_from_position( $editor, $width, $height, $position, $resize = true ) {
599
600 $size = $editor->get_size();
601
602 // resize to the largest dimension
603 if ( $resize ) {
604
605 $ratio1 = $size['width'] / $size['height'];
606 $ratio2 = $width / $height;
607
608 if ( $ratio1 < $ratio2 ) {
609 $_width = $width;
610 $_height = $width / $ratio1;
611 } else {
612 $_height = $height;
613 $_width = $height * $ratio1;
614 }
615
616 $editor->resize( $_width, $_height );
617 }
618
619 $size = $editor->get_size();
620 $crop = array( 'x' => 0, 'y' => 0 );
621
622 if ( $position[0] == 'right' )
623 $crop['x'] = absint( $size['width'] - $width );
624 else if ( $position[0] == 'center' )
625 $crop['x'] = intval( absint( $size['width'] - $width ) / 2 );
626
627 if ( $position[1] == 'bottom' )
628 $crop['y'] = absint( $size['height'] - $height );
629 else if ( $position[1] == 'center' )
630 $crop['y'] = intval( absint( $size['height'] - $height ) / 2 );
631
632
633 return $editor->crop( $crop['x'], $crop['y'], $width, $height );
634 }
635 }
636 }
637