PluginProbe
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF / 2.2
Imagify Image Optimization: Optimize Images | Compress & Convert to WebP/AVIF v2.2
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.2, at classes/Picture/Display.php

763 lines 20.4 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', -1000 ],
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 $output = '<picture' . $this->build_attributes( $attributes ) . ">\n";
222 /**
223 * Allow to add more <source> tags to the <picture> tag.
224 *
225 * @since 1.9
226 *
227 * @param string $more_source_tags Additional <source> tags.
228 * @param array $data Data built from the originale <img> tag. See $this->process_image().
229 */
230 $output .= apply_filters( 'imagify_additional_source_tags', '', $image );
231 $output .= $this->build_source_tag( $image );
232 $output .= $this->build_img_tag( $image );
233 $output .= "</picture>\n";
234
235 return $output;
236 }
237
238 /**
239 * Build the <source> tag to insert in the <picture>.
240 *
241 * @since 1.9
242 * @see $this->process_image()
243 *
244 * @param array $image An array of data.
245 *
246 * @return string A <source> tag.
247 */
248 protected function build_source_tag( $image ) {
249 $source = '';
250
251 foreach ( [ 'avif', 'webp' ] as $image_type ) {
252 $attributes = $this->build_source_attributes( $image, $image_type );
253
254 if ( empty( $attributes ) ) {
255 continue;
256 }
257
258 $source .= '<source' . $this->build_attributes( $attributes ) . "/>\n";
259 }
260
261 return $source;
262 }
263
264 /**
265 * Build the attribute for the source tag.
266 *
267 * @param array $image An array of data.
268 * @param string $image_type Type of image.
269 *
270 * @return array
271 */
272 protected function build_source_attributes( array $image, string $image_type ): array {
273 $mime_type = '';
274 $url = '';
275
276 switch ( $image_type ) {
277 case 'webp':
278 $mime_type = 'image/webp';
279 $url = 'webp_url';
280 break;
281 case 'avif':
282 $mime_type = 'image/avif';
283 $url = 'avif_url';
284 break;
285 }
286
287 $srcset_source = ! empty( $image['srcset_attribute'] ) ? $image['srcset_attribute'] : $image['src_attribute'] . 'set';
288 $attributes = [
289 'type' => $mime_type,
290 $srcset_source => [],
291 ];
292
293 if ( ! empty( $image['srcset'] ) ) {
294 foreach ( $image['srcset'] as $srcset ) {
295 if ( empty( $srcset[ $url ] ) ) {
296 continue;
297 }
298
299 $attributes[ $srcset_source ][] = $srcset[ $url ] . ' ' . $srcset['descriptor'];
300 }
301 }
302
303 if ( empty( $attributes[ $srcset_source ] ) && empty( $image['src'][ $url ] ) ) {
304 return [];
305 }
306
307 if ( empty( $attributes[ $srcset_source ] ) ) {
308 $attributes[ $srcset_source ][] = $image['src'][ $url ];
309 }
310
311 $attributes[ $srcset_source ] = implode( ', ', $attributes[ $srcset_source ] );
312
313 foreach ( [ 'data-lazy-srcset', 'data-srcset', 'srcset' ] as $srcset_attr ) {
314 if ( ! empty( $image['attributes'][ $srcset_attr ] ) && $srcset_attr !== $srcset_source ) {
315 $attributes[ $srcset_attr ] = $image['attributes'][ $srcset_attr ];
316 }
317 }
318
319 if ( 'srcset' !== $srcset_source && empty( $attributes['srcset'] ) && ! empty( $image['attributes']['src'] ) ) {
320 // Lazyload: the "src" attr should contain a placeholder (a data image or a blank.gif ).
321 $attributes['srcset'] = $image['attributes']['src'];
322 }
323
324 foreach ( [ 'data-lazy-sizes', 'data-sizes', 'sizes' ] as $sizes_attr ) {
325 if ( ! empty( $image['attributes'][ $sizes_attr ] ) ) {
326 $attributes[ $sizes_attr ] = $image['attributes'][ $sizes_attr ];
327 }
328 }
329
330 /**
331 * Filter the attributes to be added to the <source> tag.
332 *
333 * @since 1.9
334 *
335 * @param array $attributes A list of attributes to be added to the <source> tag.
336 * @param array $data Data built from the original <img> tag. See $this->process_image().
337 */
338 $attributes = apply_filters( 'imagify_picture_source_attributes', $attributes, $image );
339
340 return $attributes;
341 }
342
343 /**
344 * Build the <img> tag to insert in the <picture>.
345 *
346 * @since 1.9
347 * @see $this->process_image()
348 *
349 * @param array $image An array of data.
350 *
351 * @return string A <img> tag.
352 */
353 protected function build_img_tag( $image ) {
354 /**
355 * Gutenberg fix.
356 * Check for the 'wp-block-cover__image-background' class on the original image, and leave that class and style attributes if found.
357 */
358 if ( ! empty( $image['attributes']['class'] ) && strpos( $image['attributes']['class'], 'wp-block-cover__image-background' ) !== false ) {
359 $to_remove = [
360 'id' => '',
361 'title' => '',
362 ];
363
364 $attributes = array_diff_key( $image['attributes'], $to_remove );
365 } else {
366 $to_remove = [
367 'class' => '',
368 'id' => '',
369 'style' => '',
370 'title' => '',
371 ];
372
373 $attributes = array_diff_key( $image['attributes'], $to_remove );
374 }
375
376 /**
377 * Filter the attributes to be added to the <img> tag.
378 *
379 * @since 1.9
380 *
381 * @param array $attributes A list of attributes to be added to the <img> tag.
382 * @param array $data Data built from the originale <img> tag. See $this->process_image().
383 */
384 $attributes = apply_filters( 'imagify_picture_img_attributes', $attributes, $image );
385
386 return '<img' . $this->build_attributes( $attributes ) . "/>\n";
387 }
388
389 /**
390 * Create HTML attributes from an array.
391 *
392 * @since 1.9
393 *
394 * @param array $attributes A list of attribute pairs.
395 *
396 * @return string HTML attributes.
397 */
398 protected function build_attributes( $attributes ) {
399 if ( ! $attributes || ! is_array( $attributes ) ) {
400 return '';
401 }
402
403 $out = '';
404
405 foreach ( $attributes as $attribute => $value ) {
406 $out .= ' ' . $attribute . '="' . esc_attr( $value ) . '"';
407 }
408
409 return $out;
410 }
411
412 /** ----------------------------------------------------------------------------------------- */
413 /** VARIOUS TOOLS =========================================================================== */
414 /** ----------------------------------------------------------------------------------------- */
415
416 /**
417 * Get a list of images in a content.
418 *
419 * @since 1.9
420 *
421 * @param string $content The content.
422 *
423 * @return array
424 */
425 protected function get_images( $content ) {
426 // Remove comments.
427 $content = preg_replace( '/<!--(.*)-->/Uis', '', $content );
428
429 if ( ! preg_match_all( '/<img\s.*>/isU', $content, $matches ) ) {
430 return [];
431 }
432
433 $images = array_map( [ $this, 'process_image' ], $matches[0] );
434 $images = array_filter( $images );
435
436 /**
437 * Filter the images to display with a <picture> tag.
438 *
439 * @since 1.9
440 * @see $this->process_image()
441 *
442 * @param array $images A list of arrays.
443 * @param string $content The page content.
444 */
445 $images = apply_filters( 'imagify_webp_picture_images_to_display', $images, $content );
446
447 if ( ! $images || ! is_array( $images ) ) {
448 return [];
449 }
450
451 foreach ( $images as $i => $image ) {
452 if ( ( empty( $image['src']['webp_exists'] ) || empty( $image['src']['webp_url'] ) ) &&
453 ( empty( $image['src']['avif_exists'] ) || empty( $image['src']['avif_url'] ) ) ) {
454
455 unset( $images[ $i ] );
456 continue;
457 }
458
459 if ( empty( $image['src']['webp_exists'] ) || empty( $image['src']['webp_url'] ) ) {
460 unset( $images[ $i ]['src']['webp_url'] );
461 }
462
463 if ( empty( $image['src']['avif_exists'] ) || empty( $image['src']['avif_url'] ) ) {
464 unset( $images[ $i ]['src']['avif_url'] );
465 }
466
467 unset( $images[ $i ]['src']['webp_path'], $images[ $i ]['src']['webp_exists'] );
468 unset( $images[ $i ]['src']['avif_path'], $images[ $i ]['src']['avif_exists'] );
469
470 if ( empty( $image['srcset'] ) || ! is_array( $image['srcset'] ) ) {
471 unset( $images[ $i ]['srcset'] );
472 continue;
473 }
474
475 foreach ( $image['srcset'] as $j => $srcset ) {
476 if ( ! is_array( $srcset ) ) {
477 continue;
478 }
479
480 if ( ( empty( $srcset['webp_exists'] ) || empty( $srcset['webp_url'] ) ) &&
481 ( empty( $srcset['avif_exists'] ) || empty( $srcset['avif_url'] ) ) ) {
482 unset( $images[ $i ]['srcset'][ $j ]['webp_url'] );
483 unset( $images[ $i ]['srcset'][ $j ]['avif_url'] );
484 }
485
486 if ( empty( $srcset['webp_exists'] ) || empty( $srcset['webp_url'] ) ) {
487 unset( $images[ $i ]['srcset'][ $j ]['webp_url'] );
488 }
489
490 if ( empty( $srcset['avif_exists'] ) || empty( $srcset['avif_url'] ) ) {
491 unset( $images[ $i ]['srcset'][ $j ]['avif_url'] );
492 }
493
494 unset( $images[ $i ]['srcset'][ $j ]['webp_path'], $images[ $i ]['srcset'][ $j ]['webp_exists'] );
495 unset( $images[ $i ]['srcset'][ $j ]['avif_path'], $images[ $i ]['srcset'][ $j ]['avif_exists'] );
496 }
497 }
498
499 return $images;
500 }
501
502 /**
503 * Process an image tag and get an array containing some data.
504 *
505 * @since 1.9
506 *
507 * @param string $image An image html tag.
508 * @return array|false {
509 * An array of data if the image has a WebP version. False otherwise.
510 *
511 * @type string $tag The image tag.
512 * @type array $attributes The image attributes (minus src and srcset).
513 * @type array $src {
514 * @type string $url URL to the original image.
515 * @type string $webp_url URL to the WebP version.
516 * }
517 * @type array $srcset {
518 * An array or arrays. Not set if not applicable.
519 *
520 * @type string $url URL to the original image.
521 * @type string $webp_url URL to the WebP version. Not set if not applicable.
522 * @type string $descriptor A src descriptor.
523 * }
524 * }
525 */
526 protected function process_image( $image ) {
527 static $extensions;
528
529 $atts_pattern = '/(?<name>[^\s"\']+)\s*=\s*(["\'])\s*(?<value>.*?)\s*\2/s';
530
531 if ( ! preg_match_all( $atts_pattern, $image, $tmp_attributes, PREG_SET_ORDER ) ) {
532 // No attributes?
533 return false;
534 }
535
536 $attributes = [];
537
538 foreach ( $tmp_attributes as $attribute ) {
539 $attributes[ $attribute['name'] ] = $attribute['value'];
540 }
541
542 if ( ! empty( $attributes['class'] ) && strpos( $attributes['class'], 'imagify-no-webp' ) !== false ) {
543 // Has the 'imagify-no-webp' class.
544 return false;
545 }
546
547 // Deal with the src attribute.
548 $src_source = false;
549
550 foreach ( [ 'data-lazy-src', 'data-src', 'src' ] as $src_attr ) {
551 if ( ! empty( $attributes[ $src_attr ] ) ) {
552 $src_source = $src_attr;
553 break;
554 }
555 }
556
557 if ( ! $src_source ) {
558 // No src attribute.
559 return false;
560 }
561
562 if ( ! isset( $extensions ) ) {
563 $extensions = imagify_get_mime_types( 'image' );
564 $extensions = array_keys( $extensions );
565 $extensions = implode( '|', $extensions );
566 }
567
568 if ( ! preg_match( '@^(?<src>(?:(?:https?:)?//|/).+\.(?<extension>' . $extensions . '))(?<query>\?.*)?$@i', $attributes[ $src_source ], $src ) ) {
569 // Not a supported image format.
570 return false;
571 }
572
573 $data = [
574 'tag' => $image,
575 'attributes' => $attributes,
576 'src_attribute' => $src_source,
577 'src' => [
578 'url' => $attributes[ $src_source ],
579 ],
580 'srcset_attribute' => false,
581 'srcset' => [],
582 ];
583
584 foreach ( $this->get_nextgen_image_data_set( $src ) as $key => $value ) {
585 $data['src'][ $key ] = $value;
586 }
587
588 // Deal with the srcset attribute.
589 $srcset_source = false;
590
591 foreach ( [ 'data-lazy-srcset', 'data-srcset', 'srcset' ] as $srcset_attr ) {
592 if ( ! empty( $attributes[ $srcset_attr ] ) ) {
593 $srcset_source = $srcset_attr;
594 break;
595 }
596 }
597
598 if ( $srcset_source ) {
599 $srcset_data = [];
600
601 $data['srcset_attribute'] = $srcset_source;
602
603 $srcset = explode( ',', $attributes[ $srcset_source ] );
604
605 foreach ( $srcset as $srcs ) {
606 $srcs = preg_split( '/\s+/', trim( $srcs ) );
607
608 if ( count( $srcs ) > 2 ) {
609 // Not a good idea to have space characters in file name.
610 $descriptor = array_pop( $srcs );
611 $srcs = [ implode( ' ', $srcs ), $descriptor ];
612 }
613
614 if ( empty( $srcs[1] ) ) {
615 $srcs[1] = '1x';
616 }
617
618 if ( ! preg_match( '@^(?<src>(?:https?:)?//.+\.(?<extension>' . $extensions . '))(?<query>\?.*)?$@i', $srcs[0], $src ) ) {
619 // Not a supported image format.
620 $data['srcset'][] = [
621 'url' => $srcs[0],
622 'descriptor' => $srcs[1],
623 ];
624 continue;
625 }
626
627 $srcset_data = [
628 'url' => $srcs[0],
629 'descriptor' => $srcs[1],
630 ];
631
632 foreach ( $this->get_nextgen_image_data_set( $src ) as $key => $value ) {
633 $srcset_data[ $key ] = $value;
634 }
635
636 $data['srcset'][] = $srcset_data;
637 }
638 }
639
640 /**
641 * Filter a processed image tag.
642 *
643 * @since 1.9
644 *
645 * @param array $data An array of data for this image.
646 * @param string $image An image html tag.
647 */
648 $data = apply_filters( 'imagify_webp_picture_process_image', $data, $image );
649
650 if ( ! $data || ! is_array( $data ) ) {
651 return false;
652 }
653
654 if ( ! isset( $data['tag'], $data['attributes'], $data['src_attribute'], $data['src'], $data['srcset_attribute'], $data['srcset'] ) ) {
655 return false;
656 }
657
658 return $data;
659 }
660
661 /**
662 * Get the next-gen image(webp & avif) data set.
663 *
664 * @param array $src Array of url/path segments.
665 *
666 * @return array
667 */
668 protected function get_nextgen_image_data_set( array $src ): array {
669 $webp_url = imagify_path_to_nextgen( $src['src'], 'webp' );
670 $webp_path = $this->url_to_path( $webp_url );
671
672 $avif_url = imagify_path_to_nextgen( $src['src'], 'avif' );
673 $avif_path = $this->url_to_path( $avif_url );
674 $query_string = ! empty( $src['query'] ) ? $src['query'] : '';
675
676 return [
677 // WebP data set.
678 'webp_url' => $webp_url . $query_string,
679 'webp_path' => $webp_path,
680 'webp_exists' => $webp_path && $this->filesystem->exists( $webp_path ),
681
682 // Avif data set.
683 'avif_url' => $avif_url . $query_string,
684 'avif_path' => $avif_path,
685 'avif_exists' => $avif_path && $this->filesystem->exists( $avif_path ),
686 ];
687 }
688
689 /**
690 * Tell if a content is HTML.
691 *
692 * @since 1.9
693 *
694 * @param string $content The content.
695 *
696 * @return bool
697 */
698 protected function is_html( $content ) {
699 return preg_match( '/<\/html>/i', $content );
700 }
701
702 /**
703 * Convert a file URL to an absolute path.
704 *
705 * @since 1.9
706 *
707 * @param string $url A file URL.
708 *
709 * @return string|bool The file path. False on failure.
710 */
711 protected function url_to_path( $url ) {
712 static $uploads_url;
713 static $uploads_dir;
714 static $root_url;
715 static $root_dir;
716 static $cdn_url;
717 static $domain_url;
718
719 /**
720 * $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.
721 */
722 if ( ! isset( $uploads_url ) ) {
723 $uploads_url = set_url_scheme( $this->filesystem->get_upload_baseurl() );
724 $uploads_dir = $this->filesystem->get_upload_basedir( true );
725 $root_url = set_url_scheme( $this->filesystem->get_site_root_url() );
726 $root_dir = $this->filesystem->get_site_root();
727 $cdn_url = apply_filters( 'imagify_cdn_source_url', '' );
728 $cdn_url = $cdn_url['url'] ? set_url_scheme( $cdn_url['url'] ) : false;
729 $domain_url = wp_parse_url( $root_url );
730
731 if ( ! empty( $domain_url['scheme'] ) && ! empty( $domain_url['host'] ) ) {
732 $domain_url = $domain_url['scheme'] . '://' . $domain_url['host'] . '/';
733 } else {
734 $domain_url = false;
735 }
736 }
737
738 // Get the right URL format.
739 if ( $domain_url && strpos( $url, '/' ) === 0 ) {
740 // URL like `/path/to/image.jpg.webp`.
741 $url = $domain_url . ltrim( $url, '/' );
742 }
743
744 $url = set_url_scheme( $url );
745
746 if ( $cdn_url && $domain_url && stripos( $url, $cdn_url ) === 0 ) {
747 // CDN.
748 $url = str_ireplace( $cdn_url, $domain_url, $url );
749 }
750
751 // Return the path.
752 if ( stripos( $url, $uploads_url ) === 0 ) {
753 return str_ireplace( $uploads_url, $uploads_dir, $url );
754 }
755
756 if ( stripos( $url, $root_url ) === 0 ) {
757 return str_ireplace( $root_url, $root_dir, $url );
758 }
759
760 return false;
761 }
762 }
763