| @@ -1,7 +1,8 @@ | ||
| 1 | 1 | <?php |
| 2 | -defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' ); | |
| 3 | 2 | |
| 3 | +use Imagify\Traits\InstanceGetterTrait; | |
| 4 | + | |
| 4 | 5 | require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php'; |
| 5 | 6 | require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php'; |
| 6 | 7 | |
| 7 | 8 | /** |
| @@ -10,8 +11,9 @@ | ||
| 10 | 11 | * @since 1.7.1 |
| 11 | 12 | * @author Grégory Viguier |
| 12 | 13 | */ |
| 13 | 14 | class Imagify_Filesystem extends WP_Filesystem_Direct { |
| 15 | + use InstanceGetterTrait; | |
| 14 | 16 | |
| 15 | 17 | /** |
| 16 | 18 | * Class version. |
| 17 | 19 | * |
| @@ -28,16 +30,16 @@ | ||
| 28 | 30 | */ |
| 29 | 31 | const PATTERN_DELIMITER = '@'; |
| 30 | 32 | |
| 31 | 33 | /** |
| 32 | - * The single instance of the class. | |
| 34 | + * Cached site root URLs, keyed by blog ID. | |
| 33 | 35 | * |
| 34 | - * @var object | |
| 35 | - * @access protected | |
| 36 | + * @since 2.3.1 | |
| 37 | + * | |
| 38 | + * @var array<int,string> | |
| 36 | 39 | */ |
| 37 | - protected static $_instance; | |
| 40 | + private $site_root_urls = []; | |
| 38 | 41 | |
| 39 | - | |
| 40 | 42 | /** ----------------------------------------------------------------------------------------- */ |
| 41 | 43 | /** INSTANCIATION =========================================================================== */ |
| 42 | 44 | /** ----------------------------------------------------------------------------------------- */ |
| 43 | 45 | |
| @@ -59,26 +61,9 @@ | ||
| 59 | 61 | |
| 60 | 62 | parent::__construct( '' ); |
| 61 | 63 | } |
| 62 | 64 | |
| 63 | - /** | |
| 64 | - * Get the main Instance. | |
| 65 | - * | |
| 66 | - * @since 1.7.1 | |
| 67 | - * @access public | |
| 68 | - * @author Grégory Viguier | |
| 69 | - * | |
| 70 | - * @return object Main instance. | |
| 71 | - */ | |
| 72 | - public static function get_instance() { | |
| 73 | - if ( ! isset( self::$_instance ) ) { | |
| 74 | - self::$_instance = new self(); | |
| 75 | - } | |
| 76 | 65 | |
| 77 | - return self::$_instance; | |
| 78 | - } | |
| 79 | - | |
| 80 | - | |
| 81 | 66 | /** ----------------------------------------------------------------------------------------- */ |
| 82 | 67 | /** CUSTOM TOOLS ============================================================================ */ |
| 83 | 68 | /** ----------------------------------------------------------------------------------------- */ |
| 84 | 69 | |
| @@ -140,23 +125,23 @@ | ||
| 140 | 125 | if ( isset( $option ) ) { |
| 141 | 126 | return ''; |
| 142 | 127 | } |
| 143 | 128 | |
| 144 | - return array( | |
| 129 | + return [ | |
| 145 | 130 | 'dir_path' => '', |
| 146 | 131 | 'file_name' => '', |
| 147 | 132 | 'extension' => null, |
| 148 | 133 | 'file_base' => '', |
| 149 | - ); | |
| 134 | + ]; | |
| 150 | 135 | } |
| 151 | 136 | |
| 152 | 137 | if ( isset( $option ) ) { |
| 153 | - $options = array( | |
| 138 | + $options = [ | |
| 154 | 139 | 'dir_path' => PATHINFO_DIRNAME, |
| 155 | 140 | 'file_name' => PATHINFO_BASENAME, |
| 156 | 141 | 'extension' => PATHINFO_EXTENSION, |
| 157 | 142 | 'file_base' => PATHINFO_FILENAME, |
| 158 | - ); | |
| 143 | + ]; | |
| 159 | 144 | |
| 160 | 145 | if ( ! isset( $options[ $option ] ) ) { |
| 161 | 146 | return ''; |
| 162 | 147 | } |
| @@ -171,18 +156,18 @@ | ||
| 171 | 156 | } |
| 172 | 157 | |
| 173 | 158 | $output = pathinfo( $file_path ); |
| 174 | 159 | |
| 175 | - $output['dirname'] = $this->is_root( $output['dirname'] ) ? $this->get_root() : trailingslashit( $output['dirname'] ); | |
| 176 | - $output['extension'] = isset( $output['extension'] ) ? $output['extension'] : null; | |
| 160 | + $output['dirname'] = $this->is_root( $output['dirname'] ) ? $this->get_root() : trailingslashit( $output['dirname'] ); | |
| 161 | + $output['extension'] = isset( $output['extension'] ) ? $output['extension'] : null; | |
| 177 | 162 | |
| 178 | 163 | // '/www/htdocs/inc/lib.inc.php' |
| 179 | - return array( | |
| 164 | + return [ | |
| 180 | 165 | 'dir_path' => $output['dirname'], // '/www/htdocs/inc/' |
| 181 | 166 | 'file_name' => $output['basename'], // 'lib.inc.php' |
| 182 | 167 | 'extension' => $output['extension'], // 'php' |
| 183 | 168 | 'file_base' => $output['filename'], // 'lib.inc' |
| 184 | - ); | |
| 169 | + ]; | |
| 185 | 170 | } |
| 186 | 171 | |
| 187 | 172 | /** |
| 188 | 173 | * Recursive directory creation based on full path. Will attempt to set permissions on folders. |
| @@ -348,9 +333,9 @@ | ||
| 348 | 333 | * @return bool |
| 349 | 334 | */ |
| 350 | 335 | public function is_symlinked( $file_path ) { |
| 351 | 336 | static $site_root; |
| 352 | - static $plugin_paths = array(); | |
| 337 | + static $plugin_paths = []; | |
| 353 | 338 | global $wp_plugin_paths; |
| 354 | 339 | |
| 355 | 340 | if ( ! $file_path ) { |
| 356 | 341 | return false; |
| @@ -374,9 +359,9 @@ | ||
| 374 | 359 | |
| 375 | 360 | if ( $wp_plugin_paths && is_array( $wp_plugin_paths ) ) { |
| 376 | 361 | if ( ! $plugin_paths ) { |
| 377 | 362 | foreach ( $wp_plugin_paths as $dir => $real_dir ) { |
| 378 | - $dir = $this->normalize_path_for_comparison( $dir ); | |
| 363 | + $dir = $this->normalize_path_for_comparison( $dir ); | |
| 379 | 364 | $plugin_paths[ $dir ] = $this->normalize_path_for_comparison( $real_dir ); |
| 380 | 365 | } |
| 381 | 366 | } |
| 382 | 367 | |
| @@ -530,18 +515,18 @@ | ||
| 530 | 515 | * @return array The image data. An empty array on failure. |
| 531 | 516 | */ |
| 532 | 517 | public function get_image_size( $file_path ) { |
| 533 | 518 | if ( ! $file_path ) { |
| 534 | - return array(); | |
| 519 | + return []; | |
| 535 | 520 | } |
| 536 | 521 | |
| 537 | - $size = @getimagesize( $file_path ); | |
| 522 | + $size = @getimagesize( $file_path ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged | |
| 538 | 523 | |
| 539 | 524 | if ( ! $size || ! isset( $size[0], $size[1] ) ) { |
| 540 | - return array(); | |
| 525 | + return []; | |
| 541 | 526 | } |
| 542 | 527 | |
| 543 | - return array( | |
| 528 | + return [ | |
| 544 | 529 | 0 => (int) $size[0], |
| 545 | 530 | 1 => (int) $size[1], |
| 546 | 531 | 'width' => (int) $size[0], |
| 547 | 532 | 'height' => (int) $size[1], |
| @@ -547,11 +532,11 @@ | ||
| 547 | 532 | 'height' => (int) $size[1], |
| 548 | 533 | 'type' => (int) $size[2], |
| 549 | 534 | 'attr' => $size[3], |
| 550 | 535 | 'channels' => isset( $size['channels'] ) ? (int) $size['channels'] : null, |
| 551 | - 'bits' => isset( $size['bits'] ) ? (int) $size['bits'] : null, | |
| 536 | + 'bits' => isset( $size['bits'] ) ? (int) $size['bits'] : null, | |
| 552 | 537 | 'mime' => $size['mime'], |
| 553 | - ); | |
| 538 | + ]; | |
| 554 | 539 | } |
| 555 | 540 | |
| 556 | 541 | /** |
| 557 | 542 | * Tell if exif_read_data() is available. |
| @@ -588,14 +573,14 @@ | ||
| 588 | 573 | * @return array The EXIF headers. An empty array on failure. |
| 589 | 574 | */ |
| 590 | 575 | public function get_image_exif( $file_path, $sections = null, $arrays = false, $thumbnail = false ) { |
| 591 | 576 | if ( ! $file_path || ! $this->can_get_exif() ) { |
| 592 | - return array(); | |
| 577 | + return []; | |
| 593 | 578 | } |
| 594 | 579 | |
| 595 | - $exif = @exif_read_data( $file_path, $sections, $arrays, $thumbnail ); | |
| 580 | + $exif = @exif_read_data( $file_path, $sections, $arrays, $thumbnail ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged | |
| 596 | 581 | |
| 597 | - return is_array( $exif ) ? $exif : array(); | |
| 582 | + return is_array( $exif ) ? $exif : []; | |
| 598 | 583 | } |
| 599 | 584 | |
| 600 | 585 | /** |
| 601 | 586 | * Tell if a file is an animated gif. |
| @@ -613,9 +598,9 @@ | ||
| 613 | 598 | // Not a gif file. |
| 614 | 599 | return false; |
| 615 | 600 | } |
| 616 | 601 | |
| 617 | - $fh = @fopen( $file_path, 'rb' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen | |
| 602 | + $fh = @fopen( $file_path, 'rb' ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_operations_fopen | |
| 618 | 603 | |
| 619 | 604 | if ( ! $fh ) { |
| 620 | 605 | // Could not open the file. |
| 621 | 606 | return null; |
| @@ -631,13 +616,13 @@ | ||
| 631 | 616 | |
| 632 | 617 | // We read through the file til we reach the end of the file, or we've found at least 2 frame headers. |
| 633 | 618 | while ( ! feof( $fh ) && $count < 2 ) { |
| 634 | 619 | // Read 100kb at a time. |
| 635 | - $chunk = fread( $fh, 1024 * 100 ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fread | |
| 620 | + $chunk = fread( $fh, 1024 * 100 ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread | |
| 636 | 621 | $count += preg_match_all( '#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches ); |
| 637 | 622 | } |
| 638 | 623 | |
| 639 | - fclose( $fh ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose | |
| 624 | + fclose( $fh ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose | |
| 640 | 625 | |
| 641 | 626 | return $count > 1; |
| 642 | 627 | } |
| 643 | 628 | |
| @@ -826,8 +811,12 @@ | ||
| 826 | 811 | $root_path = $this->get_abspath(); |
| 827 | 812 | return $root_path; |
| 828 | 813 | } |
| 829 | 814 | |
| 815 | + if ( empty( $_SERVER['DOCUMENT_ROOT'] ) ) { | |
| 816 | + return $root_path; | |
| 817 | + } | |
| 818 | + | |
| 830 | 819 | /** |
| 831 | 820 | * For a multisite in its own directory, get_home_path() returns the expected path only for the main site. |
| 832 | 821 | * |
| 833 | 822 | * Friend, each time an attempt is made to improve this method, and especially this part, please increment the following counter. |
| @@ -832,9 +821,10 @@ | ||
| 832 | 821 | * |
| 833 | 822 | * Friend, each time an attempt is made to improve this method, and especially this part, please increment the following counter. |
| 834 | 823 | * Improvement attempts: 3. |
| 835 | 824 | */ |
| 836 | - $document_root = realpath( wp_unslash( $_SERVER['DOCUMENT_ROOT'] ) ); // `realpath()` is needed for those cases where $_SERVER['DOCUMENT_ROOT'] is totally different from ABSPATH. | |
| 825 | + $document_root = realpath( wp_unslash( $_SERVER['DOCUMENT_ROOT'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized | |
| 826 | + // `realpath()` is needed for those cases where $_SERVER['DOCUMENT_ROOT'] is totally different from ABSPATH. | |
| 837 | 827 | $document_root = trailingslashit( str_replace( '\\', '/', $document_root ) ); |
| 838 | 828 | $path_current_site = trim( str_replace( '\\', '/', PATH_CURRENT_SITE ), '/' ); |
| 839 | 829 | $root_path = trailingslashit( wp_normalize_path( $document_root . $path_current_site ) ); |
| 840 | 830 | |
| @@ -844,8 +834,9 @@ | ||
| 844 | 834 | /** |
| 845 | 835 | * Get the URL of the site's root. It corresponds to the main site's home page URL. |
| 846 | 836 | * |
| 847 | 837 | * @since 1.8.1 |
| 838 | + * @since 2.3.1 Added the imagify_site_root_url filter. | |
| 848 | 839 | * @access public |
| 849 | 840 | * @author Grégory Viguier |
| 850 | 841 | * |
| 851 | 842 | * @return string |
| @@ -850,37 +841,56 @@ | ||
| 850 | 841 | * |
| 851 | 842 | * @return string |
| 852 | 843 | */ |
| 853 | 844 | public function get_site_root_url() { |
| 854 | - static $root_url; | |
| 845 | + $blog_id = get_current_blog_id(); | |
| 855 | 846 | |
| 856 | - if ( isset( $root_url ) ) { | |
| 857 | - return $root_url; | |
| 847 | + if ( isset( $this->site_root_urls[ $blog_id ] ) ) { | |
| 848 | + return $this->site_root_urls[ $blog_id ]; | |
| 858 | 849 | } |
| 859 | 850 | |
| 860 | - if ( ! is_multisite() || is_main_site() ) { | |
| 861 | - $root_url = home_url( '/' ); | |
| 862 | - return $root_url; | |
| 863 | - } | |
| 851 | + $root_url = home_url( '/' ); | |
| 864 | 852 | |
| 865 | - $current_network = false; | |
| 853 | + if ( is_multisite() && ! is_main_site() ) { | |
| 854 | + $current_network = false; | |
| 866 | 855 | |
| 867 | - if ( function_exists( 'get_network' ) ) { | |
| 868 | - $current_network = get_network(); | |
| 869 | - } elseif ( function_exists( 'get_current_site' ) ) { | |
| 870 | - $current_network = get_current_site(); | |
| 856 | + if ( function_exists( 'get_network' ) ) { | |
| 857 | + $current_network = get_network(); | |
| 858 | + } elseif ( function_exists( 'get_current_site' ) ) { | |
| 859 | + $current_network = get_current_site(); | |
| 860 | + } | |
| 861 | + | |
| 862 | + if ( $current_network ) { | |
| 863 | + $scheme = is_ssl() ? 'https' : 'http'; | |
| 864 | + $root_url = set_url_scheme( 'http://' . $current_network->domain . $current_network->path, $scheme ); | |
| 865 | + $root_url = trailingslashit( $root_url ); | |
| 866 | + } | |
| 871 | 867 | } |
| 872 | 868 | |
| 873 | - if ( ! $current_network ) { | |
| 874 | - $root_url = home_url( '/' ); | |
| 875 | - return $root_url; | |
| 876 | - } | |
| 869 | + /** | |
| 870 | + * Filters the URL of the site's root. | |
| 871 | + * | |
| 872 | + * On a multisite, this URL is built from the current network's domain and path. Sites using | |
| 873 | + * domain mapping are served from a domain that does not match it, which prevents Imagify from | |
| 874 | + * recognizing their image URLs as internal. Use this filter to return the URL actually used to | |
| 875 | + * serve the current site. | |
| 876 | + * | |
| 877 | + * The returned value is cached per blog for the rest of the request: callbacks must be | |
| 878 | + * deterministic for a given blog ID. | |
| 879 | + * | |
| 880 | + * @since 2.3.1 | |
| 881 | + * | |
| 882 | + * @param string $root_url URL of the site's root, with a trailing slash. | |
| 883 | + * @param int $blog_id ID of the blog the URL was built for. | |
| 884 | + */ | |
| 885 | + $filtered_url = apply_filters( 'imagify_site_root_url', $root_url, $blog_id ); | |
| 877 | 886 | |
| 878 | - $root_url = is_ssl() ? 'https' : 'http'; | |
| 879 | - $root_url = set_url_scheme( 'http://' . $current_network->domain . $current_network->path, $root_url ); | |
| 880 | - $root_url = trailingslashit( $root_url ); | |
| 887 | + // Ignore anything a callback returns that is not a usable URL, rather than casting it blindly. | |
| 888 | + $this->site_root_urls[ $blog_id ] = is_string( $filtered_url ) && '' !== trim( $filtered_url ) | |
| 889 | + ? trailingslashit( trim( $filtered_url ) ) | |
| 890 | + : home_url( '/' ); | |
| 881 | 891 | |
| 882 | - return $root_url; | |
| 892 | + return $this->site_root_urls[ $blog_id ]; | |
| 883 | 893 | } |
| 884 | 894 | |
| 885 | 895 | /** |
| 886 | 896 | * Tell if a path is the site's root. |