OwlCyberSecurity - MANAGER
Edit File: class-imagify-filesystem.php
<?php defined( 'ABSPATH' ) || die( 'Cheatin’ uh?' ); require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-base.php'; require_once ABSPATH . 'wp-admin/includes/class-wp-filesystem-direct.php'; /** * Class that enhance the WP filesystem class. * * @since 1.7.1 * @author Grégory Viguier */ class Imagify_Filesystem extends WP_Filesystem_Direct { /** * Class version. * * @var string */ const VERSION = '1.2'; /** * Delimiter used for regex patterns. * * @var string * @since 1.8 * @author Grégory Viguier */ const PATTERN_DELIMITER = '@'; /** * The single instance of the class. * * @var object * @access protected */ protected static $_instance; /** ----------------------------------------------------------------------------------------- */ /** INSTANCIATION =========================================================================== */ /** ----------------------------------------------------------------------------------------- */ /** * Constructor. * * @since 1.7.1 * @access public * @author Grégory Viguier */ public function __construct() { // Define the permission constants if not already done. if ( ! defined( 'FS_CHMOD_DIR' ) ) { define( 'FS_CHMOD_DIR', ( fileperms( ABSPATH ) & 0777 | 0755 ) ); } if ( ! defined( 'FS_CHMOD_FILE' ) ) { define( 'FS_CHMOD_FILE', ( fileperms( ABSPATH . 'index.php' ) & 0777 | 0644 ) ); } parent::__construct( '' ); } /** * Get the main Instance. * * @since 1.7.1 * @access public * @author Grégory Viguier * * @return object Main instance. */ public static function get_instance() { if ( ! isset( self::$_instance ) ) { self::$_instance = new self(); } return self::$_instance; } /** ----------------------------------------------------------------------------------------- */ /** CUSTOM TOOLS ============================================================================ */ /** ----------------------------------------------------------------------------------------- */ /** * Get the file name. * Replacement for basename(). * * @since 1.7.1 * @access public * @author Grégory Viguier * * @param string $file_path Path to the file. * @return string|bool The base name of the given path. False on failure. */ public function file_name( $file_path ) { if ( ! $file_path ) { return false; } return wp_basename( $file_path ); } /** * Get the parent directory's path. * Replacement for dirname(). * * @since 1.7.1 * @access public * @author Grégory Viguier * * @param string $file_path Path to the file. * @return string|bool The directory path with a trailing slash. False on failure. */ public function dir_path( $file_path ) { if ( ! $file_path ) { return false; } $file_path = dirname( $file_path ); return $this->is_root( $file_path ) ? $this->get_root() : trailingslashit( $file_path ); } /** * Get information about a file path. * Replacement for pathinfo(). * * @since 1.7.1 * @access public * @author Grégory Viguier * * @param string $file_path Path to the file. * @param string $option If present, specifies a specific element to be returned; one of 'dir_path', 'file_name', 'extension' or 'file_base'. * If option is not specified, returns all available elements. * @return array|string|null If the option parameter is not passed, an associative array containing the following elements is returned: 'dir_path' (with trailing slash), 'file_name' (with extension), 'extension' (if any), and 'file_base' (without extension). */ public function path_info( $file_path, $option = null ) { if ( ! $file_path ) { if ( isset( $option ) ) { return ''; } return array( 'dir_path' => '', 'file_name' => '', 'extension' => null, 'file_base' => '', ); } if ( isset( $option ) ) { $options = array( 'dir_path' => PATHINFO_DIRNAME, 'file_name' => PATHINFO_BASENAME, 'extension' => PATHINFO_EXTENSION, 'file_base' => PATHINFO_FILENAME, ); if ( ! isset( $options[ $option ] ) ) { return ''; } $output = pathinfo( $file_path, $options[ $option ] ); if ( 'dir_path' !== $option ) { return $output; } return $this->is_root( $output ) ? $this->get_root() : trailingslashit( $output ); } $output = pathinfo( $file_path ); $output['dirname'] = $this->is_root( $output['dirname'] ) ? $this->get_root() : trailingslashit( $output['dirname'] ); $output['extension'] = isset( $output['extension'] ) ? $output['extension'] : null; // '/www/htdocs/inc/lib.inc.php' return array( 'dir_path' => $output['dirname'], // '/www/htdocs/inc/' 'file_name' => $output['basename'], // 'lib.inc.php' 'extension' => $output['extension'], // 'php' 'file_base' => $output['filename'], // 'lib.inc' ); } /** * Recursive directory creation based on full path. Will attempt to set permissions on folders. * Replacement for recursive mkdir(). * * @since 1.7.1 * @access public * @author Grégory Viguier * * @param string $path Full path to attempt to create. * @return bool Whether the path was created. True if path already exists. */ public function make_dir( $path ) { /* * Safe mode fails with a trailing slash under certain PHP versions. */ $path = untrailingslashit( wp_normalize_path( $path ) ); if ( $this->is_root( $path ) ) { return $this->is_dir( $this->get_root() ) && $this->is_writable( $this->get_root() ); } if ( $this->exists( $path ) ) { return $this->is_dir( $path ) && $this->is_writable( $path ); } $site_root = $this->get_site_root(); if ( strpos( $path, $site_root ) !== 0 ) { return false; } $bits = preg_replace( '@^' . preg_quote( $site_root, '@' ) . '@i', '', $path ); $bits = explode( '/', trim( $bits, '/' ) ); $path = untrailingslashit( $site_root ); foreach ( $bits as $bit ) { $parent_path = $path; $path .= '/' . $bit; if ( $this->exists( $path ) ) { if ( ! $this->is_dir( $path ) ) { return false; } continue; } if ( ! $this->is_writable( $parent_path ) ) { $this->chmod_dir( $parent_path ); if ( ! $this->is_writable( $parent_path ) ) { return false; } } $this->mkdir( $path ); if ( ! $this->exists( $path ) ) { return false; } $this->touch( trailingslashit( $path ) . 'index.php' ); } return true; } /** * Set a file permissions using FS_CHMOD_FILE. * * @since 1.7.1 * @access public * @author Grégory Viguier * * @param string $file_path Path to the file. * @return bool True on success, false on failure. */ public function chmod_file( $file_path ) { if ( ! $file_path ) { return false; } return $this->chmod( $file_path, FS_CHMOD_FILE ); } /** * Set a directory permissions using FS_CHMOD_DIR. * * @since 1.7.1 * @access public * @author Grégory Viguier * * @param string $file_path Path to the directory. * @return bool True on success, false on failure. */ public function chmod_dir( $file_path ) { if ( ! $file_path ) { return false; } return $this->chmod( $file_path, FS_CHMOD_DIR ); } /** * Get a file mime type. * * @since 1.7.1 * @access public * @author Grégory Viguier * * @param string $file_path A file path (prefered) or a filename. * @return string|bool A mime type. False on failure: the test is limited to mime types supported by Imagify. */ public function get_mime_type( $file_path ) { if ( ! $file_path ) { return false; } $file_type = wp_check_filetype( $file_path, imagify_get_mime_types() ); return $file_type['type']; } /** * Get a file modification date, formated as "mysql". Fallback to current date. * * @since 1.7.1 * @access public * @author Grégory Viguier * * @param string $file_path Path to the file. * @return string The date. */ public function get_date( $file_path ) { static $offset; if ( ! $file_path ) { return current_time( 'mysql' ); } $date = $this->mtime( $file_path ); if ( ! $date ) { return current_time( 'mysql' ); } if ( ! isset( $offset ) ) { $offset = get_option( 'gmt_offset' ) * HOUR_IN_SECONDS; } return gmdate( 'Y-m-d H:i:s', $date + $offset ); } /** * Tell if a file is symlinked. * * @since 1.7.1 * @access public * @author Grégory Viguier * * @param string $file_path An absolute path. * @return bool */ public function is_symlinked( $file_path ) { static $site_root; static $plugin_paths = array(); global $wp_plugin_paths; if ( ! $file_path ) { return false; } $real_path = realpath( $file_path ); if ( ! $real_path ) { return false; } if ( ! isset( $site_root ) ) { $site_root = $this->normalize_path_for_comparison( $this->get_site_root() ); } $lower_file_path = $this->normalize_path_for_comparison( $real_path ); if ( strpos( $lower_file_path, $site_root ) !== 0 ) { return true; } if ( $wp_plugin_paths && is_array( $wp_plugin_paths ) ) { if ( ! $plugin_paths ) { foreach ( $wp_plugin_paths as $dir => $real_dir ) { $dir = $this->normalize_path_for_comparison( $dir ); $plugin_paths[ $dir ] = $this->normalize_path_for_comparison( $real_dir ); } } $lower_file_path = $this->normalize_path_for_comparison( $file_path ); foreach ( $plugin_paths as $dir => $real_dir ) { if ( strpos( $lower_file_path, $dir ) === 0 ) { return true; } } } return false; } /** * Tell if a file is a pdf. * * @since 1.8 * @access public * @author Grégory Viguier * * @param string $file_path Path to the file. * @return bool */ public function is_pdf( $file_path ) { if ( function_exists( 'finfo_fopen' ) ) { $finfo = finfo_open( FILEINFO_MIME ); if ( $finfo ) { $mimetype = finfo_file( $finfo, $file_path ); if ( false !== $mimetype ) { return 'application/pdf' === $mimetype; } } } if ( function_exists( 'mime_content_type' ) ) { $mimetype = mime_content_type( $file_path ); return 'application/pdf' === $mimetype; } return false; } /** ----------------------------------------------------------------------------------------- */ /** CLASS OVERWRITES ======================================================================== */ /** ----------------------------------------------------------------------------------------- */ /** * Move a file and apply chmod. * If the file failed to be moved once, a 2nd attempt is made after applying chmod. * * @since 1.8 * @access public * @author Grégory Viguier * * @param string $source Path to the file to move. * @param string $destination Path to the destination. * @param bool $overwrite Allow to overwrite existing file at destination. * @return bool True on success, false on failure. */ public function move( $source, $destination, $overwrite = false ) { if ( parent::move( $source, $destination, $overwrite ) ) { return $this->chmod_file( $destination ); } if ( ! $this->chmod_file( $destination ) ) { return false; } if ( parent::move( $source, $destination, $overwrite ) ) { return $this->chmod_file( $destination ); } return false; } /** * Determine if a file or directory is writable. * This function is used to work around certain ACL issues in PHP primarily affecting Windows Servers. * Replacement for is_writable(). * * @since 1.7.1 * @access public * @author Grégory Viguier * * @param string $file_path Path to the file. * @return bool */ public function is_writable( $file_path ) { if ( ! $file_path ) { return false; } return wp_is_writable( $file_path ); } /** ----------------------------------------------------------------------------------------- */ /** WORK WITH IMAGES ======================================================================== */ /** ----------------------------------------------------------------------------------------- */ /** * Tell if a file is an image. * * @since 1.8 * @access public * @author Grégory Viguier * * @param string $file_path Path to the file. * @return bool */ public function is_image( $file_path ) { if ( function_exists( 'finfo_fopen' ) ) { $finfo = finfo_open( FILEINFO_MIME ); if ( $finfo ) { $mimetype = finfo_file( $finfo, $file_path ); if ( false !== $mimetype ) { return strpos( $mimetype, 'image/' ) === 0; } } } if ( function_exists( 'exif_imagetype' ) ) { $mimetype = exif_imagetype( $file_path ); return (bool) $mimetype; } if ( function_exists( 'mime_content_type' ) ) { $mimetype = mime_content_type( $file_path ); return strpos( $mimetype, 'image/' ) === 0; } return false; } /** * Get an image data. * Replacement for getimagesize(). * * @since 1.7.1 * @access public * @author Grégory Viguier * * @param string $file_path Path to the file. * @return array The image data. An empty array on failure. */ public function get_image_size( $file_path ) { if ( ! $file_path ) { return array(); } $size = @getimagesize( $file_path ); if ( ! $size || ! isset( $size[0], $size[1] ) ) { return array(); } return array( 0 => (int) $size[0], 1 => (int) $size[1], 'width' => (int) $size[0], 'height' => (int) $size[1], 'type' => (int) $size[2], 'attr' => $size[3], 'channels' => isset( $size['channels'] ) ? (int) $size['channels'] : null, 'bits' => isset( $size['bits'] ) ? (int) $size['bits'] : null, 'mime' => $size['mime'], ); } /** * Tell if exif_read_data() is available. * * @since 1.7.1 * @access public * @author Grégory Viguier * * @return bool */ public function can_get_exif() { static $callable; if ( ! isset( $callable ) ) { $callable = is_callable( 'exif_read_data' ); } return $callable; } /** * Get the EXIF headers from an image file. * Replacement for exif_read_data(). * * @since 1.7.1 * @access public * @author Grégory Viguier * @see https://secure.php.net/manual/en/function.exif-read-data.php * * @param string $file_path Path to the file. * @param string $sections A comma separated list of sections that need to be present in file to produce a result array. See exif_read_data() documentation for values: FILE, COMPUTED, ANY_TAG, IFD0, THUMBNAIL, COMMENT, EXIF. * @param bool $arrays Specifies whether or not each section becomes an array. The sections COMPUTED, THUMBNAIL, and COMMENT always become arrays as they may contain values whose names conflict with other sections. * @param bool $thumbnail When set to TRUE the thumbnail itself is read. Otherwise, only the tagged data is read. * @return array The EXIF headers. An empty array on failure. */ public function get_image_exif( $file_path, $sections = null, $arrays = false, $thumbnail = false ) { if ( ! $file_path || ! $this->can_get_exif() ) { return array(); } $exif = @exif_read_data( $file_path, $sections, $arrays, $thumbnail ); return is_array( $exif ) ? $exif : array(); } /** * Tell if a file is an animated gif. * * @since 1.9.5 * @access public * @source https://www.php.net/manual/en/function.imagecreatefromgif.php#104473 * @author Grégory Viguier * * @param string $file_path Path to the file. * @return bool|null Null if the file cannot be read. */ public function is_animated_gif( $file_path ) { if ( $this->path_info( $file_path, 'extension' ) !== 'gif' ) { // Not a gif file. return false; } $fh = @fopen( $file_path, 'rb' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen if ( ! $fh ) { // Could not open the file. return null; } /** * An animated gif contains multiple "frames", with each frame having a header made up of: * - a static 4-byte sequence (\x00\x21\xF9\x04), * - 4 variable bytes, * - a static 2-byte sequence (\x00\x2C) (some variants may use \x00\x21 ?). */ $count = 0; // We read through the file til we reach the end of the file, or we've found at least 2 frame headers. while ( ! feof( $fh ) && $count < 2 ) { // Read 100kb at a time. $chunk = fread( $fh, 1024 * 100 ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fread $count += preg_match_all( '#\x00\x21\xF9\x04.{4}\x00(\x2C|\x21)#s', $chunk, $matches ); } fclose( $fh ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose return $count > 1; } /** ----------------------------------------------------------------------------------------- */ /** WORK WITH PATHS ========================================================================= */ /** ----------------------------------------------------------------------------------------- */ /** * Make an absolute path relative to WordPress' root folder. * Also works for files from registered symlinked plugins. * * @since 1.7.1 * @access public * @author Grégory Viguier * * @param string $file_path An absolute path. * @param string $base A base path to use instead of ABSPATH. * @return string|bool A relative path. Can return the absolute path or false in case of a failure. */ public function make_path_relative( $file_path, $base = '' ) { global $wp_plugin_paths; if ( ! $file_path ) { return false; } $file_path = wp_normalize_path( $file_path ); $base = $base ? $this->normalize_dir_path( $base ) : $this->get_site_root(); $pos = strpos( $file_path, $base ); if ( false === $pos && $wp_plugin_paths && is_array( $wp_plugin_paths ) ) { // The file is probably part of a symlinked plugin. arsort( $wp_plugin_paths ); foreach ( $wp_plugin_paths as $dir => $real_dir ) { if ( strpos( $file_path, $real_dir ) === 0 ) { $file_path = wp_normalize_path( $dir . substr( $file_path, strlen( $real_dir ) ) ); } } $pos = strpos( $file_path, $base ); } if ( false === $pos ) { // We're in trouble. return $file_path; } return substr_replace( $file_path, '', 0, $pos + strlen( $base ) ); } /** * Normalize a directory path. * The path is normalized and a trailing slash is added. * * @since 1.7.1 * @access public * @author Grégory Viguier * * @param string $file_path The file path. * @return string The normalized dir path. */ public function normalize_dir_path( $file_path ) { return wp_normalize_path( trailingslashit( $file_path ) ); } /** * Normalize a file path, aiming for path comparison. * The path is normalized, case-lowered, and a trailing slash is added. * * @since 1.7.1 * @access public * @author Grégory Viguier * * @param string $file_path The file path. * @return string The normalized file path. */ public function normalize_path_for_comparison( $file_path ) { return strtolower( $this->normalize_dir_path( $file_path ) ); } /** ----------------------------------------------------------------------------------------- */ /** SOME WELL KNOWN PATHS AND URLS ========================================================== */ /** ----------------------------------------------------------------------------------------- */ /** * Tell if WordPress is installed in its own directory: aka WP's path !== site's path. * * @since 1.8.1 * @access public * @see https://codex.wordpress.org/Giving_WordPress_Its_Own_Directory * @author Grégory Viguier * * @return string */ public function has_wp_its_own_directory() { return $this->get_abspath() !== $this->get_site_root(); } /** * The path to the server's root is not always '/', it can also be '//' or 'C://'. * I am get_root. * * @since 1.7.1 * @access public * @author Grégory Viguier * * @return string The path to the server's root. */ public function get_root() { static $groot; if ( isset( $groot ) ) { return $groot; } $groot = preg_replace( '@^((?:.:)?/+).*@', '$1', $this->get_site_root() ); return $groot; } /** * Tell if a path is the server's root. * * @since 1.7.1 * @access public * @author Grégory Viguier * * @param string $path The path. * @return bool */ public function is_root( $path ) { $path = rtrim( $path, '/\\' ); return '.' === $path || '' === $path || preg_match( '@^.:$@', $path ); } /** * Get the path to the site's root. * This is an improved version of get_home_path() that *should* work in almost every cases. * Because creating a constant like ABSPATH was too simple. * * @since 1.8.1 * @access public * @see get_home_path() * @author Grégory Viguier * * @return string */ public function get_site_root() { static $root_path; if ( isset( $root_path ) ) { return $root_path; } /** * Filter the path to the site's root. * * @since 1.8.1 * @author Grégory Viguier * * @param string $root_path Path to the site's root. Default is null. */ $root_path = apply_filters( 'imagify_site_root', null ); if ( is_string( $root_path ) ) { $root_path = trailingslashit( wp_normalize_path( $root_path ) ); return $root_path; } $home = set_url_scheme( untrailingslashit( get_option( 'home' ) ), 'http' ); $siteurl = set_url_scheme( untrailingslashit( get_option( 'siteurl' ) ), 'http' ); if ( ! empty( $home ) && 0 !== strcasecmp( $home, $siteurl ) ) { $wp_path_rel_to_home = str_ireplace( $home, '', $siteurl ); /* $siteurl - $home */ $pos = strripos( str_replace( '\\', '/', ABSPATH ), trailingslashit( $wp_path_rel_to_home ) ); $root_path = substr( ABSPATH, 0, $pos ); $root_path = trailingslashit( wp_normalize_path( $root_path ) ); return $root_path; } if ( ! defined( 'PATH_CURRENT_SITE' ) || ! is_multisite() || is_main_site() ) { $root_path = $this->get_abspath(); return $root_path; } /** * For a multisite in its own directory, get_home_path() returns the expected path only for the main site. * * Friend, each time an attempt is made to improve this method, and especially this part, please increment the following counter. * Improvement attempts: 3. */ $document_root = realpath( wp_unslash( $_SERVER['DOCUMENT_ROOT'] ) ); // `realpath()` is needed for those cases where $_SERVER['DOCUMENT_ROOT'] is totally different from ABSPATH. $document_root = trailingslashit( str_replace( '\\', '/', $document_root ) ); $path_current_site = trim( str_replace( '\\', '/', PATH_CURRENT_SITE ), '/' ); $root_path = trailingslashit( wp_normalize_path( $document_root . $path_current_site ) ); return $root_path; } /** * Get the URL of the site's root. It corresponds to the main site's home page URL. * * @since 1.8.1 * @access public * @author Grégory Viguier * * @return string */ public function get_site_root_url() { static $root_url; if ( isset( $root_url ) ) { return $root_url; } if ( ! is_multisite() || is_main_site() ) { $root_url = home_url( '/' ); return $root_url; } $current_network = false; if ( function_exists( 'get_network' ) ) { $current_network = get_network(); } elseif ( function_exists( 'get_current_site' ) ) { $current_network = get_current_site(); } if ( ! $current_network ) { $root_url = home_url( '/' ); return $root_url; } $root_url = is_ssl() ? 'https' : 'http'; $root_url = set_url_scheme( 'http://' . $current_network->domain . $current_network->path, $root_url ); $root_url = trailingslashit( $root_url ); return $root_url; } /** * Tell if a path is the site's root. * * @since 1.8.1 * @access public * @author Grégory Viguier * * @param string $path The path. * @return bool */ public function is_site_root( $path ) { return $this->normalize_dir_path( $path ) === $this->get_site_root(); } /** * Get a clean value of ABSPATH. * * @since 1.7.1 * @access public * @author Grégory Viguier * * @return string The path to WordPress' root folder. */ public function get_abspath() { static $abspath; if ( isset( $abspath ) ) { return $abspath; } $abspath = wp_normalize_path( ABSPATH ); // Make sure ABSPATH is not messed up: it could be defined as a relative path for example (yeah, I know, but we've seen it). $test_file = wp_normalize_path( IMAGIFY_FILE ); $pos = strpos( $test_file, $abspath ); if ( $pos > 0 ) { // ABSPATH has a wrong value. $abspath = substr( $test_file, 0, $pos ) . $abspath; } elseif ( false === $pos && class_exists( 'ReflectionClass' ) ) { // Imagify is symlinked (dude, you look for trouble). $reflector = new ReflectionClass( 'WP' ); $test_file = $reflector->getFileName(); $pos = strpos( $test_file, $abspath ); if ( 0 < $pos ) { // ABSPATH has a wrong value. $abspath = substr( $test_file, 0, $pos ) . $abspath; } } $abspath = trailingslashit( $abspath ); if ( '/' !== substr( $abspath, 0, 1 ) && ':' !== substr( $abspath, 1, 1 ) ) { $abspath = '/' . $abspath; } return $abspath; } /** * Tell if a path is WP's root (ABSPATH). * * @since 1.7.1 * @access public * @author Grégory Viguier * * @param string $path The path. * @return bool */ public function is_abspath( $path ) { return $this->normalize_dir_path( $path ) === $this->get_abspath(); } /** * Get the upload basedir. * * @since 1.7.1 * @access public * @author Grégory Viguier * * @param bool $bypass_error True to return the path even if there is an error. This is used when we want to display this path in a message for example. * @return string|bool The path. False on failure. */ public function get_upload_basedir( $bypass_error = false ) { static $upload_basedir; static $upload_basedir_or_error; if ( isset( $upload_basedir ) ) { return $bypass_error ? $upload_basedir : $upload_basedir_or_error; } $uploads = wp_upload_dir(); $upload_basedir = $this->normalize_dir_path( $uploads['basedir'] ); if ( false !== $uploads['error'] ) { $upload_basedir_or_error = false; } else { $upload_basedir_or_error = $upload_basedir; } return $bypass_error ? $upload_basedir : $upload_basedir_or_error; } /** * Get the upload baseurl. * * @since 1.7.1 * @access public * @author Grégory Viguier * * @return string|bool The URL. False on failure. */ public function get_upload_baseurl() { static $upload_baseurl; if ( isset( $upload_baseurl ) ) { return $upload_baseurl; } $uploads = wp_upload_dir(); if ( false !== $uploads['error'] ) { $upload_baseurl = false; return $upload_baseurl; } $upload_baseurl = trailingslashit( $uploads['baseurl'] ); return $upload_baseurl; } /** * Get the path to the uploads base directory of the main site. * * @since 1.8 * @access public * @author Grégory Viguier * * @return string */ public function get_main_upload_basedir() { static $basedir; if ( isset( $basedir ) ) { return $basedir; } $basedir = get_imagify_upload_basedir( true ); if ( is_multisite() ) { $pattern = '/' . $this->get_multisite_uploads_subdir_pattern() . '$'; $basedir = preg_replace( self::PATTERN_DELIMITER . $pattern . self::PATTERN_DELIMITER, '/', $basedir ); } return $basedir; } /** * Get the URL of the uploads base directory of the main site. * * @since 1.8 * @access public * @author Grégory Viguier * * @return string */ public function get_main_upload_baseurl() { static $baseurl; if ( isset( $baseurl ) ) { return $baseurl; } $baseurl = get_imagify_upload_baseurl( true ); if ( is_multisite() ) { $pattern = '/' . $this->get_multisite_uploads_subdir_pattern() . '$'; $baseurl = preg_replace( self::PATTERN_DELIMITER . $pattern . self::PATTERN_DELIMITER, '/', $baseurl ); } return $baseurl; } /** * Get the regex pattern used to match the uploads subdir on multisite in a file path. * Pattern delimiter is `Imagify_Filesystem::PATTERN_DELIMITER`. * Paths tested against these patterns are lower-cased. * * @since 1.8 * @access public * @see _wp_upload_dir() * @author Grégory Viguier * * @return string */ public function get_multisite_uploads_subdir_pattern() { static $pattern; if ( isset( $pattern ) ) { return $pattern; } $pattern = ''; if ( ! is_multisite() ) { return $pattern; } if ( ! get_site_option( 'ms_files_rewriting' ) ) { if ( defined( 'MULTISITE' ) ) { $pattern = 'sites/\d+/'; } else { $pattern = '\d+/'; } } elseif ( defined( 'UPLOADS' ) ) { $site_id = (string) get_current_blog_id(); $path = $this->get_upload_basedir( true ); // Something like `/absolute/path/to/wp-content/blogs.dir/3/files/`, also for site 1. $path = strrev( $path ); if ( preg_match( self::PATTERN_DELIMITER . '^.*' . strrev( $site_id ) . '[^/]*/' . self::PATTERN_DELIMITER . 'U', $path, $matches ) ) { $pattern = end( $matches ); $pattern = ltrim( strtolower( strrev( $pattern ) ), '/' ); $pattern = str_replace( $site_id, '\d+', $pattern ); } } /** * Filter the regex pattern used to match the uploads subdir on multisite in a file path. * Pattern delimiter is `Imagify_Filesystem::PATTERN_DELIMITER`. * Important: lowercase, no heading slash, mandatory trailing slash. * * @since 1.8 * @author Grégory Viguier * * @param string $pattern The regex pattern. */ $pattern = apply_filters( 'imagify_multisite_uploads_subdir_pattern', $pattern ); return $pattern; } }