From cc1f2befc43cfc44401d8cafa07a1e914921bbf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Perona?= Date: Wed, 13 Jun 2018 12:24:44 -0400 Subject: [PATCH 1/9] add abstract optimization class --- .../class-abstract-optimization.php | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 inc/classes/optimization/class-abstract-optimization.php diff --git a/inc/classes/optimization/class-abstract-optimization.php b/inc/classes/optimization/class-abstract-optimization.php new file mode 100644 index 0000000000..72f84550c0 --- /dev/null +++ b/inc/classes/optimization/class-abstract-optimization.php @@ -0,0 +1,147 @@ +crawler = $crawler; + } + + /** + * Finds nodes matching the pattern in the HTML + * + * @since 3.1 + * @author Remy Perona + * + * @param string $pattern Pattern to match. + * @return bool|HtmlPageCrawler + */ + protected function find( $pattern ) { + try { + $nodes = $this->crawler->filter( $pattern ); + } catch ( Exception $e ) { + return false; + } + + if ( 0 === $nodes->count() ) { + return false; + } + + return $nodes; + } + + /** + * Determines if the file is external + * + * @since 2.11 + * @author Remy Perona + * + * @param string $url URL of the file. + * @return bool True if external, false otherwise + */ + protected function is_external_file( $url ) { + $file = get_rocket_parse_url( $url ); + $wp_content = get_rocket_parse_url( WP_CONTENT_URL ); + $hosts = get_rocket_cnames_host( $this->get_zones() ); + $hosts[] = $wp_content['host']; + $langs = get_rocket_i18n_uri(); + + // Get host for all langs. + if ( $langs ) { + foreach ( $langs as $lang ) { + $hosts[] = rocket_extract_url_component( $lang, PHP_URL_HOST ); + } + } + + $hosts_index = array_flip( array_unique( $hosts ) ); + + // URL has domain and domain is not part of the internal domains. + if ( isset( $file['host'] ) && ! empty( $file['host'] ) && ! isset( $hosts_index[ $file['host'] ] ) ) { + return true; + } + + // URL has no domain and doesn't contain the WP_CONTENT path or wp-includes. + if ( ! isset( $file['host'] ) && ! preg_match( '#(' . $wp_content['path'] . '|wp-includes)#', $file['path'] ) ) { + return true; + } + + return false; + } + + /** + * Writes the minified content to a file + * + * @since 2.11 + * @author Remy Perona + * + * @param string $content Minified content. + * @param string $minified_file Path to the minified file to write in. + * @return bool True if successful, false otherwise + */ + protected function write_minify_file( $content, $minified_file ) { + if ( rocket_direct_filesystem()->exists( $minified_file ) ) { + return true; + } + + if ( ! rocket_mkdir_p( dirname( $minified_file ) ) ) { + return false; + } + + return rocket_put_content( $minified_file, $content ); + } + + /** + * Gets the file path from an URL + * + * @since 3.1 + * @author Remy Perona + * + * @param string $url File URL. + * @return string + */ + protected function get_file_path( $url ) { + $hosts = get_rocket_cnames_host( $this->get_zones() ); + $hosts['home'] = rocket_extract_url_component( home_url(), PHP_URL_HOST ); + $hosts_index = array_flip( $hosts ); + + return rocket_url_to_path( strtok( $url, '?' ), $hosts_index ); + } + + /** + * Gets content of a file + * + * @since 3.1 + * @author Remy Perona + * + * @param string $file File path. + * @return string + */ + protected function get_file_content( $file ) { + return rocket_direct_filesystem()->get_contents( $file ); + } +} From d14c92711468389c1513a643bbf1fc2516b5fe16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Perona?= Date: Wed, 13 Jun 2018 12:24:55 -0400 Subject: [PATCH 2/9] add abstract CSS optimization class --- .../CSS/class-abstract-css-optimization.php | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 inc/classes/optimization/CSS/class-abstract-css-optimization.php diff --git a/inc/classes/optimization/CSS/class-abstract-css-optimization.php b/inc/classes/optimization/CSS/class-abstract-css-optimization.php new file mode 100644 index 0000000000..e72a18a7f9 --- /dev/null +++ b/inc/classes/optimization/CSS/class-abstract-css-optimization.php @@ -0,0 +1,142 @@ +options = $options; + $this->minify_key = $this->options->get( 'minify_css_key', create_rocket_uniqid() ); + $this->excluded_files = $this->get_excluded_files(); + $this->minify_base_path = WP_ROCKET_MINIFY_CACHE_PATH . get_current_blog_id() . '/'; + $this->minify_base_url = WP_ROCKET_MINIFY_CACHE_URL . get_current_blog_id() . '/'; + } + + /** + * Get all files to exclude from minification/concatenation. + * + * @since 2.11 + * @author Remy Perona + * + * @return string + */ + protected function get_excluded_files() { + $excluded_files = $this->options->get( 'exclude_css', array() ); + + /** + * Filters CSS files to exclude from minification/concatenation. + * + * @since 2.6 + * + * @param array $excluded_files List of excluded CSS files. + */ + $excluded_files = apply_filters( 'rocket_exclude_css', $excluded_files ); + + if ( empty( $excluded_files ) ) { + return ''; + } + + foreach ( $excluded_files as $i => $excluded_file ) { + $excluded_files[ $i ] = str_replace( '#', '\#', $excluded_file ); + } + + return implode( '|', $excluded_files ); + } + + /** + * Returns the CDN zones. + * + * @since 3.1 + * @author Remy Perona + * + * @return array + */ + public function get_zones() { + return array( 'all', 'css_and_js', self::FILE_TYPE ); + } + + /** + * Gets the minify URL + * + * @since 3.1 + * @author Remy Perona + * + * @param string $filename Minified filename. + * @return string + */ + protected function get_minify_url( $filename ) { + $minify_url = get_rocket_cdn_url( $this->minify_base_url . $filename, $this->get_zones() ); + + /** + * Filters CSS file URL with CDN hostname + * + * @since 2.1 + * + * @param string $minify_url Minified file URL. + */ + return apply_filters( 'rocket_css_url', $minify_url ); + } + + /** + * Determines if it is a file excluded from minification + * + * @since 2.11 + * @author Remy Perona + * + * @param HtmlPageCrawler $node Node corresponding to a CSS file. + * @return bool True if it is a file excluded, false otherwise + */ + protected function is_minify_excluded_file( $node ) { + // File should not be minified. + if ( $node->attr( 'data-minify' ) || $node->attr( 'data-no-minify' ) ) { + return true; + } + + $media = $node->attr( 'media' ); + + if ( $media && ! preg_match( '/(all|screen)/iU', $media ) ) { + return true; + } + + if ( $media && false !== strpos( $media, 'only screen and' ) ) { + return true; + } + + $file_path = rocket_extract_url_component( $node->attr( 'href' ), PHP_URL_PATH ); + + // File extension is not css. + if ( pathinfo( $file_path, PATHINFO_EXTENSION ) !== self::FILE_TYPE ) { + return true; + } + + if ( ! empty( $this->excluded_files ) ) { + // File is excluded from minification/concatenation. + if ( preg_match( '#^(' . $this->excluded_files . ')$#', $file_path ) ) { + return true; + } + } + + return false; + } +} From eab7c54111f411aa8cd791b82383a9bde84574d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Perona?= Date: Wed, 13 Jun 2018 12:25:04 -0400 Subject: [PATCH 3/9] add CSS minify class --- inc/classes/optimization/CSS/class-minify.php | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 inc/classes/optimization/CSS/class-minify.php diff --git a/inc/classes/optimization/CSS/class-minify.php b/inc/classes/optimization/CSS/class-minify.php new file mode 100644 index 0000000000..d3064096d7 --- /dev/null +++ b/inc/classes/optimization/CSS/class-minify.php @@ -0,0 +1,136 @@ +find( 'link[href*=".css"]' ); + + if ( ! $nodes ) { + return $this->crawler->saveHTML(); + } + + $nodes->each( function( \Wa72\HtmlPageDom\HtmlPageCrawler $node, $i ) { + $src = $node->attr( 'href' ); + + if ( preg_match( '/(?:-|\.)min.css/iU', $src ) ) { + return; + } + + if ( $this->is_external_file( $src ) ) { + return; + } + + if ( $this->is_minify_excluded_file( $node ) ) { + return; + } + + $minify_url = $this->replace_url( $src ); + + if ( ! $minify_url ) { + return; + } + + $node->attr( 'href', $minify_url ); + $node->attr( 'data-minify', '1' ); + + return; + } ); + + return $this->crawler->saveHTML(); + } + + /** + * Creates the minify URL if the minification is successful + * + * @since 2.11 + * @author Remy Perona + * + * @param string $url Original file URL. + + * @return string|bool The minify URL if successful, false otherwise + */ + private function replace_url( $url ) { + if ( empty( $url ) ) { + return false; + } + + $unique_id = md5( $url . $this->minify_key ); + $filename = preg_replace( '/\.(css)$/', '-' . $unique_id . '.css', ltrim( rocket_realpath( rocket_extract_url_component( $url, PHP_URL_PATH ) ), '/' ) ); + + $minified_file = $this->minify_base_path . $filename; + + if ( ! rocket_direct_filesystem()->exists( $minified_file ) ) { + $minified_content = $this->minify( $this->get_file_path( $url ) ); + + if ( ! $minified_content ) { + return false; + } + + $save_minify_file = $this->write_minify_file( $minified_content, $minified_file ); + + if ( ! $save_minify_file ) { + return false; + } + } + + return $this->get_minify_url( $filename ); + } + + /** + * Minifies the content + * + * @since 2.11 + * @author Remy Perona + * + * @param string|array $file File to minify. + * @return string|bool Minified content, false if empty + */ + protected function minify( $file ) { + $file_content = $this->get_file_content( $file ); + + if ( ! $file_content ) { + return false; + } + + $file_content = $this->rewrite_paths( $file, $file_content ); + $minifier = $this->get_minifier( $file_content ); + $minified_content = $minifier->minify(); + + if ( empty( $minified_content ) ) { + return false; + } + + return $minified_content; + } + + /** + * Returns a new minifier instance + * + * @since 3.1 + * @author Remy Perona + * + * @param string $file_content Content to minify. + * @return Minifier\CSS + */ + protected function get_minifier( $file_content ) { + return new Minifier\CSS( $file_content ); + } +} From 2b36e5285b2ba969e161c42647d5657c7faa6790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Perona?= Date: Wed, 13 Jun 2018 12:25:13 -0400 Subject: [PATCH 4/9] add CSS combine class --- .../optimization/CSS/class-combine.php | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 inc/classes/optimization/CSS/class-combine.php diff --git a/inc/classes/optimization/CSS/class-combine.php b/inc/classes/optimization/CSS/class-combine.php new file mode 100644 index 0000000000..d722b5bd78 --- /dev/null +++ b/inc/classes/optimization/CSS/class-combine.php @@ -0,0 +1,182 @@ +minifier = $minifier; + } + + /** + * Combines multiple Google Fonts links into one + * + * @since 3.1 + * @author Remy Perona + * + * @return string + */ + public function optimize() { + $nodes = $this->find( 'link[href*=".css"]' ); + + if ( ! $nodes ) { + return $this->crawler->saveHTML(); + } + + $combine_nodes = $nodes->each( function( \Wa72\HtmlPageDom\HtmlPageCrawler $node, $i ) { + $src = $node->attr( 'href' ); + + if ( $this->is_external_file( $src ) ) { + return; + } + + if ( $this->is_minify_excluded_file( $node ) ) { + return; + } + + return $node; + } ); + + if ( empty( $combine_nodes ) ) { + return $this->crawler->saveHTML(); + } + + $urls = array_map( function( $node ) { + return $node->attr( 'href' ); + }, $combine_nodes ); + + $minify_url = $this->combine( $urls ); + + if ( ! $minify_url ) { + return $this->crawler->saveHTML(); + } + + if ( ! $this->inject_combined_url( $minify_url ) ) { + return $this->crawler->saveHTML(); + } + + foreach ( $combine_nodes as $node ) { + $node->remove(); + } + + return $this->crawler->saveHTML(); + } + + /** + * Adds the combined CSS URL to the HTML + * + * @since 3.1 + * @author Remy Perona + * + * @param string $minify_url URL to insert. + * @return bool + */ + protected function inject_combined_url( $minify_url ) { + try { + $this->crawler->filter( 'head' )->prepend( '' ); + } catch ( Exception $e ) { + return false; + } + + return true; + } + + /** + * Creates the minify URL if the minification is successful + * + * @since 2.11 + * @author Remy Perona + * + * @param string $urls Original file URL. + + * @return string|bool The minify URL if successful, false otherwise + */ + protected function combine( $urls ) { + if ( empty( $urls ) ) { + return false; + } + + foreach ( $urls as $url ) { + $file_path[] = $this->get_file_path( $url ); + } + + $file_hash = implode( ',', $urls ); + $filename = md5( $file_hash . $this->minify_key ) . '.css'; + + $minified_file = $this->minify_base_path . $filename; + + if ( ! rocket_direct_filesystem()->exists( $minified_file ) ) { + $minified_content = $this->minify( $file_path ); + + if ( ! $minified_content ) { + return false; + } + + $minify_filepath = $this->write_minify_file( $minified_content, $minified_file ); + + if ( ! $minify_filepath ) { + return false; + } + } + + return $this->get_minify_url( $filename ); + } + + /** + * Minifies the content + * + * @since 2.11 + * @author Remy Perona + * + * @param string|array $files Files to minify. + * @return string|bool Minified content, false if empty + */ + protected function minify( $files ) { + foreach ( $files as $file ) { + $file_content = $this->get_file_content( $file ); + $file_content = $this->rewrite_paths( $file, $file_content ); + + $this->minifier->add( $file_content ); + } + + $minified_content = $this->minifier->minify(); + + if ( empty( $minified_content ) ) { + return false; + } + + return $minified_content; + } +} From 616fea2fe7c6dd587062b41e3793ebfffa9b1842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Perona?= Date: Wed, 13 Jun 2018 12:25:18 -0400 Subject: [PATCH 5/9] add path rewriter trait --- .../optimization/CSS/trait-path-rewriter.php | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 inc/classes/optimization/CSS/trait-path-rewriter.php diff --git a/inc/classes/optimization/CSS/trait-path-rewriter.php b/inc/classes/optimization/CSS/trait-path-rewriter.php new file mode 100644 index 0000000000..c7e44d9166 --- /dev/null +++ b/inc/classes/optimization/CSS/trait-path-rewriter.php @@ -0,0 +1,33 @@ + Date: Wed, 13 Jun 2018 12:25:32 -0400 Subject: [PATCH 6/9] add CSS minify/combine subscriber --- .../class-minify-css-subscriber.php | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 inc/classes/subscriber/Optimization/class-minify-css-subscriber.php diff --git a/inc/classes/subscriber/Optimization/class-minify-css-subscriber.php b/inc/classes/subscriber/Optimization/class-minify-css-subscriber.php new file mode 100644 index 0000000000..b8b9496c7a --- /dev/null +++ b/inc/classes/subscriber/Optimization/class-minify-css-subscriber.php @@ -0,0 +1,89 @@ +is_allowed() ) { + return $html; + } + + list( $html, $conditionals ) = $this->extract_ie_conditionals( $html ); + + $crawler = $this->crawler; + $crawler = $crawler::create( $html ); + + if ( $this->options->get( 'minify_css' ) && $this->options->get( 'minify_concatenate_css' ) ) { + $this->set_optimization_type( new CSS\Combine( $crawler, $this->options, new Minify\CSS() ) ); + } elseif ( $this->options->get( 'minify_css' ) && ! $this->options->get( 'minify_concatenate_css' ) ) { + $this->set_optimization_type( new CSS\Minify( $crawler, $this->options ) ); + } + + return $this->inject_ie_conditionals( $this->optimize(), $conditionals ); + } + + /** + * @inheritDoc + */ + protected function is_allowed() { + if ( defined( 'DONOTROCKETOPTIMIZE' ) && DONOTROCKETOPTIMIZE ) { + return false; + } + + if ( defined( 'DONOTMINIFYCSS' ) && DONOTMINIFYCSS ) { + return false; + } + + if ( ! $this->options->get( 'minify_css' ) ) { + return false; + } + + if ( is_rocket_post_excluded_option( 'minify_css' ) ) { + return false; + } + + return true; + } + + /** + * Returns an array of CDN zones for CSS files. + * + * @since 3.1 + * @author Remy Perona + * + * @return array + */ + public function get_zones() { + return [ 'all', 'css_and_js', 'css' ]; + } +} From e5e654e54c8a10c5d93ae0171228e50a676d1eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Perona?= Date: Wed, 13 Jun 2018 17:43:10 -0400 Subject: [PATCH 7/9] use a more generic method name --- .../class-abstract-optimization.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/inc/classes/optimization/class-abstract-optimization.php b/inc/classes/optimization/class-abstract-optimization.php index 72f84550c0..d9171c0eab 100644 --- a/inc/classes/optimization/class-abstract-optimization.php +++ b/inc/classes/optimization/class-abstract-optimization.php @@ -94,25 +94,25 @@ protected function is_external_file( $url ) { } /** - * Writes the minified content to a file + * Writes the content to a file * - * @since 2.11 + * @since 3.1 * @author Remy Perona * - * @param string $content Minified content. - * @param string $minified_file Path to the minified file to write in. - * @return bool True if successful, false otherwise + * @param string $content Content to write. + * @param string $file Path to the file to write in. + * @return bool */ - protected function write_minify_file( $content, $minified_file ) { - if ( rocket_direct_filesystem()->exists( $minified_file ) ) { + protected function write_file( $content, $file ) { + if ( rocket_direct_filesystem()->is_readable( $file ) ) { return true; } - if ( ! rocket_mkdir_p( dirname( $minified_file ) ) ) { + if ( ! rocket_mkdir_p( dirname( $file ) ) ) { return false; } - return rocket_put_content( $minified_file, $content ); + return rocket_put_content( $file, $content ); } /** From 83600ea37219fcb7a0014418c33e027e25d18f6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Perona?= Date: Wed, 13 Jun 2018 17:43:22 -0400 Subject: [PATCH 8/9] update for new method name --- inc/classes/optimization/CSS/class-minify.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/classes/optimization/CSS/class-minify.php b/inc/classes/optimization/CSS/class-minify.php index d3064096d7..dc872554d4 100644 --- a/inc/classes/optimization/CSS/class-minify.php +++ b/inc/classes/optimization/CSS/class-minify.php @@ -84,7 +84,7 @@ private function replace_url( $url ) { return false; } - $save_minify_file = $this->write_minify_file( $minified_content, $minified_file ); + $save_minify_file = $this->write_file( $minified_content, $minified_file ); if ( ! $save_minify_file ) { return false; From bad5eca987c694aeca54ee4a8adbabaaa53ed5a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Perona?= Date: Wed, 13 Jun 2018 17:43:36 -0400 Subject: [PATCH 9/9] use new method name --- inc/classes/optimization/CSS/class-combine.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inc/classes/optimization/CSS/class-combine.php b/inc/classes/optimization/CSS/class-combine.php index d722b5bd78..6b721e2102 100644 --- a/inc/classes/optimization/CSS/class-combine.php +++ b/inc/classes/optimization/CSS/class-combine.php @@ -144,7 +144,7 @@ protected function combine( $urls ) { return false; } - $minify_filepath = $this->write_minify_file( $minified_content, $minified_file ); + $minify_filepath = $this->write_file( $minified_content, $minified_file ); if ( ! $minify_filepath ) { return false;