PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / trunk
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization vtrunk
4.2.14 4.2.13 4.2.12 4.2.11 4.2.10 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 2.5.5 2.5.6 2.5.7 3.0.0 3.0.1 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.11.0 3.11.1 3.11.2 3.11.3 3.12.0 All 135 releases
← All changes | inc/app_replacer.php +189 -59 3.1.0 → trunk View file →
@@ -1,6 +1,9 @@
1 1 <?php
2 2
3 +use Optimole\Sdk\Optimole;
4 +use Optimole\Sdk\ValueObject\Position;
5 +
3 6 /**
4 7 * Class Optml_App_Replacer
5 8 *
6 9 * @package \Optml\Inc
@@ -6,13 +9,14 @@
6 9 * @package \Optml\Inc
7 10 * @author Optimole <[email protected]>
8 11 */
9 12 abstract class Optml_App_Replacer {
13 + use Optml_Dam_Offload_Utils;
10 14
11 15 /**
12 16 * Filters used for lazyload.
13 17 *
14 - * @var null Lazyload filters.
18 + * @var array Lazyload filters.
15 19 */
16 20 protected static $filters = null;
17 21 /**
18 22 * Holds an array of image sizes.
@@ -56,19 +60,31 @@
56 60 * @var Optml_Settings $settings
57 61 */
58 62 public $settings = null;
59 63 /**
60 - * Defines which is the maximum width accepted in the optimization process.
64 + * Cached offloading status.
61 65 *
66 + * @var null|bool Offload status.
67 + */
68 + public static $offload_enabled = null;
69 + /**
70 + * Defines if the dimensions should be limited when images are served.
71 + *
72 + * @var bool
73 + */
74 + protected $limit_dimensions_enabled = false;
75 + /**
76 + * Defines which is the maximum width accepted when images are served.
77 + *
62 78 * @var int
63 79 */
64 - protected $max_width = 3000;
80 + protected $limit_width = 1920;
65 81 /**
66 - * Defines which is the maximum width accepted in the optimization process.
82 + * Defines which is the maximum height accepted when images are served.
67 83 *
68 84 * @var int
69 85 */
70 - protected $max_height = 3000;
86 + protected $limit_height = 1080;
71 87 /**
72 88 * Defines if css minification should be used.
73 89 *
74 90 * @var int
@@ -82,9 +98,9 @@
82 98 protected $is_js_minify_on = 0;
83 99 /**
84 100 * A cached version of `wp_upload_dir`
85 101 *
86 - * @var null
102 + * @var null|array
87 103 */
88 104 protected $upload_resource = null;
89 105
90 106 /**
@@ -118,14 +134,14 @@
118 134 * Whether the site is whitelisted or not. Used when signing the urls.
119 135 *
120 136 * @var bool Domains.
121 137 */
122 - protected $is_allowed_site = [];
138 + protected $is_allowed_site = false;
123 139
124 140 /**
125 - * Holds the most recent value for the cache buster.
141 + * Holds the most recent value for the cache buster, updated to hold only for images if active_cache_buster_assets is defined.
126 142 *
127 - * @var string Cache Buster value.
143 + * @var string Cache Buster global value if active_cache_buster_assets is undefined else the value for images.
128 144 */
129 145 protected $active_cache_buster = '';
130 146
131 147 /**
@@ -135,15 +151,21 @@
135 151 */
136 152 protected static $ignored_url_map = [];
137 153
138 154 /**
155 + * Holds the most recent value for the cache buster for assets.
156 + *
157 + * @var string Cache Buster value.
158 + */
159 + protected $active_cache_buster_assets = '';
160 +
161 + /**
139 162 * Returns possible src attributes.
140 163 *
141 164 * @return array
142 165 */
143 166 public static function possible_src_attributes() {
144 -
145 - if ( ! empty( self::$possible_src_attributes ) && is_array( self::$possible_src_attributes ) ) {
167 + if ( null !== self::$possible_src_attributes ) {
146 168 return self::$possible_src_attributes;
147 169 }
148 170
149 171 self::$possible_src_attributes = apply_filters( 'optml_possible_src_attributes', [] );
@@ -156,14 +178,13 @@
156 178 *
157 179 * @return array
158 180 */
159 181 public static function possible_lazyload_flags() {
160 -
161 - if ( ! empty( self::$ignore_lazyload_strings ) && is_array( self::$ignore_lazyload_strings ) ) {
182 + if ( null !== self::$ignore_lazyload_strings ) {
162 183 return self::$ignore_lazyload_strings;
163 184 }
164 185
165 - self::$possible_src_attributes = apply_filters( 'optml_possible_lazyload_flags', [ 'skip-lazy', 'data-skip-lazy' ] );
186 + self::$possible_src_attributes = apply_filters( 'optml_possible_lazyload_flags', [ 'skip-lazy', 'data-skip-lazy', 'data-opt-src' ] );
166 187
167 188 return array_merge( self::$possible_src_attributes, [ '<noscript' ] );
168 189 }
169 190 /**
@@ -171,16 +192,15 @@
171 192 *
172 193 * @return array
173 194 */
174 195 public static function possible_tag_flags() {
175 -
176 - if ( ! empty( self::$ignore_tag_strings ) && is_array( self::$ignore_tag_strings ) ) {
196 + if ( null !== self::$ignore_tag_strings ) {
177 197 return self::$ignore_tag_strings;
178 198 }
179 199
180 200 self::$ignore_tag_strings = apply_filters( 'optml_skip_optimizations_css_classes', [ 'skip-optimization' ] );
181 201
182 - return self::$ignore_tag_strings;
202 + return self::$ignore_tag_strings;
183 203 }
184 204 /**
185 205 * Returns possible data-opt-src ignore flags attributes.
186 206 *
@@ -186,10 +206,9 @@
186 206 *
187 207 * @return array
188 208 */
189 209 public static function possible_data_ignore_flags() {
190 -
191 - if ( ! empty( self::$ignore_data_opt_attribute ) && is_array( self::$ignore_data_opt_attribute ) ) {
210 + if ( null !== self::$ignore_data_opt_attribute ) {
192 211 return self::$ignore_data_opt_attribute;
193 212 }
194 213
195 214 self::$ignore_data_opt_attribute = apply_filters( 'optml_ignore_data_opt_flag', [] );
@@ -201,10 +220,10 @@
201 220 * Size to crop maping.
202 221 *
203 222 * @return array Size mapping.
204 223 */
205 - protected static function size_to_crop() {
206 - if ( ! empty( self::$size_to_crop ) && is_array( self::$size_to_crop ) ) {
224 + public static function size_to_crop() {
225 + if ( ! empty( self::$size_to_crop ) ) {
207 226 return self::$size_to_crop;
208 227 }
209 228
210 229 foreach ( self::image_sizes() as $size_data ) {
@@ -223,16 +242,17 @@
223 242
224 243 /**
225 244 * Set possible custom size.
226 245 *
227 - * @param null $width Width value.
228 - * @param null $height Height Value.
229 - * @param null $crop Croping.
246 + * @param int $width Width value.
247 + * @param int $height Height Value.
248 + * @param bool|array $crop Croping.
230 249 */
231 250 public static function add_size( $width = null, $height = null, $crop = null ) {
232 - if ( empty( $width ) || empty( $height ) ) {
251 + if ( is_null( $width ) || is_null( $height ) ) {
233 252 return;
234 253 }
254 +
235 255 self::$custom_size_buffer[ 'cmole' . $width . $height ] = [
236 256 'width' => (int) $width,
237 257 'height' => (int) $height,
238 258 'enlarge' => true,
@@ -237,9 +257,8 @@
237 257 'height' => (int) $height,
238 258 'enlarge' => true,
239 259 'crop' => $crop,
240 260 ];
241 -
242 261 }
243 262
244 263 /**
245 264 * Returns the array of image sizes since `get_intermediate_image_sizes` and image metadata doesn't include the
@@ -247,11 +266,10 @@
247 266 *
248 267 * @return array
249 268 * @global $wp_additional_image_sizes
250 269 */
251 - protected static function image_sizes() {
252 -
253 - if ( ! empty( self::$image_sizes ) && is_array( self::$image_sizes ) ) {
270 + public static function image_sizes() {
271 + if ( ! empty( self::$image_sizes ) ) {
254 272 return self::$image_sizes;
255 273 }
256 274
257 275 global $_wp_additional_image_sizes;
@@ -303,9 +321,9 @@
303 321 },
304 322 self::$image_sizes
305 323 );
306 324
307 - return is_array( self::$image_sizes ) ? self::$image_sizes : [];
325 + return self::$image_sizes;
308 326 }
309 327
310 328 /**
311 329 * The initialize method.
@@ -315,8 +333,16 @@
315 333 $this->set_properties();
316 334
317 335 self::$filters = $this->settings->get_filters();
318 336 add_filter(
337 + 'optml_should_avif_ext',
338 + function ( $should_avif, $ext ) {
339 + return $ext !== 'svg';
340 + },
341 + 10,
342 + 2
343 + );
344 + add_filter(
319 345 'optml_possible_lazyload_flags',
320 346 function ( $strings = [] ) {
321 347 foreach ( self::$filters[ Optml_Settings::FILTER_TYPE_LAZYLOAD ][ Optml_Settings::FILTER_CLASS ] as $rule_flag => $status ) {
322 348 $strings[] = $rule_flag;
@@ -323,10 +349,9 @@
323 349 }
324 350
325 351 return $strings;
326 352 },
327 - 10,
328 - 2
353 + 10
329 354 );
330 355 add_filter(
331 356 'optml_skip_optimizations_css_classes',
332 357 function ( $strings = [] ) {
@@ -334,11 +359,13 @@
334 359 $strings[] = $rule_flag;
335 360 }
336 361 return $strings;
337 362 },
338 - 10,
339 - 2
363 + 10
340 364 );
365 + if ( self::$offload_enabled === null ) {
366 + self::$offload_enabled = ( new Optml_Settings() )->is_offload_enabled();
367 + }
341 368 }
342 369
343 370
344 371 /**
@@ -352,12 +379,21 @@
352 379 'directory' => $upload_data['basedir'],
353 380 ];
354 381 $this->upload_resource['url_length'] = strlen( $this->upload_resource['url'] );
355 382
356 - $content_parts = parse_url( content_url() );
383 + $content_url = content_url();
357 384
358 - $this->upload_resource['content_path'] = $content_parts['path'];
359 - $this->upload_resource['content_folder'] = ltrim( $content_parts['path'], '/' );
385 + if ( strpos( $content_url, '/' ) === 0 ) {
386 + $content_url = get_site_url() . $content_url;
387 + }
388 + $content_parts = parse_url( $content_url );
389 + $this->upload_resource['content_path'] = '/';
390 + $this->upload_resource['content_folder'] = '/';
391 + if ( isset( $content_parts['path'] ) ) {
392 + $this->upload_resource['content_path'] = $content_parts['path'];
393 + $this->upload_resource['content_folder'] = ltrim( $content_parts['path'], '/' );
394 + }
395 +
360 396 $this->upload_resource['content_folder_length'] = strlen( $this->upload_resource['content_folder'] );
361 397 $this->upload_resource['content_host'] = $content_parts['scheme'] . '://' . $content_parts['host'];
362 398
363 399 $service_data = $this->settings->get( 'service_data' );
@@ -367,11 +403,12 @@
367 403 $domain = $service_data['domain'];
368 404 }
369 405 Optml_Config::init(
370 406 [
371 - 'key' => $service_data['cdn_key'],
372 - 'secret' => $service_data['cdn_secret'],
373 - 'domain' => $domain,
407 + 'key' => $service_data['cdn_key'],
408 + 'secret' => $service_data['cdn_secret'],
409 + 'domain' => $domain,
410 + 'api_key' => $this->settings->get( 'api_key' ),
374 411 ]
375 412 );
376 413
377 414 if ( defined( 'OPTML_SITE_MIRROR' ) && constant( 'OPTML_SITE_MIRROR' ) ) {
@@ -379,9 +416,11 @@
379 416 }
380 417
381 418 $this->possible_sources = $this->extract_domain_from_urls(
382 419 array_merge(
383 - [ get_home_url() ],
420 + [
421 + get_home_url(),
422 + ],
384 423 array_values( $this->site_mappings ),
385 424 array_keys( $this->site_mappings )
386 425 )
387 426 );
@@ -386,9 +425,18 @@
386 425 )
387 426 );
388 427
389 428 $this->allowed_sources = $this->extract_domain_from_urls( $service_data['whitelist'] );
390 - $this->active_cache_buster = $this->settings->get( 'cache_buster' );
429 + $this->active_cache_buster = $this->settings->get( 'cache_buster_images' );
430 + $this->active_cache_buster_assets = $this->settings->get( 'cache_buster_assets' );
431 +
432 + if ( empty( $this->active_cache_buster ) ) {
433 + $this->active_cache_buster = $this->settings->get( 'cache_buster' );
434 + }
435 + if ( empty( $this->active_cache_buster_assets ) ) {
436 + $this->active_cache_buster_assets = $this->settings->get( 'cache_buster' );
437 + }
438 +
391 439 // Allways allow Photon urls.
392 440 $this->allowed_sources['i0.wp.com'] = true;
393 441 $this->allowed_sources['i1.wp.com'] = true;
394 442 $this->allowed_sources['i2.wp.com'] = true;
@@ -393,15 +441,18 @@
393 441 $this->allowed_sources['i1.wp.com'] = true;
394 442 $this->allowed_sources['i2.wp.com'] = true;
395 443 $this->is_allowed_site = count( array_diff_key( $this->possible_sources, $this->allowed_sources ) ) > 0;
396 444
397 - $this->max_height = $this->settings->get( 'max_height' );
398 - $this->max_width = $this->settings->get( 'max_width' );
445 + $this->limit_dimensions_enabled = $this->settings->get( 'limit_dimensions' ) === 'enabled';
446 + if ( $this->limit_dimensions_enabled ) {
447 + $this->limit_height = $this->settings->get( 'limit_height' );
448 + $this->limit_width = $this->settings->get( 'limit_width' );
449 + }
399 450
400 451 $this->is_css_minify_on = ( $this->settings->get( 'css_minify' ) === 'enabled' ) ? 1 : 0;
401 452 $this->is_js_minify_on = ( $this->settings->get( 'js_minify' ) === 'enabled' ) ? 1 : 0;
402 453
403 - add_filter( 'optml_strip_image_size_from_url', [ $this, 'strip_image_size_from_url' ], 10, 1 );
454 + add_filter( 'optml_strip_image_size_from_url', [ $this, 'strip_image_size_from_url' ], 10 );
404 455 add_filter(
405 456 'image_resize_dimensions',
406 457 [ __CLASS__, 'listen_to_sizes' ],
407 458 999999,
@@ -411,9 +462,9 @@
411 462
412 463 /**
413 464 * Method to expose upload resource property.
414 465 *
415 - * @return null
466 + * @return null|array
416 467 */
417 468 public function get_upload_resource() {
418 469 return $this->upload_resource;
419 470 }
@@ -429,9 +480,9 @@
429 480 * @param mixed $crop Cropping behaviour.
430 481 *
431 482 * @return mixed Original value.
432 483 */
433 - static function listen_to_sizes( $value, $orig_w, $orig_h, $dest_w, $dest_h, $crop ) {
484 + public static function listen_to_sizes( $value, $orig_w, $orig_h, $dest_w, $dest_h, $crop ) {
434 485 self::add_size( $dest_w, $dest_h, $crop );
435 486
436 487 return $value;
437 488 }
@@ -438,9 +489,9 @@
438 489
439 490 /**
440 491 * Extract domains and use them as keys for fast processing.
441 492 *
442 - * @param array $urls Input urls.
493 + * @param array|null $urls Input urls.
443 494 *
444 495 * @return array Array of domains as keys.
445 496 */
446 497 protected function extract_domain_from_urls( $urls = [] ) {
@@ -477,9 +528,9 @@
477 528
478 529 /**
479 530 * Check if we can replace the url.
480 531 *
481 - * @param string $url Url to change.
532 + * @param string|mixed $url Url to change.
482 533 *
483 534 * @return bool Either we can replace this url or not.
484 535 */
485 536 public function can_replace_url( $url ) {
@@ -487,8 +538,12 @@
487 538 if ( ! is_string( $url ) ) {
488 539 return false; // @codeCoverageIgnore
489 540 }
490 541
542 + if ( $this->url_has_dam_flag( $url ) ) {
543 + return true;
544 + }
545 +
491 546 $url_parts = parse_url( $url );
492 547
493 548 if ( ! isset( $url_parts['host'] ) ) {
494 549 return false;
@@ -514,15 +569,27 @@
514 569 *
515 570 * @return string
516 571 **/
517 572 public function strip_image_size_from_url( $url ) {
518 -
573 + if ( apply_filters( 'optml_should_skip_strip_image_size', false, $url ) === true ) {
574 + return $url;
575 + }
519 576 if ( preg_match( '#(-\d+x\d+(?:_c)?|(@2x))\.(' . implode( '|', array_keys( Optml_Config::$image_extensions ) ) . '){1}$#i', $url, $src_parts ) ) {
520 577 $stripped_url = str_replace( $src_parts[1], '', $url );
578 +
579 + if ( $this->settings->is_offload_enabled() ) {
580 + // Treat offloaded attachments differently.
581 + $id = $this->attachment_url_to_post_id( $stripped_url );
582 +
583 + if ( $id !== 0 && $this->is_new_offloaded_attachment( $id ) ) {
584 + return $stripped_url;
585 + }
586 + }
587 +
521 588 // Extracts the file path to the image minus the base url
522 589 $file_path = substr( $stripped_url, strpos( $stripped_url, $this->upload_resource['url'] ) + $this->upload_resource['url_length'] );
523 590 if ( file_exists( $this->upload_resource['directory'] . $file_path ) ) {
524 - $url = $stripped_url;
591 + return $stripped_url;
525 592 }
526 593 }
527 594
528 595 return $url;
@@ -544,8 +611,17 @@
544 611 $crop = ( isset( $width_height_string[3] ) && $width_height_string[3] === '_c' );
545 612 if ( $width && $height ) {
546 613 return [ $width, $height, $crop ];
547 614 }
615 + } else {
616 + $optimized_args = $this->parse_dimension_from_optimized_url( $src );
617 + if ( $optimized_args[0] !== false || $optimized_args[1] !== false ) {
618 + return [
619 + $optimized_args[0] !== 'auto' ? (int) $optimized_args[0] : false,
620 + $optimized_args[1] !== 'auto' ? (int) $optimized_args[1] : false,
621 + false,
622 + ];
623 + }
548 624 }
549 625
550 626 return [ false, false, false ];
551 627 }
@@ -552,17 +628,71 @@
552 628
553 629 /**
554 630 * Get the optimized urls for the wp media modal.
555 631 *
556 - * @param string $url Original url.
557 - * @param string $table_id The cloud id of the image.
558 - * @param string $width Image width.
559 - * @param string $height Image height.
560 - * @param array $resize Optml crop array.
632 + * @param string $url Original url.
633 + * @param string $table_id The cloud id of the image.
634 + * @param int|string $width Image width.
635 + * @param int|string $height Image height.
636 + * @param array $resize Optml crop array.
561 637 * @return string The optimized url.
562 638 */
563 639 public function get_media_optimized_url( $url, $table_id, $width = 'auto', $height = 'auto', $resize = [] ) {
564 - $optimized_url = ( new Optml_Image( $url, ['width' => $width, 'height' => $height, 'resize' => $resize, 'quality' => $this->settings->get_numeric_quality()], $this->settings->get( 'cache_buster' ) ) )->get_url();
565 - $optimized_url = str_replace( $url, Optml_Media_Offload::KEYS['not_processed_flag'] . 'media_cloud' . '/' . Optml_Media_Offload::KEYS['uploaded_flag'] . $table_id . '/' . $url, $optimized_url );
566 - return $optimized_url;
640 + return str_replace( $url, Optml_Media_Offload::KEYS['not_processed_flag'] . 'media_cloud' . '/' . Optml_Media_Offload::KEYS['uploaded_flag'] . $table_id . '/' . $url, $this->get_optimized_image_url( $url, $width, $height, $resize ) );
641 + }
642 +
643 + /**
644 + * Test that the url has the dam flag.
645 + *
646 + * @param string $url The image URL to check.
647 + *
648 + * @return bool
649 + */
650 + public function url_has_dam_flag( $url ) {
651 + return strpos( $url, Optml_Dam::URL_DAM_FLAG ) !== false;
652 + }
653 +
654 + /**
655 + * Get the optimized image url for the image url.
656 + *
657 + * @param string $url The image URL.
658 + * @param mixed $width The image width.
659 + * @param mixed $height The image height.
660 + * @param array<string, mixed>|mixed $resize The resize properties.
661 + *
662 + * @return string
663 + */
664 + protected function get_optimized_image_url( $url, $width, $height, $resize = [] ) {
665 + $width = is_int( $width ) ? $width : 'auto';
666 + $height = is_int( $height ) ? $height : 'auto';
667 + // If the image is already using Optimole URL, we extract the source to rebuild it.
668 + $url = $this->get_unoptimized_url( $url );
669 +
670 + $optimized_image = Optimole::image( $url, $this->settings->get( 'cache_buster' ) )
671 + ->width( $width )
672 + ->height( $height );
673 +
674 + if ( is_array( $resize ) && ! empty( $resize['type'] ) ) {
675 + $optimized_image->resize( $resize['type'], $resize['gravity'] ?? Position::CENTER, $resize['enlarge'] ?? false );
676 +
677 + }
678 +
679 + $optimized_image->quality( $this->settings->to_accepted_quality( $this->settings->get_quality() ) );
680 +
681 + return $optimized_image->getUrl();
682 + }
683 +
684 + /**
685 + * Returns an image id from the url.
686 + *
687 + * @param string $url The image URL.
688 + *
689 + * @return int
690 + */
691 + public function get_id_by_url( $url ) {
692 + $url = $this->get_unoptimized_url( $url );
693 + srand( crc32( $url ) );
694 + $random_id = rand();
695 + srand();
696 + return $random_id;
567 697 }
568 698 }