ray_unique( $extensions ); } /** * Get the names of the HTML attributes where WP Rocket must search for image files. * * @since 3.4 * * @return array */ private function get_attribute_names() { $attributes = [ 'href', 'src', 'srcset', 'data-large_image', 'data-thumb' ]; /** * Filter the names of the HTML attributes where WP Rocket must search for image files. * Don't prepend new names with `data-`, WPR will do it. For example if you want to add `data-foo-bar`, you only need to add `foo-bar` or `bar` to the list. * * @since 3.4 * * @param array $attributes An array of HTML attribute names. */ $attributes = apply_filters( 'rocket_attributes_for_webp', $attributes ); $attributes = array_filter( (array) $attributes, function( $attributes ) { return $attributes && is_string( $attributes ); } ); return array_unique( $attributes ); } /** * Convert a URL to an absolute path. * * @since 3.4 * * @param string $url URL to convert. * @return string|bool */ private function url_to_path( $url ) { static $hosts, $site_host, $subdir_levels; $url_host = wp_parse_url( $url, PHP_URL_HOST ); // Relative path. if ( null === $url_host ) { if ( ! isset( $subdir_levels ) ) { $subdir_levels = substr_count( preg_replace( '@^https?://@', '', site_url() ), '/' ); } if ( $subdir_levels ) { $url = ltrim( $url, '/' ); $url = explode( '/', $url ); array_splice( $url, 0, $subdir_levels ); $url = implode( '/', $url ); } $url = site_url( $url ); } // CDN. if ( ! isset( $hosts ) ) { $hosts = $this->cdn_subscriber->get_cdn_hosts( [], [ 'all', 'images' ] ); $hosts = array_flip( $hosts ); } if ( isset( $hosts[ $url_host ] ) ) { if ( ! isset( $site_host ) ) { $site_host = wp_parse_url( site_url( '/' ), PHP_URL_HOST ); } if ( $site_host ) { $url = preg_replace( '@^(https?://)' . $url_host . '/@', '$1' . $site_host . '/', $url ); } } // URL to path. $url = preg_replace( '@^https?:@', '', $url ); $paths = $this->get_url_to_path_associations(); if ( ! $paths ) { // Uh? return false; } foreach ( $paths as $asso_url => $asso_path ) { if ( 0 === strpos( $url, $asso_url ) ) { $file = str_replace( $asso_url, $asso_path, $url ); break; } } if ( empty( $file ) ) { return false; } /** This filter is documented in inc/functions/formatting.php. */ return (string) apply_filters( 'rocket_url_to_path', $file, $url ); } /** * Add a webp extension to a URL. * * @since 3.4 * * @param string $url A URL (I see you're very surprised). * @param string $extensions Allowed image extensions. * @return string|bool The same URL with a webp extension if the file exists. False if the webp image doesn't exist. */ private function url_to_webp( $url, $extensions ) { if ( ! preg_match( '@^(?.+\.(?' . $extensions . '))(?(?:\?.*)?)$@i', $url, $src_url ) ) { // Probably something like "image.jpg.webp". return false; } $src_path = $this->url_to_path( $src_url['src'] ); if ( empty( $src_path ) ) { return false; } $src_path_webp = preg_replace( '@\.' . $src_url['extension'] . '$@', '.webp', $src_path ); if ( $this->filesystem->exists( $src_path_webp ) ) { // File name: image.jpg => image.webp. return preg_replace( '@\.' . $src_url['extension'] . '$@', '.webp', $src_url['src'] ) . $src_url['query']; } if ( $this->filesystem->exists( $src_path . '.webp' ) ) { // File name: image.jpg => image.jpg.webp. return $src_url['src'] . '.webp' . $src_url['query']; } return false; } /** * Add webp extension to URLs in a srcset attribute. * * @since 3.4 * * @param array|string $srcset_values Value of a srcset attribute. * @param string $extensions Allowed image extensions. * @return string|bool An array similar to $srcset_values, with webp extensions when the files exist. False if no images have webp versions. */ private function srcset_to_webp( $srcset_values, $extensions ) { if ( ! $srcset_values ) { return false; } if ( ! is_array( $srcset_values ) ) { $srcset_values = explode( ',', $srcset_values ); } $has_webp = false; foreach ( $srcset_values as $i => $srcset_value ) { $srcset_value = preg_split( '/\s+/', trim( $srcset_value ) ); if ( count( $srcset_value ) > 2 ) { // Not a good idea to have space characters in file name. $descriptor = array_pop( $srcset_value ); $srcset_value = [ 'url' => implode( ' ', $srcset_value ), 'descriptor' => $descriptor, ]; } else { $srcset_value = [ 'url' => $srcset_value[0], 'descriptor' => ! empty( $srcset_value[1] ) ? $srcset_value[1] : '1x', ]; } $url_webp = $this->url_to_webp( $srcset_value['url'], $extensions ); if ( ! $url_webp ) { $srcset_values[ $i ] = implode( ' ', $srcset_value ); continue; } $srcset_values[ $i ] = $url_webp . ' ' . $srcset_value['descriptor']; $has_webp = true; } if ( ! $has_webp ) { return false; } return implode( ',', $srcset_values ); } /** * Get a list of URL/path associations. * URLs are schema-less, starting by a double slash. * * @since 3.4 * * @return array A list of URLs as keys and paths as values. */ private function get_url_to_path_associations() { static $list; if ( isset( $list ) ) { return $list; } $content_url = preg_replace( '@^https?:@', '', content_url( '/' ) ); $content_dir = trailingslashit( rocket_get_constant( 'WP_CONTENT_DIR' ) ); $list = [ $content_url => $content_dir ]; $upload = wp_upload_dir(); $upload_dir = trailingslashit( $upload['basedir'] ); if ( strpos( $upload_dir, $content_dir ) === false ) { $upload_url = preg_replace( '@^https?:@', '', trailingslashit( $upload['baseurl'] ) ); $list[ $upload_url ] = $upload_dir; } /** * Filter the list of URL/path associations. * The URLs with the most levels must come first. * * @since 3.4 * * @param array $list The list of URL/path associations. URLs are schema-less, starting by a double slash. */ $list = apply_filters( 'rocket_url_to_path_associations', $list ); $list = array_filter( $list, function( $path, $url ) { return $path && $url && is_string( $path ) && is_string( $url ); }, ARRAY_FILTER_USE_BOTH ); if ( $list ) { $list = array_unique( $list ); } return $list; } /** * Get a list of plugins that serve webp images on frontend. * If the CDN is used, this won't list plugins that use a technique not compatible with CDN. * * @since 3.4 * * @return array The WebP plugin names. */ private function get_plugins_serving_webp() { $webp_plugins = $this->get_webp_plugins(); if ( ! $webp_plugins ) { // Somebody probably messed up. return []; } $checks = []; $is_using_cdn = $this->is_using_cdn(); foreach ( $webp_plugins as $plugin ) { if ( $is_using_cdn && $plugin->is_serving_webp_compatible_with_cdn() ) { $checks[ $plugin->get_id() ] = $plugin->get_name(); } elseif ( ! $is_using_cdn && $plugin->is_serving_webp() ) { $checks[ $plugin->get_id() ] = $plugin->get_name(); } } return $checks; } /** * Change Youtube thumbnail extension. * * @param string $extension extension from the thumbnail. * * @return string */ public function change_youtube_thumbnail( $extension ) { if ( ! $this->options_data->get( 'cache_webp', 0 ) ) { return $extension; } return 'webp'; } }