PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / 3.1.0
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization v3.1.0
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 3.12.1 All 134 releases
optimole-wp / inc / app_replacer.php

app_replacer.php in Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization 3.1.0, at inc/app_replacer.php

569 lines 15.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Class Optml_App_Replacer
5 *
6 * @package \Optml\Inc
7 * @author Optimole <friends@optimole.com>
8 */
9 abstract class Optml_App_Replacer {
10
11 /**
12 * Filters used for lazyload.
13 *
14 * @var null Lazyload filters.
15 */
16 protected static $filters = null;
17 /**
18 * Holds an array of image sizes.
19 *
20 * @var array
21 */
22 protected static $image_sizes = [];
23 /**
24 * Holds width/height to crop array based on possible image sizes.
25 *
26 * @var array
27 */
28 protected static $size_to_crop = [];
29 /**
30 * Holds possible src attributes.
31 *
32 * @var array
33 */
34 protected static $possible_src_attributes = null;
35 /**
36 * Holds possible tag replace flags where we should ignore our optimization.
37 *
38 * @var array
39 */
40 protected static $ignore_tag_strings = null;
41 /**
42 * Holds possible lazyload flags where we should ignore our lazyload.
43 *
44 * @var array
45 */
46 protected static $ignore_lazyload_strings = null;
47 /**
48 * Holds flags that should ignore the data-opt-tag format.
49 *
50 * @var array
51 */
52 protected static $ignore_data_opt_attribute = null;
53 /**
54 * Settings handler.
55 *
56 * @var Optml_Settings $settings
57 */
58 public $settings = null;
59 /**
60 * Defines which is the maximum width accepted in the optimization process.
61 *
62 * @var int
63 */
64 protected $max_width = 3000;
65 /**
66 * Defines which is the maximum width accepted in the optimization process.
67 *
68 * @var int
69 */
70 protected $max_height = 3000;
71 /**
72 * Defines if css minification should be used.
73 *
74 * @var int
75 */
76 protected $is_css_minify_on = 1;
77 /**
78 * Defines if js minification should be used.
79 *
80 * @var int
81 */
82 protected $is_js_minify_on = 0;
83 /**
84 * A cached version of `wp_upload_dir`
85 *
86 * @var null
87 */
88 protected $upload_resource = null;
89
90 /**
91 * Possible domain sources to optimize.
92 *
93 * @var array Domains.
94 */
95 protected $possible_sources = [];
96 /**
97 * Possible custom sizes definitions.
98 *
99 * @var array Custom sizes definitions.
100 */
101 private static $custom_size_buffer = [];
102 /**
103 * Whitelisted domains sources to optimize from, according to optimole service.
104 *
105 * @var array Domains.
106 */
107 protected $allowed_sources = [];
108
109 /**
110 * Holds site mapping array,
111 * if there is already a cdn and we want to fetch the images from there
112 * and not from he original site.
113 *
114 * @var array Site mappings.
115 */
116 protected $site_mappings = [];
117 /**
118 * Whether the site is whitelisted or not. Used when signing the urls.
119 *
120 * @var bool Domains.
121 */
122 protected $is_allowed_site = [];
123
124 /**
125 * Holds the most recent value for the cache buster.
126 *
127 * @var string Cache Buster value.
128 */
129 protected $active_cache_buster = '';
130
131 /**
132 * Holds possible ignored by class urls.
133 *
134 * @var array
135 */
136 protected static $ignored_url_map = [];
137
138 /**
139 * Returns possible src attributes.
140 *
141 * @return array
142 */
143 public static function possible_src_attributes() {
144
145 if ( ! empty( self::$possible_src_attributes ) && is_array( self::$possible_src_attributes ) ) {
146 return self::$possible_src_attributes;
147 }
148
149 self::$possible_src_attributes = apply_filters( 'optml_possible_src_attributes', [] );
150
151 return self::$possible_src_attributes;
152 }
153
154 /**
155 * Returns possible src attributes.
156 *
157 * @return array
158 */
159 public static function possible_lazyload_flags() {
160
161 if ( ! empty( self::$ignore_lazyload_strings ) && is_array( self::$ignore_lazyload_strings ) ) {
162 return self::$ignore_lazyload_strings;
163 }
164
165 self::$possible_src_attributes = apply_filters( 'optml_possible_lazyload_flags', [ 'skip-lazy', 'data-skip-lazy' ] );
166
167 return array_merge( self::$possible_src_attributes, [ '<noscript' ] );
168 }
169 /**
170 * Returns possible tag replacement flags.
171 *
172 * @return array
173 */
174 public static function possible_tag_flags() {
175
176 if ( ! empty( self::$ignore_tag_strings ) && is_array( self::$ignore_tag_strings ) ) {
177 return self::$ignore_tag_strings;
178 }
179
180 self::$ignore_tag_strings = apply_filters( 'optml_skip_optimizations_css_classes', [ 'skip-optimization' ] );
181
182 return self::$ignore_tag_strings;
183 }
184 /**
185 * Returns possible data-opt-src ignore flags attributes.
186 *
187 * @return array
188 */
189 public static function possible_data_ignore_flags() {
190
191 if ( ! empty( self::$ignore_data_opt_attribute ) && is_array( self::$ignore_data_opt_attribute ) ) {
192 return self::$ignore_data_opt_attribute;
193 }
194
195 self::$ignore_data_opt_attribute = apply_filters( 'optml_ignore_data_opt_flag', [] );
196
197 return self::$ignore_data_opt_attribute;
198 }
199
200 /**
201 * Size to crop maping.
202 *
203 * @return array Size mapping.
204 */
205 protected static function size_to_crop() {
206 if ( ! empty( self::$size_to_crop ) && is_array( self::$size_to_crop ) ) {
207 return self::$size_to_crop;
208 }
209
210 foreach ( self::image_sizes() as $size_data ) {
211 if ( isset( self::$size_to_crop[ $size_data['width'] . $size_data['height'] ] ) && isset( $size_data['enlarge'] ) ) {
212 continue;
213 }
214 self::$size_to_crop[ $size_data['width'] . $size_data['height'] ] =
215 isset( $size_data['enlarge'] ) ? [
216 'crop' => $size_data['crop'],
217 'enlarge' => true,
218 ] : $size_data['crop'];
219 }
220
221 return self::$size_to_crop;
222 }
223
224 /**
225 * Set possible custom size.
226 *
227 * @param null $width Width value.
228 * @param null $height Height Value.
229 * @param null $crop Croping.
230 */
231 public static function add_size( $width = null, $height = null, $crop = null ) {
232 if ( empty( $width ) || empty( $height ) ) {
233 return;
234 }
235 self::$custom_size_buffer[ 'cmole' . $width . $height ] = [
236 'width' => (int) $width,
237 'height' => (int) $height,
238 'enlarge' => true,
239 'crop' => $crop,
240 ];
241
242 }
243
244 /**
245 * Returns the array of image sizes since `get_intermediate_image_sizes` and image metadata doesn't include the
246 * custom image sizes in a reliable way.
247 *
248 * @return array
249 * @global $wp_additional_image_sizes
250 */
251 protected static function image_sizes() {
252
253 if ( ! empty( self::$image_sizes ) && is_array( self::$image_sizes ) ) {
254 return self::$image_sizes;
255 }
256
257 global $_wp_additional_image_sizes;
258
259 // Populate an array matching the data structure of $_wp_additional_image_sizes so we have a consistent structure for image sizes
260 $images = [
261 'thumb' => [
262 'width' => intval( get_option( 'thumbnail_size_w' ) ),
263 'height' => intval( get_option( 'thumbnail_size_h' ) ),
264 'crop' => (bool) get_option( 'thumbnail_crop', false ),
265 ],
266 'medium' => [
267 'width' => intval( get_option( 'medium_size_w' ) ),
268 'height' => intval( get_option( 'medium_size_h' ) ),
269 'crop' => false,
270 ],
271 'large' => [
272 'width' => intval( get_option( 'large_size_w' ) ),
273 'height' => intval( get_option( 'large_size_h' ) ),
274 'crop' => false,
275 ],
276 'full' => [
277 'width' => null,
278 'height' => null,
279 'crop' => false,
280 ],
281 ];
282
283 // Compatibility mapping as found in wp-includes/media.php
284 $images['thumbnail'] = $images['thumb'];
285
286 // Update class variable, merging in $_wp_additional_image_sizes if any are set
287 if ( is_array( $_wp_additional_image_sizes ) && ! empty( $_wp_additional_image_sizes ) ) {
288 self::$image_sizes = array_merge( $images, $_wp_additional_image_sizes );
289 } else {
290 self::$image_sizes = $images;
291 }
292
293 self::$image_sizes = array_merge( self::$image_sizes, self::$custom_size_buffer );
294 self::$image_sizes = array_map(
295 function ( $value ) {
296 $value['crop'] = isset( $value['crop'] ) ?
297 ( is_array( $value['crop'] )
298 ? $value['crop'] :
299 (bool) $value['crop'] )
300 : false;
301
302 return $value;
303 },
304 self::$image_sizes
305 );
306
307 return is_array( self::$image_sizes ) ? self::$image_sizes : [];
308 }
309
310 /**
311 * The initialize method.
312 */
313 public function init() {
314 $this->settings = new Optml_Settings();
315 $this->set_properties();
316
317 self::$filters = $this->settings->get_filters();
318 add_filter(
319 'optml_possible_lazyload_flags',
320 function ( $strings = [] ) {
321 foreach ( self::$filters[ Optml_Settings::FILTER_TYPE_LAZYLOAD ][ Optml_Settings::FILTER_CLASS ] as $rule_flag => $status ) {
322 $strings[] = $rule_flag;
323 }
324
325 return $strings;
326 },
327 10,
328 2
329 );
330 add_filter(
331 'optml_skip_optimizations_css_classes',
332 function ( $strings = [] ) {
333 foreach ( self::$filters[ Optml_Settings::FILTER_TYPE_OPTIMIZE ][ Optml_Settings::FILTER_CLASS ] as $rule_flag => $status ) {
334 $strings[] = $rule_flag;
335 }
336 return $strings;
337 },
338 10,
339 2
340 );
341 }
342
343
344 /**
345 * Set the cdn url based on the current connected user.
346 */
347 public function set_properties() {
348
349 $upload_data = wp_upload_dir();
350 $this->upload_resource = [
351 'url' => str_replace( [ 'https://', 'http://' ], '', $upload_data['baseurl'] ),
352 'directory' => $upload_data['basedir'],
353 ];
354 $this->upload_resource['url_length'] = strlen( $this->upload_resource['url'] );
355
356 $content_parts = parse_url( content_url() );
357
358 $this->upload_resource['content_path'] = $content_parts['path'];
359 $this->upload_resource['content_folder'] = ltrim( $content_parts['path'], '/' );
360 $this->upload_resource['content_folder_length'] = strlen( $this->upload_resource['content_folder'] );
361 $this->upload_resource['content_host'] = $content_parts['scheme'] . '://' . $content_parts['host'];
362
363 $service_data = $this->settings->get( 'service_data' );
364
365 $domain = '';
366 if ( isset( $service_data['is_cname_assigned'] ) && $service_data['is_cname_assigned'] === 'yes' && ! empty( $service_data['domain'] ) ) {
367 $domain = $service_data['domain'];
368 }
369 Optml_Config::init(
370 [
371 'key' => $service_data['cdn_key'],
372 'secret' => $service_data['cdn_secret'],
373 'domain' => $domain,
374 ]
375 );
376
377 if ( defined( 'OPTML_SITE_MIRROR' ) && constant( 'OPTML_SITE_MIRROR' ) ) {
378 $this->site_mappings[ rtrim( get_home_url(), '/' ) ] = rtrim( constant( 'OPTML_SITE_MIRROR' ), '/' );
379 }
380
381 $this->possible_sources = $this->extract_domain_from_urls(
382 array_merge(
383 [ get_home_url() ],
384 array_values( $this->site_mappings ),
385 array_keys( $this->site_mappings )
386 )
387 );
388
389 $this->allowed_sources = $this->extract_domain_from_urls( $service_data['whitelist'] );
390 $this->active_cache_buster = $this->settings->get( 'cache_buster' );
391 // Allways allow Photon urls.
392 $this->allowed_sources['i0.wp.com'] = true;
393 $this->allowed_sources['i1.wp.com'] = true;
394 $this->allowed_sources['i2.wp.com'] = true;
395 $this->is_allowed_site = count( array_diff_key( $this->possible_sources, $this->allowed_sources ) ) > 0;
396
397 $this->max_height = $this->settings->get( 'max_height' );
398 $this->max_width = $this->settings->get( 'max_width' );
399
400 $this->is_css_minify_on = ( $this->settings->get( 'css_minify' ) === 'enabled' ) ? 1 : 0;
401 $this->is_js_minify_on = ( $this->settings->get( 'js_minify' ) === 'enabled' ) ? 1 : 0;
402
403 add_filter( 'optml_strip_image_size_from_url', [ $this, 'strip_image_size_from_url' ], 10, 1 );
404 add_filter(
405 'image_resize_dimensions',
406 [ __CLASS__, 'listen_to_sizes' ],
407 999999,
408 6
409 );
410 }
411
412 /**
413 * Method to expose upload resource property.
414 *
415 * @return null
416 */
417 public function get_upload_resource() {
418 return $this->upload_resource;
419 }
420
421 /**
422 * List to resizes and save the crop for later re-use.
423 *
424 * @param null $value Original resize.
425 * @param int $orig_w Original W.
426 * @param int $orig_h Original H.
427 * @param int $dest_w Output W.
428 * @param int $dest_h Output H.
429 * @param mixed $crop Cropping behaviour.
430 *
431 * @return mixed Original value.
432 */
433 static function listen_to_sizes( $value, $orig_w, $orig_h, $dest_w, $dest_h, $crop ) {
434 self::add_size( $dest_w, $dest_h, $crop );
435
436 return $value;
437 }
438
439 /**
440 * Extract domains and use them as keys for fast processing.
441 *
442 * @param array $urls Input urls.
443 *
444 * @return array Array of domains as keys.
445 */
446 protected function extract_domain_from_urls( $urls = [] ) {
447 if ( ! is_array( $urls ) ) {
448 return [];
449 }
450
451 $urls = array_map(
452 function ( $value ) {
453 $parts = parse_url( $value );
454 $domain = '';
455 if ( isset( $parts['host'] ) ) {
456 $domain = $parts['host'];
457 } elseif ( isset( $parts['path'] ) ) {
458 $domain = $parts['path'];
459 }
460 return $domain;
461 },
462 $urls
463 );
464 $urls = array_filter( $urls );
465 $urls = array_unique( $urls );
466
467 $urls = array_fill_keys( $urls, true );
468 // build www versions of urls, just in case we need them for validation.
469 foreach ( $urls as $domain => $status ) {
470 if ( ! ( substr( $domain, 0, 4 ) === 'www.' ) ) {
471 $urls[ 'www.' . $domain ] = true;
472 }
473 }
474
475 return $urls;
476 }
477
478 /**
479 * Check if we can replace the url.
480 *
481 * @param string $url Url to change.
482 *
483 * @return bool Either we can replace this url or not.
484 */
485 public function can_replace_url( $url ) {
486
487 if ( ! is_string( $url ) ) {
488 return false; // @codeCoverageIgnore
489 }
490
491 $url_parts = parse_url( $url );
492
493 if ( ! isset( $url_parts['host'] ) ) {
494 return false;
495 }
496 if ( false === ( isset( $this->possible_sources[ $url_parts['host'] ] ) || isset( $this->allowed_sources[ $url_parts['host'] ] ) ) ) {
497 return false;
498 }
499
500 if ( false === Optml_Filters::should_do_image( $url, self::$filters[ Optml_Settings::FILTER_TYPE_OPTIMIZE ][ Optml_Settings::FILTER_FILENAME ] ) ) {
501 return false;
502 }
503
504 if ( ! empty( self::$ignored_url_map ) && isset( self::$ignored_url_map[ crc32( $url ) ] ) ) {
505 return false;
506 }
507
508 return true;
509 }
510 /**
511 * Checks if the file is a image size and return the full url.
512 *
513 * @param string $url The image URL.
514 *
515 * @return string
516 **/
517 public function strip_image_size_from_url( $url ) {
518
519 if ( preg_match( '#(-\d+x\d+(?:_c)?|(@2x))\.(' . implode( '|', array_keys( Optml_Config::$image_extensions ) ) . '){1}$#i', $url, $src_parts ) ) {
520 $stripped_url = str_replace( $src_parts[1], '', $url );
521 // Extracts the file path to the image minus the base url
522 $file_path = substr( $stripped_url, strpos( $stripped_url, $this->upload_resource['url'] ) + $this->upload_resource['url_length'] );
523 if ( file_exists( $this->upload_resource['directory'] . $file_path ) ) {
524 $url = $stripped_url;
525 }
526 }
527
528 return $url;
529 }
530
531 /**
532 * Try to determine height and width from strings WP appends to resized image filenames.
533 *
534 * @param string $src The image URL.
535 *
536 * @return array An array consisting of width and height.
537 */
538 protected function parse_dimensions_from_filename( $src ) {
539 $width_height_string = [];
540 $extensions = array_keys( Optml_Config::$image_extensions );
541 if ( preg_match( '#-(\d+)x(\d+)(:?_c)?\.(?:' . implode( '|', $extensions ) . '){1}$#i', $src, $width_height_string ) ) {
542 $width = (int) $width_height_string[1];
543 $height = (int) $width_height_string[2];
544 $crop = ( isset( $width_height_string[3] ) && $width_height_string[3] === '_c' );
545 if ( $width && $height ) {
546 return [ $width, $height, $crop ];
547 }
548 }
549
550 return [ false, false, false ];
551 }
552
553 /**
554 * Get the optimized urls for the wp media modal.
555 *
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.
561 * @return string The optimized url.
562 */
563 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;
567 }
568 }
569