oont-contents/plugins/wp-rocket/inc/Engine/Optimization/Minify/CSS/Minify.php
2025-02-08 15:10:23 +01:00

341 lines
7.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace WP_Rocket\Engine\Optimization\Minify\CSS;
use WP_Rocket\Dependencies\Minify as Minifier;
use WP_Rocket\Engine\Optimization\CSSTrait;
use WP_Rocket\Engine\Optimization\Minify\ProcessorInterface;
use WP_Rocket\Logger\Logger;
/**
* Minify CSS files
*
* @since 3.1
*/
class Minify extends AbstractCSSOptimization implements ProcessorInterface {
use CSSTrait;
/**
* Minifies CSS files
*
* @since 3.1
*
* @param string $html HTML content.
*
* @return string
*/
public function optimize( $html ) {
Logger::info( 'CSS MINIFICATION PROCESS STARTED.', [ 'css minification process' ] );
$styles = $this->get_styles( $html );
if ( empty( $styles ) ) {
return $html;
}
foreach ( $styles as $style ) {
if ( $this->is_minify_excluded_file( $style ) ) {
Logger::debug(
'Style is excluded.',
[
'css minification process',
'tag' => $style[0],
]
);
continue;
}
$integrity_validated = $this->local_cache->validate_integrity( $style );
if ( false === $integrity_validated ) {
Logger::debug(
'Style integrity attribute not valid.',
[
'css minification process',
'tag' => $style[0],
]
);
continue;
}
$style['final'] = $integrity_validated;
$minify_url = $this->replace_url( strtok( $style['url'], '?' ) );
if ( ! $minify_url ) {
Logger::error(
'Style minification failed.',
[
'css minification process',
'tag' => $style[0],
]
);
continue;
}
$html = $this->replace_style( $style, $minify_url, $html );
}
return $html;
}
/**
* Get all style tags from HTML.
*
* @param string $html HTML content.
*
* @return array Array with style tags, empty array if no style tags found.
*/
protected function get_styles( $html ) {
$html_nocomments = $this->hide_comments( $html );
$styles = $this->find( '<link\s+([^>]+[\s"\'])?href\s*=\s*[\'"]\s*?(?<url>[^\'"]+\.css(?:\?[^\'"]*)?)\s*?[\'"]([^>]+)?\/?>', $html_nocomments );
if ( ! $styles ) {
Logger::debug( 'No `<link>` tags found.', [ 'css minification process' ] );
return [];
}
Logger::debug(
'Found ' . count( $styles ) . ' `<link>` tags.',
[
'css minification process',
'tags' => $styles,
]
);
return $styles;
}
/**
* Creates the minify URL if the minification is successful
*
* @since 2.11
*
* @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;
}
// This filter is documented in /inc/classes/optimization/class-abstract-optimization.php.
$url = apply_filters( 'rocket_asset_url', $url, $this->get_zones() );
$parsed_url = wp_parse_url( $url );
if ( empty( $parsed_url['path'] ) ) {
return false;
}
if ( ! empty( $parsed_url['host'] ) ) {
$url = rocket_add_url_protocol( $url );
}
$filename = ltrim( rocket_realpath( $parsed_url['path'] ), '/' );
$minified_file = rawurldecode( $this->minify_base_path . $filename );
if ( rocket_direct_filesystem()->exists( $minified_file ) ) {
Logger::debug(
'Minified CSS file already exists.',
[
'css minification process',
'path' => $minified_file,
]
);
return $this->get_full_minified_url( $minified_file, $this->get_minify_url( $filename, $url ) );
}
$external_url = $this->is_external_file( $url );
$file_path = $external_url ? $this->local_cache->get_filepath( $url ) : $this->get_file_path( $url );
if ( empty( $file_path ) ) {
Logger::error(
'Couldnt get the file path from the URL.',
[
'css minification process',
'url' => $url,
]
);
return false;
}
$file_content = $external_url ? $this->local_cache->get_content( $url ) : $this->get_file_content( $file_path );
if ( ! $file_content ) {
Logger::error(
'No file content.',
[
'css minification process',
'path' => $file_path,
]
);
return false;
}
$minified_content = $external_url ? $this->minify( $url, $minified_file, $file_content ) : $this->minify( $file_path, $minified_file, $file_content );
if ( empty( $minified_content ) ) {
return false;
}
$minified_content = $this->font_display_swap( $url, $minified_file, $minified_content );
if ( empty( $minified_content ) ) {
return false;
}
$save_minify_file = $this->save_minify_file( $minified_file, $minified_content );
if ( ! $save_minify_file ) {
return false;
}
return $this->get_full_minified_url( $minified_file, $this->get_minify_url( $filename, $url ) );
}
/**
* Replace old style tag with the minified tag.
*
* @param array $style Style matched data.
* @param string $minify_url Minified URL.
* @param string $html HTML content.
*
* @return string
*/
protected function replace_style( $style, $minify_url, $html ) {
$replace_style = str_replace( $style['url'], $minify_url, $style['final'] );
$replace_style = str_replace( '<link', '<link data-minify="1"', $replace_style );
$html = str_replace( $style[0], $replace_style, $html );
Logger::info(
'Style minification succeeded.',
[
'css minification process',
'url' => $minify_url,
]
);
return $html;
}
/**
* Save minified CSS file.
*
* @since 3.7
*
* @param string $minified_file Minified file path.
* @param string $minified_content Minified HTML content.
*
* @return bool
*/
protected function save_minify_file( $minified_file, $minified_content ) {
$save_minify_file = $this->write_file( $minified_content, $minified_file );
if ( ! $save_minify_file ) {
Logger::error(
'Minified CSS file could not be created.',
[
'css minification process',
'path' => $minified_file,
]
);
return false;
}
Logger::debug(
'Minified CSS file successfully created.',
[
'css minification process',
'path' => $minified_file,
]
);
return true;
}
/**
* Applies font display swap if the file contains @font-face.
*
* @since 3.7
*
* @param string $url File Url.
* @param string $minified_file Minified file path.
* @param string $content CSS file content.
*
* @return string
*/
protected function font_display_swap( $url, $minified_file, $content ) {
if (
preg_match( '/(?:-|\.)min.css/iU', $url )
&&
false === stripos( $content, '@font-face' )
) {
Logger::error(
'Do not apply font display swap on min.css files without font-face.',
[
'css minification process',
'path' => $minified_file,
]
);
if ( ! $this->is_external_file( $url ) ) {
return '';
}
return $content;
}
return $this->apply_font_display_swap( $content );
}
/**
* Minifies the content
*
* @since 2.11
*
* @param string $file_path Source filepath.
* @param string $minified_file Target filepath.
* @param string $file_content Content to minify.
*
* @return string
*/
protected function minify( $file_path, $minified_file, $file_content ) {
$file_content = $this->rewrite_paths( $file_path, $minified_file, $file_content );
$minifier = $this->get_minifier( $file_content );
$minified_content = $minifier->minify();
if ( empty( $minified_content ) ) {
Logger::error(
'No minified content.',
[
'css minification process',
'path' => $minified_file,
]
);
return '';
}
return $minified_content;
}
/**
* Returns a new minifier instance
*
* @since 3.1
*
* @param string $file_content Content to minify.
*
* @return Minifier\CSS
*/
protected function get_minifier( $file_content ) {
return new Minifier\CSS( $file_content );
}
}