PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / 2.5.7
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization v2.5.7
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 2.5.7, at inc/app_replacer.php

543 lines 14.2 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 Optml_Config::init(
366 [
367 'key' => $service_data['cdn_key'],
368 'secret' => $service_data['cdn_secret'],
369 ]
370 );
371
372 if ( defined( 'OPTML_SITE_MIRROR' ) && constant( 'OPTML_SITE_MIRROR' ) ) {
373 $this->site_mappings[ rtrim( get_home_url(), '/' ) ] = rtrim( constant( 'OPTML_SITE_MIRROR' ), '/' );
374 }
375
376 $this->possible_sources = $this->extract_domain_from_urls(
377 array_merge(
378 [ get_home_url() ],
379 array_values( $this->site_mappings ),
380 array_keys( $this->site_mappings )
381 )
382 );
383
384 $this->allowed_sources = $this->extract_domain_from_urls( $service_data['whitelist'] );
385 $this->active_cache_buster = $this->settings->get( 'cache_buster' );
386 // Allways allow Photon urls.
387 $this->allowed_sources['i0.wp.com'] = true;
388 $this->allowed_sources['i1.wp.com'] = true;
389 $this->allowed_sources['i2.wp.com'] = true;
390 $this->is_allowed_site = count( array_diff_key( $this->possible_sources, $this->allowed_sources ) ) > 0;
391
392 $this->max_height = $this->settings->get( 'max_height' );
393 $this->max_width = $this->settings->get( 'max_width' );
394
395 $this->is_css_minify_on = ( $this->settings->get( 'css_minify' ) === 'enabled' ) ? 1 : 0;
396 $this->is_js_minify_on = ( $this->settings->get( 'js_minify' ) === 'enabled' ) ? 1 : 0;
397
398 add_filter( 'optml_strip_image_size_from_url', [ $this, 'strip_image_size_from_url' ], 10, 1 );
399 add_filter(
400 'image_resize_dimensions',
401 [ __CLASS__, 'listen_to_sizes' ],
402 999999,
403 6
404 );
405 }
406
407 /**
408 * Method to expose upload resource property.
409 *
410 * @return null
411 */
412 public function get_upload_resource() {
413 return $this->upload_resource;
414 }
415
416 /**
417 * List to resizes and save the crop for later re-use.
418 *
419 * @param null $value Original resize.
420 * @param int $orig_w Original W.
421 * @param int $orig_h Original H.
422 * @param int $dest_w Output W.
423 * @param int $dest_h Output H.
424 * @param mixed $crop Cropping behaviour.
425 *
426 * @return mixed Original value.
427 */
428 static function listen_to_sizes( $value, $orig_w, $orig_h, $dest_w, $dest_h, $crop ) {
429 self::add_size( $dest_w, $dest_h, $crop );
430
431 return $value;
432 }
433
434 /**
435 * Extract domains and use them as keys for fast processing.
436 *
437 * @param array $urls Input urls.
438 *
439 * @return array Array of domains as keys.
440 */
441 protected function extract_domain_from_urls( $urls = [] ) {
442 if ( ! is_array( $urls ) ) {
443 return [];
444 }
445
446 $urls = array_map(
447 function ( $value ) {
448 $parts = parse_url( $value );
449
450 return isset( $parts['host'] ) ? $parts['host'] : '';
451 },
452 $urls
453 );
454 $urls = array_filter( $urls );
455 $urls = array_unique( $urls );
456
457 $urls = array_fill_keys( $urls, true );
458 // build www versions of urls, just in case we need them for validation.
459 foreach ( $urls as $domain => $status ) {
460 if ( ! ( substr( $domain, 0, 4 ) === 'www.' ) ) {
461 $urls[ 'www.' . $domain ] = true;
462 }
463 }
464
465 return $urls;
466 }
467
468 /**
469 * Check if we can replace the url.
470 *
471 * @param string $url Url to change.
472 *
473 * @return bool Either we can replace this url or not.
474 */
475 public function can_replace_url( $url ) {
476
477 if ( ! is_string( $url ) ) {
478 return false; // @codeCoverageIgnore
479 }
480
481 $url_parts = parse_url( $url );
482
483 if ( ! isset( $url_parts['host'] ) ) {
484 return false;
485 }
486 if ( false === ( isset( $this->possible_sources[ $url_parts['host'] ] ) || isset( $this->allowed_sources[ $url_parts['host'] ] ) ) ) {
487 return false;
488 }
489
490 if ( false === Optml_Filters::should_do_image( $url, self::$filters[ Optml_Settings::FILTER_TYPE_OPTIMIZE ][ Optml_Settings::FILTER_FILENAME ] ) ) {
491 return false;
492 }
493
494 if ( ! empty( self::$ignored_url_map ) && isset( self::$ignored_url_map[ crc32( $url ) ] ) ) {
495 return false;
496 }
497
498 return true;
499 }
500 /**
501 * Checks if the file is a image size and return the full url.
502 *
503 * @param string $url The image URL.
504 *
505 * @return string
506 **/
507 public function strip_image_size_from_url( $url ) {
508
509 if ( preg_match( '#(-\d+x\d+(?:_c)?|(@2x))\.(' . implode( '|', array_keys( Optml_Config::$image_extensions ) ) . '){1}$#i', $url, $src_parts ) ) {
510 $stripped_url = str_replace( $src_parts[1], '', $url );
511 // Extracts the file path to the image minus the base url
512 $file_path = substr( $stripped_url, strpos( $stripped_url, $this->upload_resource['url'] ) + $this->upload_resource['url_length'] );
513 if ( file_exists( $this->upload_resource['directory'] . $file_path ) ) {
514 $url = $stripped_url;
515 }
516 }
517
518 return $url;
519 }
520
521 /**
522 * Try to determine height and width from strings WP appends to resized image filenames.
523 *
524 * @param string $src The image URL.
525 *
526 * @return array An array consisting of width and height.
527 */
528 protected function parse_dimensions_from_filename( $src ) {
529 $width_height_string = [];
530 $extensions = array_keys( Optml_Config::$image_extensions );
531 if ( preg_match( '#-(\d+)x(\d+)(:?_c)?\.(?:' . implode( '|', $extensions ) . '){1}$#i', $src, $width_height_string ) ) {
532 $width = (int) $width_height_string[1];
533 $height = (int) $width_height_string[2];
534 $crop = ( isset( $width_height_string[3] ) && $width_height_string[3] === '_c' );
535 if ( $width && $height ) {
536 return [ $width, $height, $crop ];
537 }
538 }
539
540 return [ false, false, false ];
541 }
542 }
543