PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / 1.0.4
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization v1.0.4
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 / replacer.php

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

872 lines 22.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * The class handles the image replacements and optimizations.
5 *
6 * @package \Optml\Inc
7 * @author Optimole <friends@optimole.com>
8 */
9 class Optml_Replacer {
10 /**
11 * Cached object instance.
12 *
13 * @var Optml_Replacer
14 */
15 protected static $instance = null;
16
17 /**
18 * A list of allowd extensions.
19 *
20 * @var array
21 */
22 protected static $extensions = array(
23 'jpg|jpeg|jpe' => 'image/jpeg',
24 'png' => 'image/png',
25 'webp' => 'image/webp',
26 'svg' => 'image/svg+xml',
27 );
28
29 /**
30 * Holds an array of image sizes.
31 *
32 * @var array
33 */
34 protected static $image_sizes;
35
36 /**
37 * Te cdn url, it will be build on the run.
38 *
39 * @var null
40 */
41 protected $cdn_url = null;
42
43 /**
44 * A secret key to encode payload.
45 *
46 * @var null
47 */
48 protected $cdn_secret = null;
49
50 /**
51 * Defines which is the maximum width accepted in the optimization process.
52 *
53 * @var int
54 */
55 protected $max_width = 3000;
56
57 /**
58 * Defines the quality parameter.
59 *
60 * @var int
61 */
62 protected $quality = 'auto';
63
64 /**
65 * Defines which is the maximum width accepted in the optimization process.
66 *
67 * @var int
68 */
69 protected $max_height = 3000;
70
71 /**
72 * Holds the real images sizes as an array.
73 *
74 * @var null
75 */
76 protected $img_real_sizes = null;
77
78 /**
79 * A cached version of `wp_upload_dir`
80 *
81 * @var null
82 */
83 protected $upload_dir = null;
84 /**
85 * Setings handler.
86 *
87 * @var Optml_Settings $settings
88 */
89 protected $settings = null;
90
91 /**
92 * Class instance method.
93 *
94 * @static
95 * @since 1.0.0
96 * @access public
97 * @return Optml_Replacer
98 */
99 public static function instance() {
100 if ( is_null( self::$instance ) ) {
101 self::$instance = new self();
102 self::$instance->init();
103 }
104
105 return self::$instance;
106 }
107
108 /**
109 * The initialize method.
110 */
111 function init() {
112
113 add_filter( 'optml_replace_image', array( $this, 'get_imgcdn_url' ), 10, 2 );
114
115 $this->settings = new Optml_Settings();
116
117 $this->set_properties();
118
119 if ( ! $this->should_replace() ) {
120 return;
121 }
122
123 if ( empty( $this->cdn_url ) ) {
124 return;
125 }
126 add_filter( 'image_downsize', array( $this, 'filter_image_downsize' ), PHP_INT_MAX, 3 );
127 add_filter( 'the_content', array( $this, 'filter_the_content' ), PHP_INT_MAX );
128 add_filter( 'wp_calculate_image_srcset', array( $this, 'filter_srcset_attr' ), PHP_INT_MAX, 5 );
129 add_filter( 'init', array( $this, 'filter_options_and_mods' ) );
130 add_action( 'template_redirect', array( $this, 'init_html_replacer' ), PHP_INT_MAX );
131 add_action( 'get_post_metadata', array( $this, 'replace_meta' ), PHP_INT_MAX, 4 );
132 }
133
134 /**
135 * Set the cdn url based on the current connected user.
136 */
137 protected function set_properties() {
138 $this->upload_dir = wp_upload_dir();
139 $this->upload_dir = $this->upload_dir['baseurl'];
140
141 $service_data = $this->settings->get( 'service_data' );
142 if ( ! isset( $service_data['cdn_key'] ) ) {
143 return;
144 }
145 $cdn_key = $service_data ['cdn_key'];
146 $cdn_secret = $service_data['cdn_secret'];
147 $this->quality = $this->settings->get( 'quality' );
148 if ( empty( $cdn_key ) || empty( $cdn_secret ) ) {
149 return;
150 }
151 $this->cdn_secret = $cdn_secret;
152 $this->cdn_url = sprintf(
153 'https://%s.%s',
154 strtolower( $cdn_key ),
155 'i.optimole.com'
156 );
157 }
158
159 /**
160 * Check if we should rewrite the urls.
161 *
162 * @return bool If we can replace the image.
163 *
164 */
165 public function should_replace() {
166
167 if ( is_admin() ) {
168
169 return false;
170 }
171
172 if ( ! $this->settings->is_connected() ) {
173 return false;
174 }
175 if ( ! $this->settings->is_enabled() ) {
176
177 return false;
178 }
179 if ( array_key_exists( 'preview', $_GET ) && 'true' == $_GET['preview'] ) {
180 return false;
181 }
182
183 return true;
184 }
185
186 /**
187 * Init html replacer handler.
188 */
189 public function init_html_replacer() {
190 if ( is_admin() ) {
191 return;
192 }
193 ob_start(
194 array( &$this, 'replace_urls' )
195 );
196 }
197
198 /**
199 * This filter will replace all the images retrieved via "wp_get_image" type of functions.
200 *
201 * @param array $image The filtered value.
202 * @param int $attachment_id The related attachment id.
203 * @param array|string $size This could be the name of the thumbnail size or an array of custom dimensions.
204 *
205 * @return array
206 */
207 public function filter_image_downsize( $image, $attachment_id, $size ) {
208 // we don't run optimizations on dashboard side
209 if ( is_admin() ) {
210 return $image;
211 }
212
213 $image_url = wp_get_attachment_url( $attachment_id );
214
215 if ( $image_url ) {
216 // $image_meta = image_get_intermediate_size( $attachment_id, $size );
217 $image_meta = wp_get_attachment_metadata( $attachment_id );
218 $image_args = self::image_sizes();
219
220 // default size
221 $sizes = array(
222 'width' => isset( $image_meta['width'] ) ? intval( $image_meta['width'] ) : 'auto',
223 'height' => isset( $image_meta['height'] ) ? intval( $image_meta['height'] ) : 'auto',
224 );
225 // in case there is a custom image size $size will be an array.
226 if ( is_array( $size ) ) {
227 $sizes = array(
228 'width' => ( $size[0] < $sizes['width'] ? $size[0] : $sizes['width'] ),
229 'height' => ( $size[1] < $sizes['height'] ? $size[0] : $sizes['height'] ),
230 );
231 } elseif ( 'full' !== $size && isset( $image_args[ $size ] ) ) { // overwrite if there a size
232 $sizes = array(
233 'width' => $image_args[ $size ]['width'] < $sizes['width'] ? $image_args[ $size ]['width'] : $sizes['width'],
234 'height' => $image_args[ $size ]['height'] < $sizes['height'] ? $image_args[ $size ]['height'] : $sizes['height'],
235 );
236 }
237
238 $new_sizes = $this->validate_image_sizes( $sizes['width'], $sizes['height'] );
239
240 // resized thumbnails will have their own filenames. we should get those instead of the full image one
241 if ( is_string( $size ) && ! empty( $image_meta['sizes'] ) && ! empty( $image_meta['sizes'][ $size ] ) ) {
242 $image_url = str_replace( basename( $image_url ), $image_meta['sizes'][ $size ]['file'], $image_url );
243 }
244
245 // try to get an optimized image url.
246 $new_url = $this->get_imgcdn_url( $image_url, $new_sizes );
247 if ( $new_url === $image_url ) {
248 return $image;
249 }
250 $return = array(
251 $new_url,
252 $sizes['width'],
253 $sizes['height'],
254 false,
255 );
256
257 return $return;
258 }
259
260 // in case something wrong comes, well return the default.
261 return $image;
262 }
263
264 /**
265 * Returns the array of image sizes since `get_intermediate_image_sizes` and image metadata doesn't include the
266 * custom image sizes in a reliable way.
267 *
268 * Inspired from jetpack/photon.
269 *
270 * @global $wp_additional_image_sizes
271 *
272 * @return array
273 */
274 protected static function image_sizes() {
275 if ( null == self::$image_sizes ) {
276 global $_wp_additional_image_sizes;
277
278 // Populate an array matching the data structure of $_wp_additional_image_sizes so we have a consistent structure for image sizes
279 $images = array(
280 'thumb' => array(
281 'width' => intval( get_option( 'thumbnail_size_w' ) ),
282 'height' => intval( get_option( 'thumbnail_size_h' ) ),
283 'crop' => (bool) get_option( 'thumbnail_crop' ),
284 ),
285 'medium' => array(
286 'width' => intval( get_option( 'medium_size_w' ) ),
287 'height' => intval( get_option( 'medium_size_h' ) ),
288 'crop' => false,
289 ),
290 'large' => array(
291 'width' => intval( get_option( 'large_size_w' ) ),
292 'height' => intval( get_option( 'large_size_h' ) ),
293 'crop' => false,
294 ),
295 'full' => array(
296 'width' => null,
297 'height' => null,
298 'crop' => false,
299 ),
300 );
301
302 // Compatibility mapping as found in wp-includes/media.php
303 $images['thumbnail'] = $images['thumb'];
304
305 // Update class variable, merging in $_wp_additional_image_sizes if any are set
306 if ( is_array( $_wp_additional_image_sizes ) && ! empty( $_wp_additional_image_sizes ) ) {
307 self::$image_sizes = array_merge( $images, $_wp_additional_image_sizes );
308 } else {
309 self::$image_sizes = $images;
310 }
311 }
312
313 return is_array( self::$image_sizes ) ? self::$image_sizes : array();
314 }
315
316 /**
317 * Keep the image sizes under a sane limit.
318 *
319 * @param string $width The width value which should be sanitized.
320 * @param string $height The height value which should be sanitized.
321 *
322 * @return array
323 */
324 protected function validate_image_sizes( $width, $height ) {
325 global $content_width;
326 /**
327 * While we are inside a content filter we need to keep our max_width under the content_width global
328 * There is no reason the have a image wider than the content width.
329 */
330 if (
331 doing_filter( 'the_content' )
332 && isset( $GLOBALS['content_width'] )
333 && apply_filters( 'optml_imgcdn_allow_resize_images_from_content_width', false )
334 ) {
335 $content_width = (int) $GLOBALS['content_width'];
336
337 if ( $this->max_width > $content_width ) {
338 $this->max_width = $content_width;
339 }
340 }
341
342 $percentWidth = $percentHeight = null;
343
344 if ( $width > $this->max_width ) {
345 // we need to remember how much in percentage the width was resized and apply the same treatment to the height.
346 $percentWidth = ( 1 - $this->max_width / $width ) * 100;
347 $width = $this->max_width;
348 $height = round( $height * ( ( 100 - $percentWidth ) / 100 ), 0 );
349 }
350
351 // now for the height
352 if ( $height > $this->max_height ) {
353 $percentHeight = ( 1 - $this->max_height / $height ) * 100;
354 // if we reduce the height to max_height by $x percentage than we'll also reduce the width for the same amount.
355 $height = $this->max_height;
356 $width = round( $width * ( ( 100 - $percentHeight ) / 100 ), 0 );
357 }
358
359 return array(
360 'width' => $width,
361 'height' => $height,
362 );
363 }
364
365 /**
366 * Returns a signed image url authorized to be used in our CDN.
367 *
368 * @param string $url The url which should be signed.
369 * @param array $args Dimension params; Supports `width` and `height`.
370 *
371 * @return string
372 */
373 public function get_imgcdn_url( $url, $args = array( 'width' => 'auto', 'height' => 'auto' ) ) {
374 if ( apply_filters( 'optml_dont_replace_url', false, $url ) ) {
375 return $url;
376 }
377 if ( ! $this->check_mimetype( $url ) ) {
378
379 return $url;
380 }
381 // not used yet.
382 $compress_level = apply_filters( 'optml_image_quality', $this->quality );
383 if ( isset( $args['quality'] ) || ! empty( $args['quality'] ) ) {
384 $compress_level = $args['quality'];
385 }
386 $compress_level = $this->normalize_quality( $compress_level );
387 // this will authorize the image
388 $url_parts = explode( '://', $url );
389 $scheme = $url_parts[0];
390 $path = $url_parts[1];
391 if ( $args['width'] !== 'auto' ) {
392 $args['width'] = round( $args['width'], 0 );
393 }
394 if ( $args['height'] !== 'auto' ) {
395 $args['height'] = round( $args['height'], 0 );
396 }
397 $payload = array(
398 'path' => $this->urlception_encode( $path ),
399 'scheme' => $scheme,
400 'width' => (string) $args['width'],
401 'height' => (string) $args['height'],
402 'quality' => (string) $compress_level,
403 );
404 ksort( $payload );
405
406 $values = array_values( $payload );
407 $payload = implode( '', $values );
408 $hash = hash_hmac( 'md5', $payload, $this->cdn_secret );
409
410 $new_url = sprintf(
411 '%s/%s/%s/%s/%s/%s/%s',
412 $this->cdn_url,
413 $hash,
414 (string) $args['width'],
415 (string) $args['height'],
416 (string) $compress_level,
417 $scheme,
418 $path
419 );
420
421 return $new_url;
422 }
423
424 /**
425 * Check url mimetype.
426 *
427 * @param string $url Url to check.
428 *
429 * @return bool Is a valid image url or not.
430 */
431 private function check_mimetype( $url ) {
432
433 $mimes = self::$extensions;
434 $type = wp_check_filetype( $url, $mimes );
435
436 if ( ! isset( $type['ext'] ) || empty( $type['ext'] ) ) {
437 return false;
438 }
439
440 return true;
441 }
442
443 /**
444 * Sanitize quality.
445 *
446 * @param string|int $quality Normalize quality
447 *
448 * @return int Numeric quality.
449 */
450 private function normalize_quality( $quality ) {
451
452 if ( is_numeric( $quality ) ) {
453 return intval( $quality );
454 }
455 $quality = trim( $quality );
456 if ( $quality === 'auto' ) {
457 return 'auto';
458 }
459 if ( $quality === 'high' ) {
460 return 90;
461 }
462 if ( $quality === 'medium' ) {
463 return 60;
464 }
465 if ( $quality === 'low' ) {
466 return 30;
467 }
468
469 return 60;
470 }
471
472 /**
473 * Ensures that an url parameter can stand inside an url.
474 *
475 * @param string $url The required url.
476 *
477 * @return string
478 */
479 protected function urlception_encode( $url ) {
480 $new_url = rtrim( $url, '/' );
481
482 return urlencode( $new_url );
483 }
484
485 /**
486 * Identify images in post content.
487 *
488 * @param string $content The post content which will be filtered.
489 *
490 * @return string
491 */
492 public function filter_the_content( $content ) {
493 $images = self::parse_images_from_html( $content );
494
495 if ( empty( $images ) ) {
496 return $content; // simple. no images
497 }
498
499 $image_sizes = self::image_sizes();
500 foreach ( $images[0] as $index => $tag ) {
501 $width = $height = false;
502 $new_tag = $tag;
503 $src = $images['img_url'][ $index ];
504
505 if ( apply_filters( 'optml_imgcdn_disable_optimization_for_link', false, $src ) ) {
506 continue;
507 }
508
509 if ( false !== strpos( $src, 'i.optimole.com' ) ) {
510 continue; // we already have this
511 }
512
513 // we handle only images uploaded to this site.
514 if ( false === strpos( $src, $this->upload_dir ) ) {
515 continue;
516 }
517
518 // try to get the declared sizes from the img tag
519 if ( preg_match( '#width=["|\']?([\d%]+)["|\']?#i', $images['img_tag'][ $index ], $width_string ) ) {
520 $width = $width_string[1];
521 }
522
523 if ( preg_match( '#height=["|\']?([\d%]+)["|\']?#i', $images['img_tag'][ $index ], $height_string ) ) {
524 $height = $height_string[1];
525 }
526
527 // Detect WP registered image size from HTML class
528 if ( preg_match( '#class=["|\']?[^"\']*size-([^"\'\s]+)[^"\']*["|\']?#i', $images['img_tag'][ $index ], $size ) ) {
529 $size = array_pop( $size );
530
531 if ( false === $width && false === $height && 'full' != $size && array_key_exists( $size, $image_sizes ) ) {
532 $width = (int) $image_sizes[ $size ]['width'];
533 $height = (int) $image_sizes[ $size ]['height'];
534 }
535 } else {
536 unset( $size );
537 }
538
539 $new_sizes = $this->validate_image_sizes( $width, $height );
540
541 $new_url = $this->get_imgcdn_url( $src, $new_sizes );
542 if ( $new_url === $src ) {
543 continue;
544 }
545 // replace the url in hrefs or links
546 if ( ! empty( $images['link_url'][ $index ] ) ) {
547 if ( $this->check_mimetype( $images['link_url'][ $index ] ) ) {
548 $new_tag = preg_replace( '#(href=["|\'])' . $images['link_url'][ $index ] . '(["|\'])#i', '\1' . $new_url . '\2', $tag, 1 );
549 }
550 }
551
552 // replace the new sizes
553 $new_tag = str_replace( 'width="' . $width . '"', 'width="' . $new_sizes['width'] . '"', $new_tag );
554 $new_tag = str_replace( 'height="' . $height . '"', 'height="' . $new_sizes['height'] . '"', $new_tag );
555 // replace the new url
556 $new_tag = str_replace( 'src="' . $src . '"', 'src="' . $new_url . '"', $new_tag );
557
558 $content = str_replace( $tag, $new_tag, $content );
559 }
560
561 return $content;
562 }
563
564 /**
565 * Match all images and any relevant <a> tags in a block of HTML.
566 *
567 * @param string $content Some HTML.
568 *
569 * @return array An array of $images matches, where $images[0] is
570 * an array of full matches, and the link_url, img_tag,
571 * and img_url keys are arrays of those matches.
572 */
573 protected static function parse_images_from_html( $content ) {
574 $images = array();
575
576 if ( preg_match_all( '#(?:<a[^>]+?href=["|\'](?P<link_url>[^\s]+?)["|\'][^>]*?>\s*)?(?P<img_tag><img[^>]*?\s+?src=["|\'](?P<img_url>[^\s]+?)["|\'].*?>){1}(?:\s*</a>)?#is', $content, $images ) ) {
577 foreach ( $images as $key => $unused ) {
578 // Simplify the output as much as possible, mostly for confirming test results.
579 if ( is_numeric( $key ) && $key > 0 ) {
580 unset( $images[ $key ] );
581 }
582 }
583
584 return $images;
585 }
586
587 return array();
588 }
589
590 /**
591 * Replace image URLs in the srcset attributes and in case there is a resize in action, also replace the sizes.
592 *
593 * @param array $sources Array of image sources.
594 * @param array $size_array Array of width and height values in pixels (in that order).
595 * @param array $image_src The 'src' of the image.
596 * @param array $image_meta The image meta data as returned by 'wp_get_attachment_metadata()'.
597 * @param int $attachment_id Image attachment ID.
598 *
599 * @return array
600 */
601 public function filter_srcset_attr( $sources = array(), $size_array = array(), $image_src = array(), $image_meta = array(), $attachment_id = 0 ) {
602 if ( ! is_array( $sources ) ) {
603 return $sources;
604 }
605 $used = array();
606 $new_sources = array();
607 foreach ( $sources as $i => $source ) {
608 list( $width, $height ) = self::parse_dimensions_from_filename( $source['url'] );
609
610 if ( empty( $width ) ) {
611 $width = $image_meta['width'];
612 }
613
614 if ( empty( $height ) ) {
615 $height = $image_meta['height'];
616 }
617
618 $new_sizes = $this->validate_image_sizes( $width, $height );
619 $new_url = $this->get_imgcdn_url( $source['url'], $new_sizes );
620 if ( isset( $used[ md5( $new_url ) ] ) ) {
621 continue;
622 }
623
624 $used[ md5( $new_url ) ] = true;
625 $new_sources[ $i ] = $sources[ $i ];
626 $new_sources[ $i ]['url'] = $new_url;
627
628 if ( $new_sources[ $i ]['descriptor'] ) {
629 $new_sources[ $i ]['value'] = $new_sizes['width'];
630 } else {
631 $new_sources[ $i ]['value'] = $new_sizes['height'];
632 }
633 }
634
635 return $new_sources;
636 }
637
638 /**
639 * Try to determine height and width from strings WP appends to resized image filenames.
640 *
641 * @param string $src The image URL.
642 *
643 * @return array An array consisting of width and height.
644 */
645 public static function parse_dimensions_from_filename( $src ) {
646 $width_height_string = array();
647 $extensions = array_keys( self::$extensions );
648 if ( preg_match( '#-(\d+)x(\d+)\.(?:' . implode( '|', $extensions ) . '){1}$#i', $src, $width_height_string ) ) {
649 $width = (int) $width_height_string[1];
650 $height = (int) $width_height_string[2];
651
652 if ( $width && $height ) {
653 return array( $width, $height );
654 }
655 }
656
657 return array( false, false );
658 }
659
660 /**
661 * Handles the url replacement in options and theme mods.
662 */
663 public function filter_options_and_mods() {
664 /**
665 * `optml_imgcdn_options_with_url` is a filter that allows themes or plugins to select which option
666 * holds an url and needs an optimization.
667 */
668 $theme_slug = get_option( 'stylesheet' );
669
670 $options_list = apply_filters(
671 'optml_imgcdn_options_with_url', array(
672 "theme_mods_$theme_slug",
673 )
674 );
675
676 foreach ( $options_list as $option ) {
677 add_filter( "option_$option", array( $this, 'replace_option_url' ) );
678
679 // this one will not work for theme mods, since get_theme_mod('header_image', $default) has its own default.
680 // add_filter( "default_option_$option", array( $this, 'replace_option_url' ) );
681 }
682
683 }
684
685 /**
686 * A filter which turns a local url into an optimized CDN image url or an array of image urls.
687 *
688 * @param string $url The url which should be replaced.
689 *
690 * @return string Replaced url.
691 */
692 public function replace_option_url( $url ) {
693 if ( empty( $url ) ) {
694 return $url;
695 }
696 // $url might be an array or an json encoded array with urls.
697 if ( is_array( $url ) || filter_var( $url, FILTER_VALIDATE_URL ) === false ) {
698 $array = $url;
699 $encoded = false;
700
701 // it might a json encoded array
702 if ( is_string( $url ) ) {
703 $array = json_decode( $url, true );
704 $encoded = true;
705 }
706
707 // in case there is an array, apply it recursively.
708 if ( is_array( $array ) ) {
709 foreach ( $array as $index => $value ) {
710 $array[ $index ] = $this->replace_option_url( $value );
711 }
712
713 if ( $encoded ) {
714 return json_encode( $array );
715 } else {
716 return $array;
717 }
718 }
719
720 if ( filter_var( $url, FILTER_VALIDATE_URL ) === false ) {
721 return $url;
722 }
723 }
724
725 // we handle only images uploaded to this site./
726 // @TODO this is still wrong, not all the images are coming from the uploads folder.
727 // if ( false === strpos( $url, $this->upload_dir ) ) {
728 // return $url;
729 // }
730 // get the optimized url.
731 $new_url = $this->get_imgcdn_url( $url );
732
733 return $new_url;
734 }
735
736 /**
737 * Replace urls in post meta values.
738 *
739 * @param mixed $metadata Metadata.
740 * @param int $object_id Post id.
741 * @param string $meta_key Meta key.
742 * @param bool $single Is single?
743 *
744 * @return mixed Altered meta.
745 */
746 function replace_meta( $metadata, $object_id, $meta_key, $single ) {
747
748 $meta_needed = '_elementor_data';
749
750 if ( isset( $meta_key ) && $meta_needed == $meta_key ) {
751 remove_filter( 'get_post_metadata', array( $this, 'replace_meta' ), PHP_INT_MAX );
752
753 $current_meta = get_post_meta( $object_id, $meta_needed, $single );
754 add_filter( 'get_post_metadata', array( $this, 'replace_meta' ), PHP_INT_MAX, 4 );
755
756 if ( ! is_string( $current_meta ) ) {
757 return $metadata;
758 }
759
760 return $this->replace_urls( $current_meta, 'elementor' );
761 }
762
763 // Return original if the check does not pass
764 return $metadata;
765 }
766
767 /**
768 * Filter raw content for urls.
769 *
770 * @param string $html HTML to filter.
771 *
772 * @return mixed Filtered content.
773 */
774 public function replace_urls( $html, $context = 'raw' ) {
775
776 switch ( $context ) {
777 case 'elementor':
778 $old_urls = $this->extract_slashed_urls( $html );
779 $urls = array_map( 'wp_unslash', $old_urls );
780 $urls = array_combine( $old_urls, $urls );
781 //return $html;
782 break;
783 case 'raw':
784 default:
785 $urls = wp_extract_urls( $html );
786
787 $urls = array_combine( $urls, $urls );
788 break;
789
790 }
791
792 $cdn_url = $this->cdn_url;
793 $site_url = get_site_url();
794 $urls = array_filter(
795 $urls, function ( $url ) use ( $cdn_url, $site_url ) {
796 if ( strpos( $url, $cdn_url ) !== false ) {
797 return false;
798 }
799 if ( strpos( $url, $site_url ) === false ) {
800 return false;
801 }
802
803 return $this->check_mimetype( $url );
804 }
805 );
806
807 $urls = array_map( array( $this, 'get_imgcdn_url' ), $urls );
808
809 return str_replace( array_keys( $urls ), array_values( $urls ), $html );
810 }
811
812 /**
813 * Extract slashed urls from content.
814 *
815 * @param string $content Content to parse.
816 *
817 * @return array Urls found.
818 */
819 private function extract_slashed_urls( $content ) {
820 /**
821 * Regex rule to match slashed urls, i.e -> http:\/\/optimole.com\/wp-content\/uploads\/2018\/09\/picture.jpg
822 * Based on the extract_url patter.
823 *
824 * @var string Regex rule string.
825 *
826 */
827 $regex = "/(http(s)*.+?)\"/";
828
829 preg_match_all(
830 $regex,
831 $content,
832 $urls
833 );
834
835 $urls = array_map( function ( $value ) {
836 return rtrim( html_entity_decode( $value ), '\\' );
837 }, $urls[1] );
838
839 $urls = array_unique( $urls );
840
841 return array_values( $urls );
842 }
843
844 /**
845 * Throw error on object clone
846 *
847 * The whole idea of the singleton design pattern is that there is a single
848 * object therefore, we don't want the object to be cloned.
849 *
850 * @access public
851 * @since 1.0.0
852 * @return void
853 */
854 public function __clone() {
855 // Cloning instances of the class is forbidden.
856 _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; huh?', 'optimole-wp' ), '1.0.0' );
857 }
858
859 /**
860 * Disable unserializing of the class
861 *
862 * @access public
863 * @since 1.0.0
864 * @return void
865 */
866 public function __wakeup() {
867 // Unserializing instances of the class is forbidden.
868 _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin&#8217; huh?', 'optimole-wp' ), '1.0.0' );
869 }
870
871 }
872