PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 2.3.1
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v2.3.1
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.9 2.2.8 trunk 1.10 1.3.3 1.3.4 1.3.5 1.3.5.1 1.3.5.2 1.3.6 1.3.6.1 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 All 103 releases
imagify / classes / Picture / Display.php

Display.php in Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF 2.3.1, at classes/Picture/Display.php

771 lines 20.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 declare(strict_types=1);
3
4 namespace Imagify\Picture;
5
6 use Imagify\EventManagement\SubscriberInterface;
7 use Imagify_Filesystem;
8
9 /**
10 * Display Next-gen images on the site with <picture> tags.
11 *
12 * @since 1.9
13 */
14 class Display implements SubscriberInterface {
15 /**
16 * Option value.
17 *
18 * @var string
19 */
20 const OPTION_VALUE = 'picture';
21
22 /**
23 * Filesystem object.
24 *
25 * @var Imagify_Filesystem
26 */
27 protected $filesystem;
28
29 /**
30 * Constructor.
31 *
32 * @param Imagify_Filesystem $filesystem Filesystem instance.
33 */
34 public function __construct( Imagify_Filesystem $filesystem ) {
35 $this->filesystem = $filesystem;
36 }
37
38 /**
39 * Array of events this subscriber listens to
40 *
41 * @return array
42 */
43 public static function get_subscribed_events() {
44 return [
45 'template_redirect' => 'start_content_process',
46 'imagify_process_webp_content' => 'process_content',
47 ];
48 }
49
50 /** ----------------------------------------------------------------------------------------- */
51 /** ADD <PICTURE> TAGS TO THE PAGE ========================================================== */
52 /** ----------------------------------------------------------------------------------------- */
53
54 /**
55 * Start buffering the page content.
56 *
57 * @since 1.9
58 *
59 * @return void
60 */
61 public function start_content_process() {
62 if ( ! get_imagify_option( 'display_nextgen' ) ) {
63 return;
64 }
65
66 if ( self::OPTION_VALUE !== get_imagify_option( 'display_nextgen_method' ) ) {
67 return;
68 }
69
70 $allow = apply_filters_deprecated( 'imagify_allow_picture_tags_for_webp', [ true ], '2.2', 'imagify_allow_picture_tags_for_nextgen' );
71
72 /**
73 * Prevent the replacement of <img> tags into <picture> tags.
74 *
75 * @since 1.9
76 *
77 * @param bool $allow True to allow the use of <picture> tags (default). False to prevent their use.
78 */
79 $allow = apply_filters( 'imagify_allow_picture_tags_for_nextgen', true );
80
81 if ( ! $allow ) {
82 return;
83 }
84
85 ob_start( [ $this, 'maybe_process_buffer' ] );
86 }
87
88 /**
89 * Maybe process the page content.
90 *
91 * @since 1.9
92 *
93 * @param string $buffer The buffer content.
94 *
95 * @return string
96 */
97 public function maybe_process_buffer( $buffer ) {
98 if ( ! $this->is_html( $buffer ) ) {
99 return $buffer;
100 }
101
102 if ( strlen( $buffer ) <= 255 ) {
103 // Buffer length must be > 255 (IE does not read pages under 255 c).
104 return $buffer;
105 }
106
107 $buffer = $this->process_content( $buffer );
108
109 /**
110 * Filter the page content after Imagify.
111 *
112 * @since 1.9
113 *
114 * @param string $buffer The page content.
115 */
116 $buffer = (string) apply_filters( 'imagify_buffer', $buffer );
117
118 return $buffer;
119 }
120
121 /**
122 * Process the content.
123 *
124 * @since 1.9
125 *
126 * @param string $content The content.
127 *
128 * @return string
129 */
130 public function process_content( $content ) {
131 $html_no_picture_tags = $this->remove_picture_tags( $content );
132 $images = $this->get_images( $html_no_picture_tags );
133
134 if ( ! $images ) {
135 return $content;
136 }
137
138 foreach ( $images as $image ) {
139 $tag = $this->build_picture_tag( $image );
140 $content = str_replace( $image['tag'], $tag, $content );
141 }
142
143 return $content;
144 }
145
146 /**
147 * Remove pre-existing <picture> tags.
148 *
149 * We shouldn't replace images already nested inside picture tags
150 * that are already in the page.
151 *
152 * @since 1.10.0
153 *
154 * @param string $html Content of the page.
155 *
156 * @return string HTML content without pre-existing <picture> tags.
157 */
158 private function remove_picture_tags( $html ) {
159 $replace = preg_replace( '#<picture[^>]*>.*?<\/picture\s*>#mis', '', $html );
160
161 if ( null === $replace ) {
162 return $html;
163 }
164
165 return $replace;
166 }
167
168 /** ----------------------------------------------------------------------------------------- */
169 /** BUILD HTML TAGS AND ATTRIBUTES ========================================================== */
170 /** ----------------------------------------------------------------------------------------- */
171
172 /**
173 * Build the <picture> tag to insert.
174 *
175 * @since 1.9
176 * @see $this->process_image()
177 *
178 * @param array $image An array of data.
179 *
180 * @return string A <picture> tag.
181 */
182 protected function build_picture_tag( $image ) {
183 $to_remove = [
184 'alt' => '',
185 'height' => '',
186 'width' => '',
187 'data-lazy-src' => '',
188 'data-src' => '',
189 'src' => '',
190 'data-lazy-srcset' => '',
191 'data-srcset' => '',
192 'srcset' => '',
193 'data-lazy-sizes' => '',
194 'data-sizes' => '',
195 'sizes' => '',
196 ];
197
198 $attributes = array_diff_key( $image['attributes'], $to_remove );
199
200 /**
201 * Filter the attributes to be added to the <picture> tag.
202 *
203 * @since 1.9
204 *
205 * @param array $attributes A list of attributes to be added to the <picture> tag.
206 * @param array $data Data built from the originale <img> tag. See $this->process_image().
207 */
208 $attributes = apply_filters( 'imagify_picture_attributes', $attributes, $image );
209
210 /**
211 * Remove Gutenberg specific attributes from picture tag, leave them on img tag.
212 * Optional: $attributes['class'] = 'imagify-webp-cover-wrapper'; for website admin styling ease.
213 */
214 if ( ! empty( $image['attributes']['class'] ) && strpos( $image['attributes']['class'], 'wp-block-cover__image-background' ) !== false ) {
215 unset( $attributes['style'] );
216 unset( $attributes['class'] );
217 unset( $attributes['data-object-fit'] );
218 unset( $attributes['data-object-position'] );
219 }
220
221 $picture_attributes = array_filter(
222 $attributes,
223 function ( $attribute ) {
224 return strpos( $attribute, 'data-wp' ) === false;
225 },
226 ARRAY_FILTER_USE_KEY
227 );
228
229 $output = '<picture' . $this->build_attributes( $picture_attributes ) . ">\n";
230 /**
231 * Allow to add more <source> tags to the <picture> tag.
232 *
233 * @since 1.9
234 *
235 * @param string $more_source_tags Additional <source> tags.
236 * @param array $data Data built from the originale <img> tag. See $this->process_image().
237 */
238 $output .= apply_filters( 'imagify_additional_source_tags', '', $image );
239 $output .= $this->build_source_tag( $image );
240 $output .= $this->build_img_tag( $image );
241 $output .= "</picture>\n";
242
243 return $output;
244 }
245
246 /**
247 * Build the <source> tag to insert in the <picture>.
248 *
249 * @since 1.9
250 * @see $this->process_image()
251 *
252 * @param array $image An array of data.
253 *
254 * @return string A <source> tag.
255 */
256 protected function build_source_tag( $image ) {
257 $source = '';
258
259 foreach ( [ 'avif', 'webp' ] as $image_type ) {
260 $attributes = $this->build_source_attributes( $image, $image_type );
261
262 if ( empty( $attributes ) ) {
263 continue;
264 }
265
266 $source .= '<source' . $this->build_attributes( $attributes ) . "/>\n";
267 }
268
269 return $source;
270 }
271
272 /**
273 * Build the attribute for the source tag.
274 *
275 * @param array $image An array of data.
276 * @param string $image_type Type of image.
277 *
278 * @return array
279 */
280 protected function build_source_attributes( array $image, string $image_type ): array {
281 $mime_type = '';
282 $url = '';
283
284 switch ( $image_type ) {
285 case 'webp':
286 $mime_type = 'image/webp';
287 $url = 'webp_url';
288 break;
289 case 'avif':
290 $mime_type = 'image/avif';
291 $url = 'avif_url';
292 break;
293 }
294
295 $srcset_source = ! empty( $image['srcset_attribute'] ) ? $image['srcset_attribute'] : $image['src_attribute'] . 'set';
296 $attributes = [
297 'type' => $mime_type,
298 $srcset_source => [],
299 ];
300
301 if ( ! empty( $image['srcset'] ) ) {
302 foreach ( $image['srcset'] as $srcset ) {
303 if ( empty( $srcset[ $url ] ) ) {
304 continue;
305 }
306
307 $attributes[ $srcset_source ][] = $srcset[ $url ] . ' ' . $srcset['descriptor'];
308 }
309 }
310
311 if ( empty( $attributes[ $srcset_source ] ) && empty( $image['src'][ $url ] ) ) {
312 return [];
313 }
314
315 if ( empty( $attributes[ $srcset_source ] ) ) {
316 $attributes[ $srcset_source ][] = $image['src'][ $url ];
317 }
318
319 $attributes[ $srcset_source ] = implode( ', ', $attributes[ $srcset_source ] );
320
321 foreach ( [ 'data-lazy-srcset', 'data-srcset', 'srcset' ] as $srcset_attr ) {
322 if ( ! empty( $image['attributes'][ $srcset_attr ] ) && $srcset_attr !== $srcset_source ) {
323 $attributes[ $srcset_attr ] = $image['attributes'][ $srcset_attr ];
324 }
325 }
326
327 if ( 'srcset' !== $srcset_source && empty( $attributes['srcset'] ) && ! empty( $image['attributes']['src'] ) ) {
328 // Lazyload: the "src" attr should contain a placeholder (a data image or a blank.gif ).
329 $attributes['srcset'] = $image['attributes']['src'];
330 }
331
332 foreach ( [ 'data-lazy-sizes', 'data-sizes', 'sizes' ] as $sizes_attr ) {
333 if ( ! empty( $image['attributes'][ $sizes_attr ] ) ) {
334 $attributes[ $sizes_attr ] = $image['attributes'][ $sizes_attr ];
335 }
336 }
337
338 /**
339 * Filter the attributes to be added to the <source> tag.
340 *
341 * @since 1.9
342 *
343 * @param array $attributes A list of attributes to be added to the <source> tag.
344 * @param array $data Data built from the original <img> tag. See $this->process_image().
345 */
346 $attributes = apply_filters( 'imagify_picture_source_attributes', $attributes, $image );
347
348 return $attributes;
349 }
350
351 /**
352 * Build the <img> tag to insert in the <picture>.
353 *
354 * @since 1.9
355 * @see $this->process_image()
356 *
357 * @param array $image An array of data.
358 *
359 * @return string A <img> tag.
360 */
361 protected function build_img_tag( $image ) {
362 /**
363 * Gutenberg fix.
364 * Check for the 'wp-block-cover__image-background' class on the original image, and leave that class and style attributes if found.
365 */
366 if ( ! empty( $image['attributes']['class'] ) && strpos( $image['attributes']['class'], 'wp-block-cover__image-background' ) !== false ) {
367 $to_remove = [
368 'id' => '',
369 'title' => '',
370 ];
371
372 $attributes = array_diff_key( $image['attributes'], $to_remove );
373 } else {
374 $to_remove = [
375 'class' => '',
376 'id' => '',
377 'style' => '',
378 'title' => '',
379 ];
380
381 $attributes = array_diff_key( $image['attributes'], $to_remove );
382 }
383
384 /**
385 * Filter the attributes to be added to the <img> tag.
386 *
387 * @since 1.9
388 *
389 * @param array $attributes A list of attributes to be added to the <img> tag.
390 * @param array $data Data built from the originale <img> tag. See $this->process_image().
391 */
392 $attributes = apply_filters( 'imagify_picture_img_attributes', $attributes, $image );
393
394 return '<img' . $this->build_attributes( $attributes ) . "/>\n";
395 }
396
397 /**
398 * Create HTML attributes from an array.
399 *
400 * @since 1.9
401 *
402 * @param array $attributes A list of attribute pairs.
403 *
404 * @return string HTML attributes.
405 */
406 protected function build_attributes( $attributes ) {
407 if ( ! $attributes || ! is_array( $attributes ) ) {
408 return '';
409 }
410
411 $out = '';
412
413 foreach ( $attributes as $attribute => $value ) {
414 $out .= ' ' . $attribute . '="' . esc_attr( $value ) . '"';
415 }
416
417 return $out;
418 }
419
420 /** ----------------------------------------------------------------------------------------- */
421 /** VARIOUS TOOLS =========================================================================== */
422 /** ----------------------------------------------------------------------------------------- */
423
424 /**
425 * Get a list of images in a content.
426 *
427 * @since 1.9
428 *
429 * @param string $content The content.
430 *
431 * @return array
432 */
433 protected function get_images( $content ) {
434 // Remove comments.
435 $content = preg_replace( '/<!--(.*)-->/Uis', '', $content );
436
437 if ( ! preg_match_all( '/<img\s.*>/isU', $content, $matches ) ) {
438 return [];
439 }
440
441 $images = array_map( [ $this, 'process_image' ], $matches[0] );
442 $images = array_filter( $images );
443
444 /**
445 * Filter the images to display with a <picture> tag.
446 *
447 * @since 1.9
448 * @see $this->process_image()
449 *
450 * @param array $images A list of arrays.
451 * @param string $content The page content.
452 */
453 $images = apply_filters( 'imagify_webp_picture_images_to_display', $images, $content );
454
455 if ( ! $images || ! is_array( $images ) ) {
456 return [];
457 }
458
459 foreach ( $images as $i => $image ) {
460 if ( ( empty( $image['src']['webp_exists'] ) || empty( $image['src']['webp_url'] ) ) &&
461 ( empty( $image['src']['avif_exists'] ) || empty( $image['src']['avif_url'] ) ) ) {
462
463 unset( $images[ $i ] );
464 continue;
465 }
466
467 if ( empty( $image['src']['webp_exists'] ) || empty( $image['src']['webp_url'] ) ) {
468 unset( $images[ $i ]['src']['webp_url'] );
469 }
470
471 if ( empty( $image['src']['avif_exists'] ) || empty( $image['src']['avif_url'] ) ) {
472 unset( $images[ $i ]['src']['avif_url'] );
473 }
474
475 unset( $images[ $i ]['src']['webp_path'], $images[ $i ]['src']['webp_exists'] );
476 unset( $images[ $i ]['src']['avif_path'], $images[ $i ]['src']['avif_exists'] );
477
478 if ( empty( $image['srcset'] ) || ! is_array( $image['srcset'] ) ) {
479 unset( $images[ $i ]['srcset'] );
480 continue;
481 }
482
483 foreach ( $image['srcset'] as $j => $srcset ) {
484 if ( ! is_array( $srcset ) ) {
485 continue;
486 }
487
488 if ( ( empty( $srcset['webp_exists'] ) || empty( $srcset['webp_url'] ) ) &&
489 ( empty( $srcset['avif_exists'] ) || empty( $srcset['avif_url'] ) ) ) {
490 unset( $images[ $i ]['srcset'][ $j ]['webp_url'] );
491 unset( $images[ $i ]['srcset'][ $j ]['avif_url'] );
492 }
493
494 if ( empty( $srcset['webp_exists'] ) || empty( $srcset['webp_url'] ) ) {
495 unset( $images[ $i ]['srcset'][ $j ]['webp_url'] );
496 }
497
498 if ( empty( $srcset['avif_exists'] ) || empty( $srcset['avif_url'] ) ) {
499 unset( $images[ $i ]['srcset'][ $j ]['avif_url'] );
500 }
501
502 unset( $images[ $i ]['srcset'][ $j ]['webp_path'], $images[ $i ]['srcset'][ $j ]['webp_exists'] );
503 unset( $images[ $i ]['srcset'][ $j ]['avif_path'], $images[ $i ]['srcset'][ $j ]['avif_exists'] );
504 }
505 }
506
507 return $images;
508 }
509
510 /**
511 * Process an image tag and get an array containing some data.
512 *
513 * @since 1.9
514 *
515 * @param string $image An image html tag.
516 * @return array|false {
517 * An array of data if the image has a WebP version. False otherwise.
518 *
519 * @type string $tag The image tag.
520 * @type array $attributes The image attributes (minus src and srcset).
521 * @type array $src {
522 * @type string $url URL to the original image.
523 * @type string $webp_url URL to the WebP version.
524 * }
525 * @type array $srcset {
526 * An array or arrays. Not set if not applicable.
527 *
528 * @type string $url URL to the original image.
529 * @type string $webp_url URL to the WebP version. Not set if not applicable.
530 * @type string $descriptor A src descriptor.
531 * }
532 * }
533 */
534 protected function process_image( $image ) {
535 static $extensions;
536
537 $atts_pattern = '/(?<name>[^\s"\']+)\s*=\s*(["\'])\s*(?<value>.*?)\s*\2/s';
538
539 if ( ! preg_match_all( $atts_pattern, $image, $tmp_attributes, PREG_SET_ORDER ) ) {
540 // No attributes?
541 return false;
542 }
543
544 $attributes = [];
545
546 foreach ( $tmp_attributes as $attribute ) {
547 $attributes[ $attribute['name'] ] = $attribute['value'];
548 }
549
550 if ( ! empty( $attributes['class'] ) && strpos( $attributes['class'], 'imagify-no-webp' ) !== false ) {
551 // Has the 'imagify-no-webp' class.
552 return false;
553 }
554
555 // Deal with the src attribute.
556 $src_source = false;
557
558 foreach ( [ 'data-lazy-src', 'data-src', 'src' ] as $src_attr ) {
559 if ( ! empty( $attributes[ $src_attr ] ) ) {
560 $src_source = $src_attr;
561 break;
562 }
563 }
564
565 if ( ! $src_source ) {
566 // No src attribute.
567 return false;
568 }
569
570 if ( ! isset( $extensions ) ) {
571 $extensions = imagify_get_mime_types( 'image' );
572 $extensions = array_keys( $extensions );
573 $extensions = implode( '|', $extensions );
574 }
575
576 if ( ! preg_match( '@^(?<src>(?:(?:https?:)?//|/).+\.(?<extension>' . $extensions . '))(?<query>\?.*)?$@i', $attributes[ $src_source ], $src ) ) {
577 // Not a supported image format.
578 return false;
579 }
580
581 $data = [
582 'tag' => $image,
583 'attributes' => $attributes,
584 'src_attribute' => $src_source,
585 'src' => [
586 'url' => $attributes[ $src_source ],
587 ],
588 'srcset_attribute' => false,
589 'srcset' => [],
590 ];
591
592 foreach ( $this->get_nextgen_image_data_set( $src ) as $key => $value ) {
593 $data['src'][ $key ] = $value;
594 }
595
596 // Deal with the srcset attribute.
597 $srcset_source = false;
598
599 foreach ( [ 'data-lazy-srcset', 'data-srcset', 'srcset' ] as $srcset_attr ) {
600 if ( ! empty( $attributes[ $srcset_attr ] ) ) {
601 $srcset_source = $srcset_attr;
602 break;
603 }
604 }
605
606 if ( $srcset_source ) {
607 $srcset_data = [];
608
609 $data['srcset_attribute'] = $srcset_source;
610
611 $srcset = explode( ',', $attributes[ $srcset_source ] );
612
613 foreach ( $srcset as $srcs ) {
614 $srcs = preg_split( '/\s+/', trim( $srcs ) );
615
616 if ( count( $srcs ) > 2 ) {
617 // Not a good idea to have space characters in file name.
618 $descriptor = array_pop( $srcs );
619 $srcs = [ implode( ' ', $srcs ), $descriptor ];
620 }
621
622 if ( empty( $srcs[1] ) ) {
623 $srcs[1] = '1x';
624 }
625
626 if ( ! preg_match( '@^(?<src>(?:https?:)?//.+\.(?<extension>' . $extensions . '))(?<query>\?.*)?$@i', $srcs[0], $src ) ) {
627 // Not a supported image format.
628 $data['srcset'][] = [
629 'url' => $srcs[0],
630 'descriptor' => $srcs[1],
631 ];
632 continue;
633 }
634
635 $srcset_data = [
636 'url' => $srcs[0],
637 'descriptor' => $srcs[1],
638 ];
639
640 foreach ( $this->get_nextgen_image_data_set( $src ) as $key => $value ) {
641 $srcset_data[ $key ] = $value;
642 }
643
644 $data['srcset'][] = $srcset_data;
645 }
646 }
647
648 /**
649 * Filter a processed image tag.
650 *
651 * @since 1.9
652 *
653 * @param array $data An array of data for this image.
654 * @param string $image An image html tag.
655 */
656 $data = apply_filters( 'imagify_webp_picture_process_image', $data, $image );
657
658 if ( ! $data || ! is_array( $data ) ) {
659 return false;
660 }
661
662 if ( ! isset( $data['tag'], $data['attributes'], $data['src_attribute'], $data['src'], $data['srcset_attribute'], $data['srcset'] ) ) {
663 return false;
664 }
665
666 return $data;
667 }
668
669 /**
670 * Get the next-gen image(webp & avif) data set.
671 *
672 * @param array $src Array of url/path segments.
673 *
674 * @return array
675 */
676 protected function get_nextgen_image_data_set( array $src ): array {
677 $webp_url = imagify_path_to_nextgen( $src['src'], 'webp' );
678 $webp_path = $this->url_to_path( $webp_url );
679
680 $avif_url = imagify_path_to_nextgen( $src['src'], 'avif' );
681 $avif_path = $this->url_to_path( $avif_url );
682 $query_string = ! empty( $src['query'] ) ? $src['query'] : '';
683
684 return [
685 // WebP data set.
686 'webp_url' => $webp_url . $query_string,
687 'webp_path' => $webp_path,
688 'webp_exists' => $webp_path && $this->filesystem->exists( $webp_path ),
689
690 // Avif data set.
691 'avif_url' => $avif_url . $query_string,
692 'avif_path' => $avif_path,
693 'avif_exists' => $avif_path && $this->filesystem->exists( $avif_path ),
694 ];
695 }
696
697 /**
698 * Tell if a content is HTML.
699 *
700 * @since 1.9
701 *
702 * @param string $content The content.
703 *
704 * @return bool
705 */
706 protected function is_html( $content ) {
707 return preg_match( '/<\/html>/i', $content );
708 }
709
710 /**
711 * Convert a file URL to an absolute path.
712 *
713 * @since 1.9
714 *
715 * @param string $url A file URL.
716 *
717 * @return string|bool The file path. False on failure.
718 */
719 protected function url_to_path( $url ) {
720 static $uploads_url;
721 static $uploads_dir;
722 static $root_url;
723 static $root_dir;
724 static $cdn_url;
725 static $domain_url;
726
727 /**
728 * $url, $uploads_url, $root_url, and $cdn_url are passed through `set_url_scheme()` only to make sure `stripos()` doesn't fail over a stupid http/https difference.
729 */
730 if ( ! isset( $uploads_url ) ) {
731 $uploads_url = set_url_scheme( $this->filesystem->get_upload_baseurl() );
732 $uploads_dir = $this->filesystem->get_upload_basedir( true );
733 $root_url = set_url_scheme( $this->filesystem->get_site_root_url() );
734 $root_dir = $this->filesystem->get_site_root();
735 $cdn_url = apply_filters( 'imagify_cdn_source_url', '' );
736 $cdn_url = $cdn_url['url'] ? set_url_scheme( $cdn_url['url'] ) : false;
737 $domain_url = wp_parse_url( $root_url );
738
739 if ( ! empty( $domain_url['scheme'] ) && ! empty( $domain_url['host'] ) ) {
740 $domain_url = $domain_url['scheme'] . '://' . $domain_url['host'] . '/';
741 } else {
742 $domain_url = false;
743 }
744 }
745
746 // Get the right URL format.
747 if ( $domain_url && strpos( $url, '/' ) === 0 ) {
748 // URL like `/path/to/image.jpg.webp`.
749 $url = $domain_url . ltrim( $url, '/' );
750 }
751
752 $url = set_url_scheme( $url );
753
754 if ( $cdn_url && $domain_url && stripos( $url, $cdn_url ) === 0 ) {
755 // CDN.
756 $url = str_ireplace( $cdn_url, $domain_url, $url );
757 }
758
759 // Return the path.
760 if ( stripos( $url, $uploads_url ) === 0 ) {
761 return str_ireplace( $uploads_url, $uploads_dir, $url );
762 }
763
764 if ( stripos( $url, $root_url ) === 0 ) {
765 return str_ireplace( $root_url, $root_dir, $url );
766 }
767
768 return false;
769 }
770 }
771